Reputation: 2069
I use a specialized python module which modifies some of the Django class methods in the runtime (aka monkey-patching). If I need these 'old' versions is it possible to 'come back' to them overriding monkey patching?
Something like importing the initial version of these classes, for example?
Here is an example of how patching was done in the package:
from django.template.base import FilterExpression
def patch_filter_expression():
original_resolve = FilterExpression.resolve
def resolve(self, context, ignore_failures=False):
return original_resolve(self, context, ignore_failures=False)
FilterExpression.resolve = resolve
Upvotes: 3
Views: 4541
Reputation: 1124170
It depends on what the patch did. Monkeypatching is nothing special, it's just an assignment of a different object to a name. If nothing else references the old value anymore, then it's gone from Python's memory.
But if the code that patched the name has kept a reference to the original object in the form of a different variable, then the original object is still there to be 'restored':
import target.module
_original_function = target.module.target_function
def new_function(*args, **kwargs):
result = _original_function(*args, **kwargs)
return result * 5
target.module.target_function = new_function
Here the name target_function
in the target.module
module namespace was re-bound to point to new_function
, but the original object is still available as _original_function
in the namespace of the patching code.
If this is done in a function, then the original could be available as a closure too. For your specific example, you can get the original with:
FilterExpression.resolve.__closure__[0].cell_contents
or, if you prefer access by name:
def closure_mapping(func):
closures, names = func.__closure__, func.__code__.co_freevars
return {n: c.cell_contents for n, c in zip(names, closures)}
original_resolve = closure_mapping(FilterExpression.resolve)['original_resolve']
Otherwise, you can tell Python to reload the original module with importlib.reload()
:
import target.module
importlib.reload(target.module)
This refreshes the module namespace, 'resetting' all global names to what they'd been set to at import time (any additional names are retained).
Note, however, that any code holding a direct reference to the patched object (such as your class object), would not see the updated objects! That's because from target.module import target_function
creates a new reference to the target_function
object in the current namespace and no amount of reloading of the original target.module
module will update any of the other direct references. You'd have to update those other references manually, or reload their namespaces too.
Upvotes: 7