patchy
patchy copied to clipboard
a patchy interface to allow use in contexts
import contextlib
from concurrent import futures
import patchy
def sample():
return 1
patchy.prepare(
sample,
"""\
@@ -1,2 +1,2 @@
def sample():
- return 1
+ return 9001
""",
)
def run(patch):
with prepared_patch() if patch else contextlib.nullcontext():
return target()
with futures.ThreadPoolExecutor(max_workers=999) as executor:
assert list(executor.map(run, [i % 2 for i in range(100)))) == [1, 9001, 1, 9001 ...]
eg it would have to replace .__code__ with something that delegated to a ContextVar that either ran the old code or the patch, then the ContextVar could switch between them
Hmm maybe it would be better to just document how people could do it themselves with existing patchy:
import contextlib
from concurrent import futures
import patchy
def sample():
return 1
sample_ctx = ContextVar('sample', default=1)
patchy.patch(
sample,
f"""\
@@ -1,2 +1,2 @@
def sample():
- return 1
+ return importlib.import_module({__name__!r}).sample_ctx.get()
""",
)
def contextvar_context_manager(ctx):
@contextlib.contextmanager
def cm(update):
token = ctx.set(update)
try:
yield
finally:
ctx.reset(token)
sample_ctx_context_manager = contextvar_context_manager(sample_ctx)
def run(patch):
with sample_ctx_context_manager(9001) if patch else contextlib.nullcontext():
return target()
with futures.ThreadPoolExecutor(max_workers=999) as executor:
assert list(executor.map(run, [i % 2 for i in range(100)))) == [1, 9001, 1, 9001 ...]
Yeah I don't think replacing __code__ with something that does dispatch is possible.
Anyway... why do you want this?
Gonna close for now, if someone has this problem again they can find this.