Monkey patching a class in another module in Python
Use mock
library.
import thirdpartymodule_a
import thirdpartymodule_b
import mock
def new_init(self):
self.a = 43
with mock.patch.object(thirdpartymodule_a.SomeClass, '__init__', new_init):
thirdpartymodule_b.dosomething() # -> print 43
thirdpartymodule_b.dosomething() # -> print 42
or
import thirdpartymodule_b
import mock
def new_init(self):
self.a = 43
with 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 wrapt
import thirdpartymodule_a
import 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 = 43
thirdpartymodule_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)
The following should work:
import thirdpartymodule_a
import thirdpartymodule_b
def new_init(self):
self.a = 43
thirdpartymodule_a.SomeClass.__init__ = new_init
thirdpartymodule_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