From python, is it possible to track assignments at the module level to (other) user code?

I am working on a tool that can benefit from being able to track all references to a given object from within python.

In particular, I would like to create a double test system that could replace all attributes at the module level of this type. For example, suppose the following code is in module c:

from a import b 

If a is a module, b is a reference to an object named ab, but this is a separate link. If my test binary system later replaces ab, cb will still refer to the original object.

I would like my tool to track all ab assignments for aliases, but module-level anti-aliasing would go a long way towards my goal.

Metaphorically, I would like to redefine Module.__setattribute__ :

 def __setattribute__(self, name, value): if isinstance(value, interesting_types): # remember this use of the interesting object and call super for normal processing. 

Suppose I can load my code before modules that can be tracked can be loaded.

+7
python metaprogramming
source share
1 answer

This might work for you. First, some code:

a.py

 b = 42 # other module definitions 

fakery.py

 class Fakery(object): def __init__(self, mod): self.module = __import__(mod) import sys sys.modules[self.module.__name__] = self def __getattr__(self, name): print "__getattr__ called with '%s'" % name result = getattr(self.module, name) if name == 'b': result += 1 return result 

Example

 >>> import fakery >>> fakery.Fakery('a') <fakery.Fakery object at 0x109007110> >>> from a import b __getattr__ called with '__path__' __getattr__ called with 'b' >>> print b 43 >>> import a >>> print a <fakery.Fakery object at 0x109007110> 

All you have to do is change the Fakery class to do what you want to do. In this case, I just add 1 to a b .

I hope this is clear how this works, but here is a quick explanation.

When a module is imported, its entry is populated in sys.modules . When you instantiate a Fakery object, it imports the module by name (using __import__ ), and then replaces that module entry in sys.modules with itself.

Python imports modules only once, storing the imported module in sys.modules . Each import after the first returns an entry in sys.modules. The Fakery object inserts itself in sys.modules['a'] , replacing the real module. All subsequent import a or from a import <whatever> Fakery now routed to the Fakery instance. Since this is just a class, you can do all kinds of crazy things using magic methods or metaprogramming.

__getattr__ convenient because it is called when the requested attribute does not exist.

+2
source share

All Articles