I sometime would like to apply small fixes to small issues, just for testing, or for internal purposes.
For example I would like to just replace self.builder.current_docname
with node.source
in sphinx.writers.html5.HTML5Translator.visit_literal_block
.
The (elegant) way of doing it is to copy-paste the method where the patch should be applied then modify the little tiny thing I have to modify, then override the original method with the new one. In this case I would have a lot of imports and I have to copy/paste the whole method locally (bad SSOT is bad).
Instead I would would like to write something like:
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...
I've read here that the reflective nature of Python allows for run-time hacks with inspect
and ast
. So I wrote this:
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
The main issue is exec(code, mod)
in which the context is missing. To work in any/most cases, it has to be executed in the context of the original function (imports...).
Is there an elegant way of doing it?