AKA hooking the args & return value of every function in a Python module
23 Jun 2017
Suppose you want to create a Python wrapper for some module, but hook into EVERY function call to do something with the args and results.
In my case, the module was a C Extension (using cffi), and I needed to convert most arguments to <cdata> things for cffi, and then turn most of the results back into Python objects.
Indeed, you could do a billion lines of this.
But nah, time to stir some metaprogramming into the soup.
I’ll assume you have functions like this in the wrapper module.
You’ll need a decorator to actually apply the functions (though we won’t use it like a decorator). Pretty simple, just passes args and kwargs through one function, and the return value through the other. We use functools.wraps to preserve the original function’s name, docstring, etc.
Now, we set the the module that the decorated members should be imported into. I wanted to dump all the resulting objects into the wrapper module (as if we did from SOMETHING.lib import *), so I just used __name__, but if that’s not the case for you, you’ll have to change this to another module.
We iterate through all the objects in the source module, and apply the decorator if it’s a C function (change BuiltinFunctionType to FunctionType to process Python functions). Then, using setattr, we assign the hooked object to the target module with the original name.