Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does one monkey patch a function in python?

I'm having trouble replacing a function from a different module with another function and it's driving me crazy.

Let's say I have a module bar.py that looks like this:

from a_package.baz import do_something_expensive  def a_function():     print do_something_expensive() 

And I have another module that looks like this:

from bar import a_function a_function()  from a_package.baz import do_something_expensive do_something_expensive = lambda: 'Something really cheap.' a_function()  import a_package.baz a_package.baz.do_something_expensive = lambda: 'Something really cheap.' a_function() 

I would expect to get the results:

Something expensive! Something really cheap. Something really cheap. 

But instead I get this:

Something expensive! Something expensive! Something expensive! 

What am I doing wrong?

like image 918
guidoism Avatar asked Mar 03 '10 22:03

guidoism


People also ask

How does a monkey patch work?

In Python, the term monkey patch only refers to dynamic modifications of a class or module at runtime, motivated by the intent to patch existing third-party code as a workaround to a bug or feature which does not act as you desire.

What does patch mean in Python?

What is (monkey-)patching in Python? (monkey-) patching is a technique for changing code behaviour without altering its source. It is done in runtime, usually by overriding attributes of existing objects. An object can be an instance of some sort, a class or even a module.

What is monkey patching and is it ever a good idea?

Monkey patching is good for testing or mocking out behavior. They can be localized in factory/class decorators/metaclasses where they create a patched new class/object from another object to help with "cross-cutting concerns" in between ALL methods like logging/memoization/caching/database/persistance/unit conversion.

What is Monkeypatch in Pytest?

Monkeypatching functionsmonkeypatch can be used to patch functions dependent on the user to always return a specific value. In this example, monkeypatch. setattr is used to patch Path. home so that the known testing path Path("/abc") is always used when the test is run.


2 Answers

It may help to think of how Python namespaces work: they're essentially dictionaries. So when you do this:

from a_package.baz import do_something_expensive do_something_expensive = lambda: 'Something really cheap.' 

think of it like this:

do_something_expensive = a_package.baz['do_something_expensive'] do_something_expensive = lambda: 'Something really cheap.' 

Hopefully you can realize why this doesn't work then :-) Once you import a name into a namespace, the value of the name in the namespace you imported from is irrelevant. You're only modifying the value of do_something_expensive in the local module's namespace, or in a_package.baz's namespace, above. But because bar imports do_something_expensive directly, rather than referencing it from the module namespace, you need to write to its namespace:

import bar bar.do_something_expensive = lambda: 'Something really cheap.' 
like image 62
Nicholas Riley Avatar answered Sep 20 '22 05:09

Nicholas Riley


There's a really elegant decorator for this: Guido van Rossum: Python-Dev list: Monkeypatching Idioms.

There's also the dectools package, which I saw an PyCon 2010, which may be able to be used in this context too, but that might actually go the other way (monkeypatching at the method declarative level... where you're not)

like image 20
RyanWilcox Avatar answered Sep 21 '22 05:09

RyanWilcox