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 import
ed 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
How to Get the Executable's Current Directory in Py2Exe
Better Way to Shuffle Two Numpy Arrays in Unison
Check If Value Already Exists Within List of Dictionaries
How to Copy Inmemoryuploadedfile Object to Disk
How to Get Exception Message in Python Properly
How to Force a Python Wheel to Be Platform Specific When Building It
Django 1.7 - "No Migrations to Apply" When Run Migrate After Makemigrations
Python Flask Intentional Empty Response
Pandas Groupby.Size VS Series.Value_Counts VS Collections.Counter with Multiple Series
Import Module Works in Terminal But Not in Idle
How to Install Xgboost Package in Python (Windows Platform)
Can a Decorator of an Instance Method Access the Class
How to Check If One Dictionary Is a Subset of Another Larger Dictionary