Monkey patching a class in another module in Python
The following should work:
import thirdpartymodule_aimport thirdpartymodule_bdef new_init(self): self.a = 43thirdpartymodule_a.SomeClass.__init__ = new_initthirdpartymodule_b.dosomething()
If you want the new init to call the old init replace the new_init()
definition with the following:
old_init = thirdpartymodule_a.SomeClass.__init__def new_init(self, *k, **kw): old_init(self, *k, **kw) self.a = 43
Use mock
library.
import thirdpartymodule_aimport thirdpartymodule_bimport mockdef new_init(self): self.a = 43with mock.patch.object(thirdpartymodule_a.SomeClass, '__init__', new_init): thirdpartymodule_b.dosomething() # -> print 43thirdpartymodule_b.dosomething() # -> print 42
or
import thirdpartymodule_bimport mockdef new_init(self): self.a = 43with mock.patch('thirdpartymodule_a.SomeClass.__init__', new_init): thirdpartymodule_b.dosomething()thirdpartymodule_b.dosomething()
One another possible approach, very similar to Andrew Clark's one, is to use wrapt library.Among other useful things, this library provides wrap_function_wrapper
and patch_function_wrapper
helpers. They can be used like this:
import wraptimport thirdpartymodule_aimport thirdpartymodule_b@wrapt.patch_function_wrapper(thirdpartymodule_a.SomeClass, '__init__')def new_init(wrapped, instance, args, kwargs): # here, wrapped is the original __init__, # instance is `self` instance (it is not true for classmethods though), # args and kwargs are tuple and dict respectively. # first call original init wrapped(*args, **kwargs) # note it is already bound to the instance # and now do our changes instance.a = 43thirdpartymodule_b.do_something()
Or sometimes you may want to use wrap_function_wrapper
which is not a decorator but othrewise works the same way:
def new_init(wrapped, instance, args, kwargs): pass # ...wrapt.wrap_function_wrapper(thirdpartymodule_a.SomeClass, '__init__', new_init)