Search code examples
pythonpython-internalsinspectstack-framelocals

How can I force update the Python locals() dictionary of a different stack frame?


In Python 2 (not sure about 3), the locals dictionary only gets updated when you actually call locals(). So e.g.

l=locals()
x=2
l['x']

fails because l doesn't have the key "x" in it, but

l=locals()
x=2
locals()
l['x']

returns 2.

I'm looking for a way to force an update of the locals dictionary, but the trick is that I'm in a different stack frame. So e.g. I'm looking to do

l=locals()
x=2
force_update()
l['x']

and I need to write the force_update() function. I know that from said function I can get the parent frame via inspect.currentframe().f_back, and even the parent (non-updated) locals via inspect.currentframe().f_back.f_locals, but how can I force an update?

If this seems convoluted, my main goal is to write a function which is shorthand for "{some} string".format(**dict(globals(),**locals())) so I don't have to type that out each time, and can instead do fmt("{some} string"). Doing so I run into the issue above.

Edit: With Martjin answer below, below is essentially the solution I was looking for. One could play around with exactly how they get the stack frame of the callee, here I do it via partial.

from functools import partial
from inspect import currentframe

fmt = partial(lambda s,f: s.format(**dict(globals(),**f.f_locals)),f=currentframe())
x=2
print fmt("{x}") #prints "2"

Solution

  • Simply accessing f_locals on a frame object triggers the copy, so using inspect.currentframe().f_back.f_locals is enough.

    See the frame_getlocals() function in the frameobject.c implementation:

    static PyObject *
    frame_getlocals(PyFrameObject *f, void *closure)
    {
        PyFrame_FastToLocals(f);
        Py_INCREF(f->f_locals);
        return f->f_locals;
    }
    

    PyFrame_FastToLocals is the function used to copy the data from the interal array tracking locals values to a dictionary. frame_getlocals is used to implement the frame.f_locals descriptor (a property); see the frame_getsetlist definition.

    The PyFrame_FastToLocalsWithError function used above is exactly what locals() uses to produce the same dictionary (by wrapping the PyEval_GetLocals function).