Let foo
be a Python package, built on top of another Python package bar
. Sometimes foo
may wish to extend the bar
package by adding features (e.g. methods) to existing classes or modules. Sometimes the whole package is meant as a temporary location where experimental features can mature and be distributed early until they get merged into the bar
package.
In such cases, it's desirable to write the source code of those features as closely as if they were in the bar
package.
recursive-monkey-patch
enables this by providing a tool to recursively monkey patch the bar
package. Let's assume for example that we are writing a package bar-foo
that requires the addition of a method f
to the class bar.x.y.z.Z
.
To achieve this, one writes a module foo.x.y.z.Z
containing a dummy Z
class:
class Z:
def f(self):
return "f"
And then, upon initializing the package, one runs:
import bar
import foo
from recursive_monkey_patch import monkey_patch
monkey_patch(foo, bar)
which will recursively crawl through foo
, and insert methods like f
at the corresponding location in bar
. If a class or module in foo
does not exist in bar
, then the module is inserted at the corresponding location in bar
This package is primarily meant for writing (experimental) packages on top of SageMath, an open source software for mathematics that includes a large Python library. However, the dependency upon the SageMath
package is actually light:
- Running the doctests requires
sage
; - When the SageMath package is present, the monkey patching involves a few additional Sage specific hacks.
- Support for lazy imports
- Where is the natural place for running `monkey_patch in a package?
- patchy: runtime application of source patches at the granularity of functions
- unittest.mock.patch: temporary runtime monkey patching of functions; meant primarily for testing purposes