marius marius - 7 months ago 37
Python Question

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"

Answer

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).