是否可以还原(返回到旧方法)python Monkey补丁

时间:2018-06-28 21:35:41

标签: python monkeypatching

我使用一个专用的python模块,该模块在运行时修改了一些Django类方法(又名Monkey-patching)。如果我需要这些“旧”版本,是否有可能“回来”以覆盖猴子补丁?

例如,要导入这些类的初始版本?

这是一个如何在软件包中进行修补的示例:

read/update

1 个答案:

答案 0 :(得分:2)

这取决于补丁的功能。 Monkeypatching没什么特别的,它只是将不同的对象分配给名称。如果没有其他东西引用旧值了,那么它就已经从Python的内存中消失了。

但是,如果修补名称的代码以不同的变量形式保留了对原始对象的引用,则原始对象仍然可以“恢复”:

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

此处,target_function模块命名空间中的名称target.module被重新绑定以指向new_function,但是原始对象在_original_function的命名空间中仍然可用。修补代码。

如果在函数中完成此操作,则原始文档也可以作为 closure 使用。对于您的特定示例,您可以通过以下方式获取原件:

FilterExpression.resolve.__closure__[0].cell_contents

或者,如果您希望按名称访问:

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']

否则,您可以告诉Python使用importlib.reload() 重新加载原始模块:

import target.module
importlib.reload(target.module)

这将刷新模块名称空间,将所有全局名称“重置”为在导入时设置的名称(保留所有其他名称)。

但是请注意,任何直接引用 patched 对象(例如您的类对象)的代码都看不到更新后的对象!这是因为from target.module import target_function在当前名称空间中创建了对target_function对象的新引用,并且对原始target.module模块的重载不会更新任何其他直接引用。您必须手动更新这些其他引用,或者也重新加载它们的名称空间。