[Python-Dev] frame.f_locals is writable

Brett C. bac at OCF.Berkeley.EDU
Thu Jan 13 22:50:24 CET 2005


Shane Holloway (IEEE) wrote:
> For a little background, I'm working on making an edit and continue 
> support in python a little more robust.  So, in replacing references to 
> unmodifiable types like tuples and bound-methods (instance or class), I 
> iterate over gc.get_referrers.
> 
> So, I'm working on frame types, and wrote this code::
> 
>     def replaceFrame(self, ref, oldValue, newValue):
>         for name, value in ref.f_locals.items():
>             if value is oldValue:
>                 ref.f_locals[name] = newValue
>                 assert ref.f_locals[name] is newValue
> 
> 
> But unfortunately, the assert fires.  f_locals is writable, but not 
> modifiable.  I did a bit of searching on Google Groups, and found 
> references to a desire for smalltalk like "swap" functionality using a 
> similar approach, but no further ideas or solutions.
> 
> While I am full well expecting the smack of "don't do that", this 
> functionality would be very useful for debugging long-running 
> applications.  Is this possible to implement in CPython and ports?  Is 
> there an optimization reason to not do this?
> 

So it would be doable, but it is not brain-dead simple if you want to keep the 
interface of a dict.  Locals, in the frame, are an array of PyObjects (see 
PyFrameObject->f_localsplus).  When you request f_locals that returns a dict 
that was created by a function that takes the array, traverses it, and creates 
a dict with the proper names (using PyFrameObject->f_code->co_varnames for the 
array offset -> name mapping).  The resulting dict gets stored in 
PyFrameObject->f_locals.  So it is writable as you discovered since it is just 
a dict, but it is not used in Python/ceval.c except for IMPORT_STAR; changes 
are just never even considered.  The details for all of this can be found in 
Objects/frameobject.c:PyFrame_FastToLocals() .

The interesting thing is that there is a corresponding PyFrame_LocalsToFast() 
function that seems to do what you want; it takes the dict in 
PyFrameObject->f_locals and propogates the changes into 
PyFrameObject->f_localsplus (or at least seems to; don't have time to stare at 
the code long enough to make sure it does that exactly).  So the functionality 
is there (and is in the API even).  It just isn't called explicitly except in 
two points in Python/ceval.c where you can't get at it.  =)

As to making changes to f_locals actually matter would require either coming up 
with a proxy object that is stored in f_locals instead of a dict and 
dynamically grab everything from f_localsplus as needed.  That would suck for 
performance and be a pain to keep the dict API.  So you can count that out.

Other option would be to add a function that either directly modified single 
values in f_localsplus, a function that takes a dict and propogates the values, 
or a function that just calls PyFrame_LocalsToFast() .

Personally I am against this, but that is because you would single-handedly 
ruin my master's thesis and invalidate any possible type inferencing one can do 
in Python without some semantic change.  But then again my thesis shows that 
amount of type inferencing is not worth the code complexity so it isn't totally 
devastating.  =)

And you are right, "don't do that".  =)

Back to the putrid, boggy marsh of JavaLand for me...

-Brett


More information about the Python-Dev mailing list