A Python package designed for temporary recursive monkeypatching.
Project description
Monkeypatching
This package is designed for temporary, recursive monkeypatching. That is to replace an object within a module with another object, across the entire module hierarchy. With this package, you can temporarily change a deeply nested behaviour in a module across the whole module.
Why recursive monkeypatching?
Python modules are often structured to import functions from nested submodules. If you need to monkeypatch a function, it's often not sufficient to simply patch it at its function definition, since the function can persist from other import expressions. For example, if module.submodule.function
is used within module.othersubmodule
and is imported as:
from .submodule import function
Patching module.submodule.function
won't replace module.othersubmodule.function
and won't affect its usage. Recursive monkeypatching, however, ensures that the object is replaced throughout the module hierarchy.
Why temporary monkeypatching?
Monkeypatching can introduce ambiguity and result in unpredictable behavior. When you modify an object in an imported module, this change affects not only the current scope but also any other modules that rely on it. Such alterations can lead to unintended consequences and complicate system maintenance and debugging.
However, in the context of temporary monkeypatching, you can alter a module's behavior for a specific scope without affecting the underlying implementation. This is analogous to class inheritance, where a subclass can extend functionality without modifying the parent class.
With temporary monkeypatching, as soon as you exit the context (e.g., a with
block), the original behavior is restored, minimizing the chances of unintended ripple effects across your codebase.
Installation
To install Monkeypatching, use:
pip install monkeypatching
Usage
Temporary attribute replacement
Specific attributes within a module can temporarily be replaced using monkeypatch_setattr
:
from monkeypatching import monkeypatch_setattr
import json
def mock_loads(data, *args, **kwargs):
return {"mocked": True}
with monkeypatch_setattr(json, "loads", mock_loads):
print(json.loads('{"key": "value"}'))
# Output: {"mocked": True}
Temporary module patching
An example of temporary monkeypatching:
from monkeypatching import monkeypatch_module_object
import json
from json import dumps
def mock_dumps(data, *args, **kwargs):
kwargs['indent'] = 4 # Force indentation to 4 characters
return dumps(data, *args, **kwargs)
# Monkeypatch json.dumps in the json module and submodules
with monkeypatch_module_object(json, json.dumps, mock_dumps):
print(json.dumps({"key": "value"}))
# Output:
# {
# "key": "value"
# }
# After the block, json.dumps reverts to its original
print(json.dumps({"key": "value"}))
# Output: {"key": "value"}
Permanent module patching
For a lasting effect:
from monkeypatching import monkeypatch_module_object
import json
monkeypatch_module_object(json, json.dumps, mock_dumps)
print(json.dumps({"key": "value"}))
# Output:
# {
# "key": "value"
# }
Caching
For repeated monkeypatching at identical locations:
# With caching enabled
with monkeypatch_module_object(json, json.dumps, mock_dumps, cached=True):
print(json.dumps({"key": "value"}))
# Output:
# {
# "key": "value"
# }"
Caution: Activate caching only if monkeypatch locations remain consistent and the object hasn't been replaced elsewhere.
Overriding globals during function call
You can override global variables of a function during the execution of that function call.
from monkeypatching import override_globals_during_function_call
pi = 3
class PretendModule:
@staticmethod
def get_tau():
return 2 * pi
# Override 'pi' during a call to 'get_tau'
with override_globals_during_function_call(PretendModule, 'get_tau', {'pi': 3.14159265358979}):
print(PretendModule.get_tau())
# Output: 6.28318530717958
print(PretendModule.get_tau())
# Output: 3
API documentation
monkeypatch_module_object(module, obj_original, obj_replacement, cached=False)
Parameters
module
: Target module.obj_original
: The object that will be replaced.obj_replacement
: The object that will replaceobj_original
.cached
: Whether to cache the monkeypatch locations; default isFalse
.
Returns
A context manager. Within a with
block, obj_original
gets temporarily replaced by obj_replacement
across the specified module
and submodules. Outside a with
block, the replacement is permanent.
monkeypatch_setattr(module, attr_name, obj_replacement)
Parameters
module
: Target module.attr_name
: Attribute to be set viasetattr(module, attr_name, obj_replacement)
.obj_replacement
: Replacement object.
Returns
A context manager. Within a with
block, the specific attribute in the module is replaced by obj_replacement
. Outside a with
block, the replacement is permanent and identical to writing setattr(module, attr_name, obj_replacement)
.
override_globals_during_function_call(module, function_name, replacements, module_wide = False, module_wide_cached = False)
Parameters
module
: Target module.function_name
: The name of the function within the module.replacements
: A dictionary or object defining the global variables to be overridden.module_wide
: If set to True, the override acts asmonkeypatch_module_object
, otherwise asmonkeypatch_setattr
.module_wide_cached
: Whether to cache the monkeypatch locations; default isFalse
.
Returns
A context manager. Within a with
block, obj_original
gets temporarily replaced by obj_replacement
across the specified module
and submodules. Outside a with
block, the replacement is permanent.
Contributing
Contributions are always welcome! For bugs, features, or documentation updates, please open an issue or submit a pull request.
License
This project is under the MIT License.
Thank you for using Monkeypatching! For more information or queries, raise an issue or reach out with a pull request.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file monkeypatching-0.2.0.tar.gz
.
File metadata
- Download URL: monkeypatching-0.2.0.tar.gz
- Upload date:
- Size: 11.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.0.0 CPython/3.9.19
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 2ccf764f7fb47ae535d2f2c84e877d1af9d74fcc03f528dde9fe7a508d963c0c |
|
MD5 | 59f1372247d0467e15db808016c17ad1 |
|
BLAKE2b-256 | 9750da13b8963726da1d320e52485566280f5947978603c7f88c42e52b1829cd |
File details
Details for the file monkeypatching-0.2.0-py3-none-any.whl
.
File metadata
- Download URL: monkeypatching-0.2.0-py3-none-any.whl
- Upload date:
- Size: 8.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.0.0 CPython/3.9.19
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 6cc6d9002e57050bf9885e20e6602d20713d054eec123c98f297f17548037d37 |
|
MD5 | 0572d9ca8844863ba6dfd88b09499722 |
|
BLAKE2b-256 | b845deec36cb300891d8fe6aebe8aef4457d87a96b5baea50c929b4758706492 |