For a project, I need to substitute a function from a module. I would like to achieve this by the following minimal example
In mod.py
the original function is defined,
def f():
print("mod.py")
which should be overridden by f2
in mod2.py
,
import sys
def f2():
print("mod2.py")
sys.modules["mod"].f = f2
In main.py
I first load the original function, which I immediately patch
from mod import f
import mod2
f() # I'm expecting "mod2.py", but get "mod.py" instead
It seems, that the function definition of f
in the __main__
's globals() is a not a reference to sys.modules["mod"].f
, which I was hoping.
If in main.py
I import the module in which f
is defined, instead f
itself, I can patch it
import mod
import mod2
print(mod.f()) # patched from mod2
But I'd like to find a solution which works both imports in main.py
— the import of the whole module (import mod
) and the function itself (from mod import f
).
I also tried to somehow modify the locals()
from the parent frame via inspect.currentframe().f_back.f_locals
but this seems doesn't work as well and would feel hacky, if I'd find a solution like this, which would do what I want.
Is this in principle possible with the way Python handles import
s?
Here's a good way to monkey-patch the function in mod.py
so that it will affect any further imports of mod
after an import mod2
. This works because modules are cached in sys.modules
— but you don't need to explicitly reference that if you do things as shown.
mod.py
:
def f():
print("mod.py")
mod2.py
:
import mod
def f2():
print("mod2.py")
# Monkey-patch `mod` module.
mod.f = f2
main.py
:
import mod
import mod2 # Importing this applies the patch to the `mod` module.
mod.f() # Prints -> mod2.py