Functions have a __globals__
field that you can reassign to change how lookups of globals is done, it gets initialized based on the f_globals
of the frame which either comes from the defining function or at the top level comes from the __dict__
for the module. You can write your own class that has a __getitem__
that for example records what global lookups occur. However, if you do this you run into some trouble:
- CPython internally uses
PyDict_
functions on the globals object, meaning that it must actually be a dictionary or a subclass of a dictionary, otherwise you crash. - If you make it a subclass of a dictionary, the most obvious approach is to take the original globals dictionary on construction then just override
__getitem__
to do the recording and then do the lookup in the original. But this creates a subtle problem – your object is really 2 dictionaries, the inherited one and the original globals being stored as a member. You can try to override every dictionary relevant method, but thePyDict_*
functions bypass user overrides and always call the original dictionary version so they get the inherited dict not the member one. I don’t need to monitor the internal accesses that the interpreter makes, but I do need the interpreter to see an consistent view so it doesn’t get confused/crash. - You could instead subclass dict, but on construction instead of storing the original globals dict you could call
self.update(original)
to copy all the contents, then override__getitem__
to record. This way there is really only one dictionary for the function. But then you have the problem thatmodule.__dict__
is no longer the same object as thefunc.__globals__
, so any new globals at module level will be invisible to the function and vice versa. - This leads to the idea of patching every
module.__dict__
to be a tracking dictionary type and letting that automatically propagate down to to the__globals__
of the functions the module defines. But you can’t reassign__dict__
. I could try reassigning it in C, but I don’t know if this is expected to work in the future? - I could try in C directly overwriting the
ob_type
pointer for the module dict to transform it in place into my custom dict subclass. But is that future proof either? - There is also the
PyDict_AddWatcher
API, but it only lets you watch modifications, not pure lookups.
Would appreciate any tips on the best way to do it.