@entwanne, @debnet et moi jouons avec les gestionnaires de contextes ces temps-ci, ça m’a permis de faire ça par exemple : mdk/forking - La forge de l'AFPy
Le jeu du jour et d’aller plus loin dans la libération des gestionnaires de contextes, et je commence à toucher quelque chose, probablement encore un peu fragile, à peine testé, mais ça commence à marcher :
import ast
import inspect
import sys
import textwrap
class Stop(Exception):
"""Internal exception to block the execution of the context body."""
class With:
def tracer_cb(self, frame, event, arg):
if frame is self.frame and event == "opcode":
raise Stop
def __enter__(self):
current_frame = inspect.currentframe()
self.frame = current_frame.f_back
self.frame.f_trace = self.tracer_cb
self.frame.f_trace_opcodes = True
sys.settrace(self.tracer_cb)
def execute(self, body):
"""Do whatever with the body of the with statement."""
raise NotImplementedError
def _locate_with_body(self, tree, lineno):
"""Locate the with statement, in tree, at lineno."""
for node in ast.walk(tree):
try:
node_lineno = node.lineno
except AttributeError:
continue
if node_lineno == lineno:
return node.body
def __exit__(self, exc_type, exc, tb):
frame = tb.tb_frame
src = textwrap.dedent(inspect.getsource(tb.tb_frame))
tree = ast.parse(src)
module = ast.Module(body=self._locate_with_body(tree, tb.tb_frame.f_lineno))
compiled = compile(module, filename="<with>", mode="exec")
Function = type(Repeat.__enter__)
self.execute(Function(compiled, frame.f_globals, name="<with>"))
return True
class Repeat(With):
def __init__(self, times):
self.times = times
def execute(self, body):
for _ in range(self.times):
body()
with Repeat(4):
print(42)