Python: how to learn monkey patch (swap)

Suppose I have the following 2 classes in module a

 class Real(object): ... def print_stuff(self): print 'real' class Fake(Real): def print_stff(self): print 'fake' 

in module b it uses the class Real

 from a import Real Real().print_stuff() 

How to make a monkey patch so that when b imports Real , it actually changes to Fake ?

I tried to do this in the initialization script, but this will not work.

 if env == 'dev': from a import Real, Fake Real = Fake 

My goal is to use the Fake class in development mode.

+6
source share
2 answers

The problem is that when you do -

 from a import Real, Fake 

Basically, you import these two classes into the initialize script namespace and create the Real and Fake names in the initialize script namespace. Then you specify the name Real in the initialize script on Fake , but that will not change anything in the actual module a .

If initialize script is another .py / script module when run at the beginning of your source program, you can use below -

 if env == 'dev': import a a.Real = a.Fake 

Note: this would make a.Real reference to the Fake class whenever you use the Real module from a after executing the specified line.


Although I would suggest that it is best to do this in your module a , by providing the ability to check env in this module, as -

 if <someothermodule>.env == 'dev': Real = Fake 

As asked in the comments -

Doesn't import also import into script namespace initialization? What is the difference between importing modules and classes?

The fact is that when you import only a class using from a import class , what you actually do is create this class variable in the module namespace (in the module into which you import it), changing this variable to a point to something new in this module namespace does not affect the source class in its source module object, it only affects the module in which it was changed.

But when you do import a , you simply import module a (and when you import a module object, it is also cached in the sys.modules dictionary, so any other imports in a from any other modules will get this cached version from sys.modules ) (Another note : from a import something also internally imports a and caches it in sys.modules , but allows you to not go into these details, since I think that is not necessary here).

And then when you do a.Real = <something> , you change the Real attribute of the object of module a , which points to the class, to something else, it directly mutates module a , therefore, this change is also reflected when module a imported from another module.

+2
source

You can use patch from the mock module. Here is an example:

 with patch('yourpackage.b.Real') as fake_real: fake_real.return_value = Fake() foo = b.someClass() foo.somemethod() 
+3
source

All Articles