Monkey Patching a Class in Another Module in Python

Monkey patching a class in another module in Python

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

Monkey patch an object from another module in Python

You can use mock.patch to temporarily patch module1.foo_ with dummy_foo_ by using it as a context manager

from mock import patch
def test_blah(dummy_foo_):
with patch('module1.foo_', dummy_foo_) as mock:
assert blah() == 'blah'

python monkey patch a new class and import it

This is because of the import system.

Reading through the doc, you can find this paragraph:

[...] the statement from spam.ham import eggs, sausage as saus results in

_temp = __import__('spam.ham', globals(), locals(), ['eggs', 'sausage'], 0)
eggs = _temp.eggs
saus = _temp.sausage

The problem is: what does __import__() does?

The import statement combines two operations; it searches for the named module, then it binds the results of that search to a name in the local scope. [...]

A direct call to __import__() performs only the module search and, if found, the module creation operation.

So, when you re-import the module, your customization will be lost.

To ensure it stays on, you can import numpy as np and then, when using np - after you assing this new class - you can always access wrong_add.

>>> import numpy as np
>>> np.random.wrong_functions = wrong_functions
>>> np.random.wrong_function.wrong_add(1, 1)
3

EDIT: If you need/want to just call wrong_add instead of full package path to function, you can always assign it to a variable.

>>> wrong_add = np.random.wrong_function.wrong_add
>>> wrong_add(2, 2)
5

Python monkey patch a module that gets executed by another module

I don't see any difference between the question you referenced and your situation. In both cases a modules import a name from another module and you want to patch the object referenced by that name.

In that question the module is written by the author, in your question it's a third party module.

I think your problem lies in the fact the the third party already imported the object name, so if you try to replace the object in the module where it resides, the third party module will still use the old one, because it still references the old object.

module_1.py

class AForm(Form):
...

module_2.py

from module_1 import AForm

If you import module_2 and then monkey-patch module_1 replace AForm with another object

class AFormPatched(Form):
...

import module_1
module_1.AForm = AFormPatched

Your changes will not affect module_2, because module_2.AForm still points to the original object.

To solve this:

Option 1. module_2 should look like:

import module_1
module_1.AForm # using AForm in this form

Option 2. Patch also module_2.AForm:

class AFormPatched(Form):
...

import module_1
module_1.AForm = AFormPatched
import module_2
module_2.AForm = AFormPatched

Or don't patch module_1 you want only module_2 to use the patched version.

Option 3. Patch attributes of the object. If you don't want to or cannot monkey patch all the places where the object name is used, sometimes it works if you don't replace the object, but patch only some of its attributes. It depends on the object and the behavior you are trying to patch:

import third_party_module
third_party_module.AForm.__init__ = ...

Is there a way to monkey patch a class inside a module before the module body is executed in python?

The problem here is that when you imported the main.py file, it executed the code a = A(4) using the real implementation of the class A. Then the rest of your test.py was executed and you replaced the A reference, but it was too late.
You can check that by adding in your test :

print(__name__)                     # __main__
print(main.__name__) # so70731368_main
print(A.__module__) # __main__
print(main.A.__module__) # __main__
print(main.a.__class__.__module__) # so70731368_main

Here, __main__ is a bit confusing but that's how Python call the first file you run (in your case test.py). The a instance is declared in the so70731368_main module, and it used the A class from the same module, you just changed A after the fact with the definition from the test file (__main__).

The fact that you need to patch two definitions (A and a) defined in the same file is very tricky. unittest.mock.patch is not powerful enough to patch inside an import (it patches after the import).

You can not, in a clean and simple way, prevent a to be instantiated as a main.A (real) class and get printed. What you can do is patch it after, for later uses, that is what you showed.

To answer directly your question : "patching" means replacing one reference by another, so the reference has to already be defined. In your example, it would require to patch between the class definition and the class instantiation (for the print to not use the real a), which is not supported.

There is no simple solution to this problem. If you have control over the code of the main.py file, then try to change it so that it does not instantiate a at import time.

monkey patch add new class and functions to existing module

I think you're saying that track.py is inaccessible (perhaps from a library you don't want to touch).

There's a couple ways you could do it. One is to just import the module then at runtime alter the object to include the class.

import track
track.B = B # or setattr(track, 'B', B)
track.some_func = some_func # or settattr(track, 'some_func', some_func)

You could also merge in the __init__.py e.g.

import track
import track_monkey_patch
track.__dict__.update(track_monkey_patch.__dict__)

Also, along the lines of making a decorator or some kind of wrapping to just do it for you, if you are okay adding an external dependency then it looks like someone has done this (never used it myself, just a quick google).
https://github.com/christophercrouzet/gorilla

Test code that anyone could use

import collections  # could have been anything just came to my head quickly
def foo():
print("foooooo")
collections.foo = foo
collections.foo()
>>> foooooo

Monkey-patch Python class

import module
class ReplaceClass(object):
....
module.MyClass = ReplaceClass

Monkey-patching a method of an instance with hierarchical inheritance

super() is retrieving the method from the parent class object, of which there is only one. You can patch the method on that class, but the change will be felt by all instances of the parent class and its descendants. You can't patch on an individual instance.



Related Topics



Leave a reply



Submit