Reputation: 393
I want to be able to intercept variable assignments within a function and execute custom code. I have tried creating a custom dict as follows:
class userdict(dict):
def __setitem__(self, key, value):
print 'run my code'
dict.__setitem__(self, key, value)
If I exec code using this as the global dict, then my custom code will run on each variable assignment. e.g.:
UserDict = userdict()
exec 'x = 1' in UserDict
#outputs 'run my code'
But if my code is inside a function, it doesn't work:
code = """
def foo():
global x
x = 1
"""
exec code in UserDict
UserDict['foo']()
In this case, 'x' is assigned, but my custom code does not run. I assume that within a function, the global dict is being modified somehow without calling setitem. Is this correct? Is there a way to intercept variable assignments within a function and execute custom code?
I want to do this in order to synchronize certain objects available inside a function with other objects in my program. In order words, when the an assignment to certain variables occurs inside the function, that change should propagate to other variables in my program.
Upvotes: 5
Views: 923
Reputation: 414079
The issue might be that builtin dict
methods don't call overridden in subclasses methods in CPython. Pypy, Jython call custom __setitem__()
so they see immediately when x
is set.
dis
module shows that STORE_GLOBAL
is used to set x
:
>>> def f():
... global x
... x = 1
...
...
>>> import dis
>>> dis.dis(f)
4 0 LOAD_CONST 1 (1)
3 STORE_GLOBAL 0 (x)
6 LOAD_CONST 0 (None)
9 RETURN_VALUE
It is implemented in ceval.c
as:
TARGET(STORE_GLOBAL)
w = GETITEM(names, oparg);
v = POP();
err = PyDict_SetItem(f->f_globals, w, v);
Py_DECREF(v);
if (err == 0) DISPATCH();
break;
if PyDict_SetItem()
is replaced with PyObject_SetItem()
then CPython also works i.e., custom __setitem__()
is called.
Upvotes: 3