Python 中的简单通用运行时猴子补丁?
Easy generic runtime monkey patching in Python?
有时我想对小问题进行小修复,仅用于测试或内部目的。
例如,我想在 sphinx.writers.html5.HTML5Translator.visit_literal_block
.
中将 self.builder.current_docname
替换为 node.source
这样做的(优雅)方法是复制粘贴应该应用补丁的方法,然后修改我必须修改的小东西,然后用新方法覆盖原始方法。在这种情况下,我会有很多导入,我必须在本地 copy/paste 整个方法(糟糕的 SSOT 是糟糕的)。
相反,我想写这样的东西:
from monkey import ReMonkeyPatcher # Fake...
from sphinx.writer.html5 import HTML5Translator
# Override the class's method
ReMonkeyPatcher(HTML5Translator, 'self.builder.current_docname', 'node.source')
# Profit...
我读过 here,Python 的反射特性允许 运行-time hack inspect
和 ast
。所以我写了这个:
def monkey_replace(function, search, replace):
source = inspect.getsource(function)
while code[0].isspace():
code = textwrap.dedent(code)
code = code.replace(search, replace)
ast_tree = ast.parse(code)
compile(ast_tree, '<string>', mode='exec')
mod = {}
exec(code, mod) # <-- Here is the issue...
method = mod[function.__name__]
replace_function(function, method) # Doesn't know how to do that yet
主要问题是 exec(code, mod)
缺少上下文。要在 any/most 情况下工作,它必须在原始函数(导入...)的上下文中执行。
有什么优雅的方法吗?
在Python monkey-patching中非常容易。正如您在 post 中所说:
因为没有受保护的变量,你可以简单地覆盖你想要修补的函数:
HTMLTranslator.builder.current_docname = node.source
有时我想对小问题进行小修复,仅用于测试或内部目的。
例如,我想在 sphinx.writers.html5.HTML5Translator.visit_literal_block
.
self.builder.current_docname
替换为 node.source
这样做的(优雅)方法是复制粘贴应该应用补丁的方法,然后修改我必须修改的小东西,然后用新方法覆盖原始方法。在这种情况下,我会有很多导入,我必须在本地 copy/paste 整个方法(糟糕的 SSOT 是糟糕的)。
相反,我想写这样的东西:
from monkey import ReMonkeyPatcher # Fake...
from sphinx.writer.html5 import HTML5Translator
# Override the class's method
ReMonkeyPatcher(HTML5Translator, 'self.builder.current_docname', 'node.source')
# Profit...
我读过 here,Python 的反射特性允许 运行-time hack inspect
和 ast
。所以我写了这个:
def monkey_replace(function, search, replace):
source = inspect.getsource(function)
while code[0].isspace():
code = textwrap.dedent(code)
code = code.replace(search, replace)
ast_tree = ast.parse(code)
compile(ast_tree, '<string>', mode='exec')
mod = {}
exec(code, mod) # <-- Here is the issue...
method = mod[function.__name__]
replace_function(function, method) # Doesn't know how to do that yet
主要问题是 exec(code, mod)
缺少上下文。要在 any/most 情况下工作,它必须在原始函数(导入...)的上下文中执行。
有什么优雅的方法吗?
在Python monkey-patching中非常容易。正如您在 post 中所说: 因为没有受保护的变量,你可以简单地覆盖你想要修补的函数:
HTMLTranslator.builder.current_docname = node.source