Adventures in Meta programming in Python: im_class, __import__, __getattribute__
With my recent work in Python win32 programming I’ve had a real need for AAA style mocking framework. Unable to find anything that I’ve been completely happy with I started my own simple mocking framework and got to learn some Python style meta programming in the process. I’ve found out a lot about the depth of the Python 2.x object model over the last 2 weeks and here are some of the nicer things I’ve found (updated as per Chris Taveres below):
function_class = MyClass.foo.im_class
This is useful if you pass in an instance method somewhere and you want to be able to access its attached class. Decorators on instance methods normally use this, but I needed it to get a nicer syntax on my asserts, I wanted this:
So now when I want to track what class is passed in I just have to start with the following:
classdef = method.im_class #I get the method and the class in one parameter!
module = __import__(“foomodule”)
this creates a module import by string. This becomes super powerful say if you wanted to replace functionality on a module, but didn’t know its name before hand. In my case I wanted to be able to patch in a replacement method for mocking and then put it back when I was done, and I didn’t want to have to keep passing in the module name on resets. The only downside to it so far is it behaves a little quirky with packages and you have walk your module hierarchy to get it to do what you want
example of patching originals back into the module:
modulename = mod
methodname = mod
module = __import__(modulename) #this is the magic. if module name has no package aka : “barmod” this will work. if its “foopackage.barmod” it’ll import the “foopackage”
for i in modulename.split(“.”)[1:]: #works down the chain
module = getattr(module, i) #resets the module variable with the lower hierarchy module
setattr(module, methodname, original) #actually pass the original function object back onto the module.
MyClass.__getattribute__ = interception
__getattribute__ is called when any attribute is accessed on a class including __getattribute__ itself which is a bit silly. This was the primary engine of my mocking framework as it allowed me to record all calls to the methods on a mocked class. Just remember when you use this to have it not call itself or you’ll be in endless recursion!
def interceptor(self, name):
if name == “__getattribute__”: #guard condition against calling itself
return object.__getattribute__(self, name)
#whatever interception logic you need here
MyClass.__getattribute__ = interceptor
m = MyClass()
m.stuff() #interception logic will be called here </div> </div>
I’m just starting to dip into the Python object model now and I’ll try and share what I find over the next few weeks.