Python: monkey patch a function used by classes in the same module

77 views Asked by At

I want to modify a function (not a method) in a module, such that the classes within the same module that use that function behave differently.

For the sake of simplicity assume I have a file called _module.py and a file __init__.py that sit in a directory called my_library.

# file _module.py
def _do_something():
    print('hello')

class foo():
    def bar(self):
        _do_something()
# file __init__.py
from ._module import foo

I have tried monkey patching the function _do_something like this

import my_library
my_library._do_something = lambda: print('goodbye')
a = my_library.foo()
a.bar()

but it still outputs hello. I also have tried the following

from my_library import foo, _do_something
_do_something = lambda: print('goodbye')
a = foo()
a.bar()

but it throws a cannot import name '_do_something' from 'my_library' error.


How can I modify _do_something such that the change is reflected when I call foo.bar?


PS: Actually I want to change the function _estimate_gaussian_parameters used by sklearn.mixture.GaussianMixture and both are located in the same file. But I don't think this is relevant to the question.

2

There are 2 answers

1
Tranbi On BEST ANSWER

You should patch the function of _module and not the one imported in my_library:

import my_library
my_library._module._do_something = lambda: print('goodbye')
a = my_library.foo()
a.bar()

Also I recommend using unittest.mock (see doc for patch):

import my_library
from unittest.mock import patch

with patch.object(my_library._module, '_do_something', lambda: print('goodbye')):
    a = my_library.foo()
    a.bar()
1
Erdit On

If you wish to modify the function _do_something, you must make sure that you modify the function directly in the exact module it is located in.

The issue with your attempt is that you're trying to modify the function in the imported module my_library, but the method foo.bar is referencing the original function _do_something from _module.py.

import my_library

# Monkey patch the function in the module where its from
my_library._module._do_something = lambda: print('goodbye')

a = my_library.foo()
a.bar() #'goodbye'

Notice how in the code above we're accessing the function within _module.py directly. This is the key!