gh-154562: Add ContextVar(thread_inheritable=...). - #154564
Conversation
Context variables created this way will automatically be inherited by the context for new threads, regardless of the setting of `thread_inherit_context`.
Documentation build overview
18 files changed ·
|
|
Another option would be to add a Example library code that wants to detect support: # logic to fallback to regular ContextVar
if hasattr(ContextVar, "thread_inheritable"):
var = ContextVar("var", thread_inheritable=True)
else:
var = ContextVar("var")
# logic to fallback to global binding
if hasattr(ContextVar, "thread_inheritable"):
# Context-local and inherited by new threads.
def _new_var(name, **kwargs):
return ContextVar(name, thread_inheritable=True, **kwargs)
elif getattr(sys.flags, "thread_inherit_context", False):
# Threads inherit the whole context, so an ordinary
# context variable is inherited too.
_new_var = ContextVar
else:
# Keep the library's historical global semantics.
_new_var = _GlobalVar |
f180876 to
f38695a
Compare
|
Using a keyword has the problem that you could pass |
I actually think this is useful, if you make |
|
I switched this back to draft. Given discussion on Discourse, I think we should have the tri-value |
Use a keyword arg so that `ContextVar(thread_inheritable=False)` is supported. The default is `thread_inheritable=None`, which means to use the value of `thread_inherit_context`. This allows libraries to explicitly specify if they want a variable to be inherited by new threads. Add C function `PyContextVar_NewWithFlags` for creating context variables which override automatic thread context inheritance.
ContextVar.thread_inheritable().ContextVar(thread_inheritable=...).
Summary
Add the keyword-only
thread_inheritableparameter tocontextvars.ContextVarto control automatic inheritance of individual variable bindings by new threading.Thread instances:sys.flags.thread_inherit_context.The policy is exposed through the read-only
ContextVar.thread_inheritableattribute.This gives libraries per-variable control between inheriting no context and inheriting the entire context. In particular, libraries can migrate global state to context-local state while preserving inheritance by new threads, without requiring applications to change a process-wide flag. Variables can also be excluded when the flag is enabled.
Behavior
context=is supplied,Thread.start()captures the caller's current bindings selected by the global flag and each variable'sthread_inheritablepolicy.ContextVarconstruction remains unchanged: omittingthread_inheritable, or passing None, followssys.flags.thread_inherit_context._thread.start_new_thread()or extension-module APIs, are unaffected.The C API adds
PyContextVar_NewWithFlags()with Py_CONTEXTVAR_INHERIT_THREAD_DEFAULT, Py_CONTEXTVAR_INHERIT_THREAD_ALWAYS, and Py_CONTEXTVAR_INHERIT_THREAD_NEVER.Implementation
Each Context maintains a second HAMT containing exactly the bindings selected for implicit thread inheritance. This redundant subset is kept synchronized by ContextVar.set() and reset() according to the interpreter-wide flag and each variable's immutable policy.
Thread.start()uses the private_contextvars._new_thread_context()factory to initialize the child directly from this subset. Because HAMTs are immutable, the subset can be shared with the child without scanning or copying bindings, so startup cost is independent of the number of bound variables.ContextVargains an internal three-state inheritance policy. PyContext remains publicly opaque, so the additional field does not affect the public C API layout.