pythoncpython

Modify existing variable in `locals()` or `frame.f_locals`


I have found some vaguely related questions to this question, but not any clean and specific solution for CPython. And I assume that a "valid" solution is interpreter specific.

First the things I think I understand:

So I understand that, given those limitations, it will never be safe to add extra variables to the locals, because it breaks the interpreter structure.

However, it should be possible to change a variable already existing, isn't it?

Things that I considered:

The variables should be somewhere, preferably writeable... but I am not capable of finding it. Even if it is an array (for interpreter efficient access), or I need some extra C-specific wiring, I am ready to commit to it.

How can I achieve that modification of variables from a tracer function or from a decorated wrapped function or something like that?

Hackish exec is doing things like this or this.


Solution

  • It exists an undocumented C-API call for doing things like that:

    PyFrame_LocalsToFast

    There is some more discussion in this PyDev blog post. The basic idea seems to be:

    import ctypes
    
    ...
    
    frame.f_locals.update({
        'a': 'newvalue',
        'b': other_local_value,
    })
    ctypes.pythonapi.PyFrame_LocalsToFast(
        ctypes.py_object(frame), ctypes.c_int(0))
    

    I have yet to test if this works as expected.

    Note that there might be some way to access the Fast directly, to avoid an indirection if the requirements is only modification of existing variable. But, as this seems to be mostly non-documented API, source code is the documentation resource.