Threads#
threading.Thread does not inherit context, so these two helpers carry it across the boundary. asyncio needs neither: tasks snapshot the context natively.
wrap#
- nodrill.wrap(fn)#
Bind
fnto a snapshot of the context active whenwrapwas called, and return a callable with the same signature.- Raises:
TypeError –
fnis a coroutine or async-generator function. Calling one only builds the coroutine, whose body then resumes in the caller’s context, so the snapshot would be silently dropped. asyncio needs no wrapping, since a task created inside a provider block snapshots the context itself.
The snapshot is taken at
wrap()time, not at call time. Wrapping at import time binds import-time state.Each invocation replays the snapshot into a fresh
Context, so the wrapped callable is safe to call concurrently, and any contextvar writes the callee makes stay inside that call.The wrapper is built with
functools.wraps(), so__name__,__doc__and__wrapped__carry through.with provider(Config()): Thread(target=wrap(job)).start()
Executor#
- class nodrill.Executor#
A
ThreadPoolExecutorsubclass whose tasks run under the context active when they were submitted.The constructor,
shutdown, and the context-manager protocol are inherited unchanged. Onlysubmit()is overridden, somap()propagates context as well.Each task runs under its own
copy_context()snapshot, so a worker’s writes never leak into another task or back to the submitter.with provider(Config()), Executor(max_workers=4) as pool: results = list(pool.map(job, range(10)))