python-peps/pep-0567/index.html

1004 lines
89 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="color-scheme" content="light dark">
<title>PEP 567 Context Variables | peps.python.org</title>
<link rel="shortcut icon" href="../_static/py.png">
<link rel="canonical" href="https://peps.python.org/pep-0567/">
<link rel="stylesheet" href="../_static/style.css" type="text/css">
<link rel="stylesheet" href="../_static/mq.css" type="text/css">
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" media="(prefers-color-scheme: light)" id="pyg-light">
<link rel="stylesheet" href="../_static/pygments_dark.css" type="text/css" media="(prefers-color-scheme: dark)" id="pyg-dark">
<link rel="alternate" type="application/rss+xml" title="Latest PEPs" href="https://peps.python.org/peps.rss">
<meta property="og:title" content='PEP 567 Context Variables | peps.python.org'>
<meta property="og:description" content="This PEP proposes a new contextvars module and a set of new CPython C APIs to support context variables. This concept is similar to thread-local storage (TLS), but, unlike TLS, it also allows correctly keeping track of values per asynchronous task, e.g...">
<meta property="og:type" content="website">
<meta property="og:url" content="https://peps.python.org/pep-0567/">
<meta property="og:site_name" content="Python Enhancement Proposals (PEPs)">
<meta property="og:image" content="https://peps.python.org/_static/og-image.png">
<meta property="og:image:alt" content="Python PEPs">
<meta property="og:image:width" content="200">
<meta property="og:image:height" content="200">
<meta name="description" content="This PEP proposes a new contextvars module and a set of new CPython C APIs to support context variables. This concept is similar to thread-local storage (TLS), but, unlike TLS, it also allows correctly keeping track of values per asynchronous task, e.g...">
<meta name="theme-color" content="#3776ab">
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="svg-sun-half" viewBox="0 0 24 24" pointer-events="all">
<title>Following system colour scheme</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="9"></circle>
<path d="M12 3v18m0-12l4.65-4.65M12 14.3l7.37-7.37M12 19.6l8.85-8.85"></path>
</svg>
</symbol>
<symbol id="svg-moon" viewBox="0 0 24 24" pointer-events="all">
<title>Selected dark colour scheme</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z"></path>
</svg>
</symbol>
<symbol id="svg-sun" viewBox="0 0 24 24" pointer-events="all">
<title>Selected light colour scheme</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
</symbol>
</svg>
<script>
document.documentElement.dataset.colour_scheme = localStorage.getItem("colour_scheme") || "auto"
</script>
<section id="pep-page-section">
<header>
<h1>Python Enhancement Proposals</h1>
<ul class="breadcrumbs">
<li><a href="https://www.python.org/" title="The Python Programming Language">Python</a> &raquo; </li>
<li><a href="../pep-0000/">PEP Index</a> &raquo; </li>
<li>PEP 567</li>
</ul>
<button id="colour-scheme-cycler" onClick="setColourScheme(nextColourScheme())">
<svg aria-hidden="true" class="colour-scheme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
<svg aria-hidden="true" class="colour-scheme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg aria-hidden="true" class="colour-scheme-icon-when-light"><use href="#svg-sun"></use></svg>
<span class="visually-hidden">Toggle light / dark / auto colour theme</span>
</button>
</header>
<article>
<section id="pep-content">
<h1 class="page-title">PEP 567 Context Variables</h1>
<dl class="rfc2822 field-list simple">
<dt class="field-odd">Author<span class="colon">:</span></dt>
<dd class="field-odd">Yury Selivanov &lt;yury&#32;&#97;t&#32;edgedb.com&gt;</dd>
<dt class="field-even">Status<span class="colon">:</span></dt>
<dd class="field-even"><abbr title="Accepted and implementation complete, or no longer active">Final</abbr></dd>
<dt class="field-odd">Type<span class="colon">:</span></dt>
<dd class="field-odd"><abbr title="Normative PEP with a new feature for Python, implementation change for CPython or interoperability standard for the ecosystem">Standards Track</abbr></dd>
<dt class="field-even">Created<span class="colon">:</span></dt>
<dd class="field-even">12-Dec-2017</dd>
<dt class="field-odd">Python-Version<span class="colon">:</span></dt>
<dd class="field-odd">3.7</dd>
<dt class="field-even">Post-History<span class="colon">:</span></dt>
<dd class="field-even">12-Dec-2017, 28-Dec-2017, 16-Jan-2018</dd>
</dl>
<hr class="docutils" />
<section id="contents">
<details><summary>Table of Contents</summary><ul class="simple">
<li><a class="reference internal" href="#abstract">Abstract</a></li>
<li><a class="reference internal" href="#api-design-and-implementation-revisions">API Design and Implementation Revisions</a></li>
<li><a class="reference internal" href="#rationale">Rationale</a></li>
<li><a class="reference internal" href="#introduction">Introduction</a></li>
<li><a class="reference internal" href="#specification">Specification</a><ul>
<li><a class="reference internal" href="#contextvars-contextvar">contextvars.ContextVar</a></li>
<li><a class="reference internal" href="#contextvars-token">contextvars.Token</a></li>
<li><a class="reference internal" href="#contextvars-context">contextvars.Context</a></li>
<li><a class="reference internal" href="#asyncio">asyncio</a></li>
</ul>
</li>
<li><a class="reference internal" href="#implementation">Implementation</a></li>
<li><a class="reference internal" href="#summary-of-the-new-apis">Summary of the New APIs</a><ul>
<li><a class="reference internal" href="#python-api">Python API</a></li>
<li><a class="reference internal" href="#c-api">C API</a></li>
</ul>
</li>
<li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a><ul>
<li><a class="reference internal" href="#replicating-threading-local-interface">Replicating threading.local() interface</a></li>
<li><a class="reference internal" href="#replacing-token-with-contextvar-unset">Replacing Token with ContextVar.unset()</a></li>
<li><a class="reference internal" href="#having-token-reset-instead-of-contextvar-reset">Having Token.reset() instead of ContextVar.reset()</a></li>
<li><a class="reference internal" href="#making-context-objects-picklable">Making Context objects picklable</a></li>
<li><a class="reference internal" href="#making-context-a-mutablemapping">Making Context a MutableMapping</a></li>
<li><a class="reference internal" href="#having-initial-values-for-contextvars">Having initial values for ContextVars</a></li>
</ul>
</li>
<li><a class="reference internal" href="#backwards-compatibility">Backwards Compatibility</a></li>
<li><a class="reference internal" href="#examples">Examples</a><ul>
<li><a class="reference internal" href="#converting-code-that-uses-threading-local">Converting code that uses threading.local()</a></li>
<li><a class="reference internal" href="#offloading-execution-to-other-threads">Offloading execution to other threads</a></li>
</ul>
</li>
<li><a class="reference internal" href="#reference-implementation">Reference Implementation</a></li>
<li><a class="reference internal" href="#acceptance">Acceptance</a></li>
<li><a class="reference internal" href="#references">References</a></li>
<li><a class="reference internal" href="#acknowledgments">Acknowledgments</a></li>
<li><a class="reference internal" href="#copyright">Copyright</a></li>
</ul>
</details></section>
<section id="abstract">
<h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2>
<p>This PEP proposes a new <code class="docutils literal notranslate"><span class="pre">contextvars</span></code> module and a set of new
CPython C APIs to support context variables. This concept is
similar to thread-local storage (TLS), but, unlike TLS, it also allows
correctly keeping track of values per asynchronous task, e.g.
<code class="docutils literal notranslate"><span class="pre">asyncio.Task</span></code>.</p>
<p>This proposal is a simplified version of <a class="pep reference internal" href="../pep-0550/" title="PEP 550 Execution Context">PEP 550</a>. The key
difference is that this PEP is concerned only with solving the case
for asynchronous tasks, not for generators. There are no proposed
modifications to any built-in types or to the interpreter.</p>
<p>This proposal is not strictly related to Python Context Managers.
Although it does provide a mechanism that can be used by Context
Managers to store their state.</p>
</section>
<section id="api-design-and-implementation-revisions">
<h2><a class="toc-backref" href="#api-design-and-implementation-revisions" role="doc-backlink">API Design and Implementation Revisions</a></h2>
<p>In <strong>Python 3.7.1</strong> the signatures of all context variables
C APIs were <strong>changed</strong> to use <code class="docutils literal notranslate"><span class="pre">PyObject</span> <span class="pre">*</span></code> pointers instead
of <code class="docutils literal notranslate"><span class="pre">PyContext</span> <span class="pre">*</span></code>, <code class="docutils literal notranslate"><span class="pre">PyContextVar</span> <span class="pre">*</span></code>, and <code class="docutils literal notranslate"><span class="pre">PyContextToken</span> <span class="pre">*</span></code>,
e.g.:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="o">//</span> <span class="ow">in</span> <span class="mf">3.7.0</span><span class="p">:</span>
<span class="n">PyContext</span> <span class="o">*</span><span class="n">PyContext_New</span><span class="p">(</span><span class="n">void</span><span class="p">);</span>
<span class="o">//</span> <span class="ow">in</span> <span class="mf">3.7.1</span><span class="o">+</span><span class="p">:</span>
<span class="n">PyObject</span> <span class="o">*</span><span class="n">PyContext_New</span><span class="p">(</span><span class="n">void</span><span class="p">);</span>
</pre></div>
</div>
<p>See <a class="footnote-reference brackets" href="#id12" id="id1">[6]</a> for more details. The <a class="reference internal" href="#c-api">C API</a> section of this PEP was
updated to reflect the change.</p>
</section>
<section id="rationale">
<h2><a class="toc-backref" href="#rationale" role="doc-backlink">Rationale</a></h2>
<p>Thread-local variables are insufficient for asynchronous tasks that
execute concurrently in the same OS thread. Any context manager that
saves and restores a context value using <code class="docutils literal notranslate"><span class="pre">threading.local()</span></code> will
have its context values bleed to other code unexpectedly when used
in async/await code.</p>
<p>A few examples where having a working context local storage for
asynchronous code is desirable:</p>
<ul class="simple">
<li>Context managers like <code class="docutils literal notranslate"><span class="pre">decimal</span></code> contexts and <code class="docutils literal notranslate"><span class="pre">numpy.errstate</span></code>.</li>
<li>Request-related data, such as security tokens and request
data in web applications, language context for <code class="docutils literal notranslate"><span class="pre">gettext</span></code>, etc.</li>
<li>Profiling, tracing, and logging in large code bases.</li>
</ul>
</section>
<section id="introduction">
<h2><a class="toc-backref" href="#introduction" role="doc-backlink">Introduction</a></h2>
<p>The PEP proposes a new mechanism for managing context variables.
The key classes involved in this mechanism are <code class="docutils literal notranslate"><span class="pre">contextvars.Context</span></code>
and <code class="docutils literal notranslate"><span class="pre">contextvars.ContextVar</span></code>. The PEP also proposes some policies
for using the mechanism around asynchronous tasks.</p>
<p>The proposed mechanism for accessing context variables uses the
<code class="docutils literal notranslate"><span class="pre">ContextVar</span></code> class. A module (such as <code class="docutils literal notranslate"><span class="pre">decimal</span></code>) that wishes to
use the new mechanism should:</p>
<ul class="simple">
<li>declare a module-global variable holding a <code class="docutils literal notranslate"><span class="pre">ContextVar</span></code> to
serve as a key;</li>
<li>access the current value via the <code class="docutils literal notranslate"><span class="pre">get()</span></code> method on the
key variable;</li>
<li>modify the current value via the <code class="docutils literal notranslate"><span class="pre">set()</span></code> method on the
key variable.</li>
</ul>
<p>The notion of “current value” deserves special consideration:
different asynchronous tasks that exist and execute concurrently
may have different values for the same key. This idea is well known
from thread-local storage but in this case the locality of the value is
not necessarily bound to a thread. Instead, there is the notion of the
“current <code class="docutils literal notranslate"><span class="pre">Context</span></code>” which is stored in thread-local storage.
Manipulation of the current context is the responsibility of the
task framework, e.g. asyncio.</p>
<p>A <code class="docutils literal notranslate"><span class="pre">Context</span></code> is a mapping of <code class="docutils literal notranslate"><span class="pre">ContextVar</span></code> objects to their values.
The <code class="docutils literal notranslate"><span class="pre">Context</span></code> itself exposes the <code class="docutils literal notranslate"><span class="pre">abc.Mapping</span></code> interface
(not <code class="docutils literal notranslate"><span class="pre">abc.MutableMapping</span></code>!), so it cannot be modified directly.
To set a new value for a context variable in a <code class="docutils literal notranslate"><span class="pre">Context</span></code> object,
the user needs to:</p>
<ul class="simple">
<li>make the <code class="docutils literal notranslate"><span class="pre">Context</span></code> object “current” using the <code class="docutils literal notranslate"><span class="pre">Context.run()</span></code>
method;</li>
<li>use <code class="docutils literal notranslate"><span class="pre">ContextVar.set()</span></code> to set a new value for the context
variable.</li>
</ul>
<p>The <code class="docutils literal notranslate"><span class="pre">ContextVar.get()</span></code> method looks for the variable in the current
<code class="docutils literal notranslate"><span class="pre">Context</span></code> object using <code class="docutils literal notranslate"><span class="pre">self</span></code> as a key.</p>
<p>It is not possible to get a direct reference to the current <code class="docutils literal notranslate"><span class="pre">Context</span></code>
object, but it is possible to obtain a shallow copy of it using the
<code class="docutils literal notranslate"><span class="pre">contextvars.copy_context()</span></code> function. This ensures that the
<em>caller</em> of <code class="docutils literal notranslate"><span class="pre">Context.run()</span></code> is the sole owner of its <code class="docutils literal notranslate"><span class="pre">Context</span></code>
object.</p>
</section>
<section id="specification">
<h2><a class="toc-backref" href="#specification" role="doc-backlink">Specification</a></h2>
<p>A new standard library module <code class="docutils literal notranslate"><span class="pre">contextvars</span></code> is added with the
following APIs:</p>
<ol class="arabic simple">
<li>The <code class="docutils literal notranslate"><span class="pre">copy_context()</span> <span class="pre">-&gt;</span> <span class="pre">Context</span></code> function is used to get a copy of
the current <code class="docutils literal notranslate"><span class="pre">Context</span></code> object for the current OS thread.</li>
<li>The <code class="docutils literal notranslate"><span class="pre">ContextVar</span></code> class to declare and access context variables.</li>
<li>The <code class="docutils literal notranslate"><span class="pre">Context</span></code> class encapsulates context state. Every OS thread
stores a reference to its current <code class="docutils literal notranslate"><span class="pre">Context</span></code> instance.
It is not possible to control that reference directly.
Instead, the <code class="docutils literal notranslate"><span class="pre">Context.run(callable,</span> <span class="pre">*args,</span> <span class="pre">**kwargs)</span></code> method is
used to run Python code in another context.</li>
</ol>
<section id="contextvars-contextvar">
<h3><a class="toc-backref" href="#contextvars-contextvar" role="doc-backlink">contextvars.ContextVar</a></h3>
<p>The <code class="docutils literal notranslate"><span class="pre">ContextVar</span></code> class has the following constructor signature:
<code class="docutils literal notranslate"><span class="pre">ContextVar(name,</span> <span class="pre">*,</span> <span class="pre">default=_NO_DEFAULT)</span></code>. The <code class="docutils literal notranslate"><span class="pre">name</span></code> parameter
is used for introspection and debug purposes, and is exposed
as a read-only <code class="docutils literal notranslate"><span class="pre">ContextVar.name</span></code> attribute. The <code class="docutils literal notranslate"><span class="pre">default</span></code>
parameter is optional. Example:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Declare a context variable &#39;var&#39; with the default value 42.</span>
<span class="n">var</span> <span class="o">=</span> <span class="n">ContextVar</span><span class="p">(</span><span class="s1">&#39;var&#39;</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="mi">42</span><span class="p">)</span>
</pre></div>
</div>
<p>(The <code class="docutils literal notranslate"><span class="pre">_NO_DEFAULT</span></code> is an internal sentinel object used to
detect if the default value was provided.)</p>
<p><code class="docutils literal notranslate"><span class="pre">ContextVar.get(default=_NO_DEFAULT)</span></code> returns a value for
the context variable for the current <code class="docutils literal notranslate"><span class="pre">Context</span></code>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Get the value of `var`.</span>
<span class="n">var</span><span class="o">.</span><span class="n">get</span><span class="p">()</span>
</pre></div>
</div>
<p>If there is no value for the variable in the current context,
<code class="docutils literal notranslate"><span class="pre">ContextVar.get()</span></code> will:</p>
<ul class="simple">
<li>return the value of the <em>default</em> argument of the <code class="docutils literal notranslate"><span class="pre">get()</span></code> method,
if provided; or</li>
<li>return the default value for the context variable, if provided; or</li>
<li>raise a <code class="docutils literal notranslate"><span class="pre">LookupError</span></code>.</li>
</ul>
<p><code class="docutils literal notranslate"><span class="pre">ContextVar.set(value)</span> <span class="pre">-&gt;</span> <span class="pre">Token</span></code> is used to set a new value for
the context variable in the current <code class="docutils literal notranslate"><span class="pre">Context</span></code>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Set the variable &#39;var&#39; to 1 in the current context.</span>
<span class="n">var</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</pre></div>
</div>
<p><code class="docutils literal notranslate"><span class="pre">ContextVar.reset(token)</span></code> is used to reset the variable in the
current context to the value it had before the <code class="docutils literal notranslate"><span class="pre">set()</span></code> operation
that created the <code class="docutils literal notranslate"><span class="pre">token</span></code> (or to remove the variable if it was
not set):</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Assume: var.get(None) is None</span>
<span class="c1"># Set &#39;var&#39; to 1:</span>
<span class="n">token</span> <span class="o">=</span> <span class="n">var</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="k">try</span><span class="p">:</span>
<span class="c1"># var.get() == 1</span>
<span class="k">finally</span><span class="p">:</span>
<span class="n">var</span><span class="o">.</span><span class="n">reset</span><span class="p">(</span><span class="n">token</span><span class="p">)</span>
<span class="c1"># After reset: var.get(None) is None,</span>
<span class="c1"># i.e. &#39;var&#39; was removed from the current context.</span>
</pre></div>
</div>
<p>The <code class="docutils literal notranslate"><span class="pre">ContextVar.reset()</span></code> method raises:</p>
<ul class="simple">
<li>a <code class="docutils literal notranslate"><span class="pre">ValueError</span></code> if it is called with a token object created
by another variable;</li>
<li>a <code class="docutils literal notranslate"><span class="pre">ValueError</span></code> if the current <code class="docutils literal notranslate"><span class="pre">Context</span></code> object does not match
the one where the token object was created;</li>
<li>a <code class="docutils literal notranslate"><span class="pre">RuntimeError</span></code> if the token object has already been used once
to reset the variable.</li>
</ul>
</section>
<section id="contextvars-token">
<h3><a class="toc-backref" href="#contextvars-token" role="doc-backlink">contextvars.Token</a></h3>
<p><code class="docutils literal notranslate"><span class="pre">contextvars.Token</span></code> is an opaque object that should be used to
restore the <code class="docutils literal notranslate"><span class="pre">ContextVar</span></code> to its previous value, or to remove it from
the context if the variable was not set before. It can be created
only by calling <code class="docutils literal notranslate"><span class="pre">ContextVar.set()</span></code>.</p>
<p>For debug and introspection purposes it has:</p>
<ul class="simple">
<li>a read-only attribute <code class="docutils literal notranslate"><span class="pre">Token.var</span></code> pointing to the variable
that created the token;</li>
<li>a read-only attribute <code class="docutils literal notranslate"><span class="pre">Token.old_value</span></code> set to the value the
variable had before the <code class="docutils literal notranslate"><span class="pre">set()</span></code> call, or to <code class="docutils literal notranslate"><span class="pre">Token.MISSING</span></code>
if the variable wasnt set before.</li>
</ul>
</section>
<section id="contextvars-context">
<h3><a class="toc-backref" href="#contextvars-context" role="doc-backlink">contextvars.Context</a></h3>
<p><code class="docutils literal notranslate"><span class="pre">Context</span></code> object is a mapping of context variables to values.</p>
<p><code class="docutils literal notranslate"><span class="pre">Context()</span></code> creates an empty context. To get a copy of the current
<code class="docutils literal notranslate"><span class="pre">Context</span></code> for the current OS thread, use the
<code class="docutils literal notranslate"><span class="pre">contextvars.copy_context()</span></code> method:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">ctx</span> <span class="o">=</span> <span class="n">contextvars</span><span class="o">.</span><span class="n">copy_context</span><span class="p">()</span>
</pre></div>
</div>
<p>To run Python code in some <code class="docutils literal notranslate"><span class="pre">Context</span></code>, use <code class="docutils literal notranslate"><span class="pre">Context.run()</span></code>
method:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">ctx</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">function</span><span class="p">)</span>
</pre></div>
</div>
<p>Any changes to any context variables that <code class="docutils literal notranslate"><span class="pre">function</span></code> causes will
be contained in the <code class="docutils literal notranslate"><span class="pre">ctx</span></code> context:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">var</span> <span class="o">=</span> <span class="n">ContextVar</span><span class="p">(</span><span class="s1">&#39;var&#39;</span><span class="p">)</span>
<span class="n">var</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="s1">&#39;spam&#39;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
<span class="c1"># &#39;var&#39; was set to &#39;spam&#39; before</span>
<span class="c1"># calling &#39;copy_context()&#39; and &#39;ctx.run(main)&#39;, so:</span>
<span class="c1"># var.get() == ctx[var] == &#39;spam&#39;</span>
<span class="n">var</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="s1">&#39;ham&#39;</span><span class="p">)</span>
<span class="c1"># Now, after setting &#39;var&#39; to &#39;ham&#39;:</span>
<span class="c1"># var.get() == ctx[var] == &#39;ham&#39;</span>
<span class="n">ctx</span> <span class="o">=</span> <span class="n">copy_context</span><span class="p">()</span>
<span class="c1"># Any changes that the &#39;main&#39; function makes to &#39;var&#39;</span>
<span class="c1"># will be contained in &#39;ctx&#39;.</span>
<span class="n">ctx</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">main</span><span class="p">)</span>
<span class="c1"># The &#39;main()&#39; function was run in the &#39;ctx&#39; context,</span>
<span class="c1"># so changes to &#39;var&#39; are contained in it:</span>
<span class="c1"># ctx[var] == &#39;ham&#39;</span>
<span class="c1"># However, outside of &#39;ctx&#39;, &#39;var&#39; is still set to &#39;spam&#39;:</span>
<span class="c1"># var.get() == &#39;spam&#39;</span>
</pre></div>
</div>
<p><code class="docutils literal notranslate"><span class="pre">Context.run()</span></code> raises a <code class="docutils literal notranslate"><span class="pre">RuntimeError</span></code> when called on the same
context object from more than one OS thread, or when called
recursively.</p>
<p><code class="docutils literal notranslate"><span class="pre">Context.copy()</span></code> returns a shallow copy of the context object.</p>
<p><code class="docutils literal notranslate"><span class="pre">Context</span></code> objects implement the <code class="docutils literal notranslate"><span class="pre">collections.abc.Mapping</span></code> ABC.
This can be used to introspect contexts:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">ctx</span> <span class="o">=</span> <span class="n">contextvars</span><span class="o">.</span><span class="n">copy_context</span><span class="p">()</span>
<span class="c1"># Print all context variables and their values in &#39;ctx&#39;:</span>
<span class="nb">print</span><span class="p">(</span><span class="n">ctx</span><span class="o">.</span><span class="n">items</span><span class="p">())</span>
<span class="c1"># Print the value of &#39;some_variable&#39; in context &#39;ctx&#39;:</span>
<span class="nb">print</span><span class="p">(</span><span class="n">ctx</span><span class="p">[</span><span class="n">some_variable</span><span class="p">])</span>
</pre></div>
</div>
<p>Note that all Mapping methods, including <code class="docutils literal notranslate"><span class="pre">Context.__getitem__</span></code> and
<code class="docutils literal notranslate"><span class="pre">Context.get</span></code>, ignore default values for context variables
(i.e. <code class="docutils literal notranslate"><span class="pre">ContextVar.default</span></code>). This means that for a variable <em>var</em>
that was created with a default value and was not set in the
<em>context</em>:</p>
<ul class="simple">
<li><code class="docutils literal notranslate"><span class="pre">context[var]</span></code> raises a <code class="docutils literal notranslate"><span class="pre">KeyError</span></code>,</li>
<li><code class="docutils literal notranslate"><span class="pre">var</span> <span class="pre">in</span> <span class="pre">context</span></code> returns <code class="docutils literal notranslate"><span class="pre">False</span></code>,</li>
<li>the variable isnt included in <code class="docutils literal notranslate"><span class="pre">context.items()</span></code>, etc.</li>
</ul>
</section>
<section id="asyncio">
<h3><a class="toc-backref" href="#asyncio" role="doc-backlink">asyncio</a></h3>
<p><code class="docutils literal notranslate"><span class="pre">asyncio</span></code> uses <code class="docutils literal notranslate"><span class="pre">Loop.call_soon()</span></code>, <code class="docutils literal notranslate"><span class="pre">Loop.call_later()</span></code>,
and <code class="docutils literal notranslate"><span class="pre">Loop.call_at()</span></code> to schedule the asynchronous execution of a
function. <code class="docutils literal notranslate"><span class="pre">asyncio.Task</span></code> uses <code class="docutils literal notranslate"><span class="pre">call_soon()</span></code> to run the
wrapped coroutine.</p>
<p>We modify <code class="docutils literal notranslate"><span class="pre">Loop.call_{at,later,soon}</span></code> and
<code class="docutils literal notranslate"><span class="pre">Future.add_done_callback()</span></code> to accept the new optional <em>context</em>
keyword-only argument, which defaults to the current context:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">call_soon</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">callback</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="n">context</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="k">if</span> <span class="n">context</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">context</span> <span class="o">=</span> <span class="n">contextvars</span><span class="o">.</span><span class="n">copy_context</span><span class="p">()</span>
<span class="c1"># ... some time later</span>
<span class="n">context</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">callback</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">)</span>
</pre></div>
</div>
<p>Tasks in asyncio need to maintain their own context that they inherit
from the point they were created at. <code class="docutils literal notranslate"><span class="pre">asyncio.Task</span></code> is modified
as follows:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Task</span><span class="p">:</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">coro</span><span class="p">):</span>
<span class="o">...</span>
<span class="c1"># Get the current context snapshot.</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_context</span> <span class="o">=</span> <span class="n">contextvars</span><span class="o">.</span><span class="n">copy_context</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_loop</span><span class="o">.</span><span class="n">call_soon</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_step</span><span class="p">,</span> <span class="n">context</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">_context</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">_step</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">exc</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="o">...</span>
<span class="c1"># Every advance of the wrapped coroutine is done in</span>
<span class="c1"># the task&#39;s context.</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_loop</span><span class="o">.</span><span class="n">call_soon</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_step</span><span class="p">,</span> <span class="n">context</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">_context</span><span class="p">)</span>
<span class="o">...</span>
</pre></div>
</div>
</section>
</section>
<section id="implementation">
<h2><a class="toc-backref" href="#implementation" role="doc-backlink">Implementation</a></h2>
<p>This section explains high-level implementation details in
pseudo-code. Some optimizations are omitted to keep this section
short and clear.</p>
<p>The <code class="docutils literal notranslate"><span class="pre">Context</span></code> mapping is implemented using an immutable dictionary.
This allows for a O(1) implementation of the <code class="docutils literal notranslate"><span class="pre">copy_context()</span></code>
function. The reference implementation implements the immutable
dictionary using Hash Array Mapped Tries (HAMT); see <a class="pep reference internal" href="../pep-0550/" title="PEP 550 Execution Context">PEP 550</a>
for analysis of HAMT performance <a class="footnote-reference brackets" href="#id7" id="id2">[1]</a>.</p>
<p>For the purposes of this section, we implement an immutable dictionary
using a copy-on-write approach and the built-in dict type:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">_ContextData</span><span class="p">:</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_mapping</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">()</span>
<span class="k">def</span> <span class="fm">__getitem__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_mapping</span><span class="p">[</span><span class="n">key</span><span class="p">]</span>
<span class="k">def</span> <span class="fm">__contains__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">):</span>
<span class="k">return</span> <span class="n">key</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">_mapping</span>
<span class="k">def</span> <span class="fm">__len__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_mapping</span><span class="p">)</span>
<span class="k">def</span> <span class="fm">__iter__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">iter</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_mapping</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">set</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="n">copy</span> <span class="o">=</span> <span class="n">_ContextData</span><span class="p">()</span>
<span class="n">copy</span><span class="o">.</span><span class="n">_mapping</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_mapping</span><span class="o">.</span><span class="n">copy</span><span class="p">()</span>
<span class="n">copy</span><span class="o">.</span><span class="n">_mapping</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span>
<span class="k">return</span> <span class="n">copy</span>
<span class="k">def</span> <span class="nf">delete</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">):</span>
<span class="n">copy</span> <span class="o">=</span> <span class="n">_ContextData</span><span class="p">()</span>
<span class="n">copy</span><span class="o">.</span><span class="n">_mapping</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_mapping</span><span class="o">.</span><span class="n">copy</span><span class="p">()</span>
<span class="k">del</span> <span class="n">copy</span><span class="o">.</span><span class="n">_mapping</span><span class="p">[</span><span class="n">key</span><span class="p">]</span>
<span class="k">return</span> <span class="n">copy</span>
</pre></div>
</div>
<p>Every OS thread has a reference to the current <code class="docutils literal notranslate"><span class="pre">Context</span></code> object:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">PyThreadState</span><span class="p">:</span>
<span class="n">context</span><span class="p">:</span> <span class="n">Context</span>
</pre></div>
</div>
<p><code class="docutils literal notranslate"><span class="pre">contextvars.Context</span></code> is a wrapper around <code class="docutils literal notranslate"><span class="pre">_ContextData</span></code>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Context</span><span class="p">(</span><span class="n">collections</span><span class="o">.</span><span class="n">abc</span><span class="o">.</span><span class="n">Mapping</span><span class="p">):</span>
<span class="n">_data</span><span class="p">:</span> <span class="n">_ContextData</span>
<span class="n">_prev_context</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="n">Context</span><span class="p">]</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_data</span> <span class="o">=</span> <span class="n">_ContextData</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_prev_context</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="nb">callable</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_prev_context</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">RuntimeError</span><span class="p">(</span>
<span class="sa">f</span><span class="s1">&#39;cannot enter context: </span><span class="si">{</span><span class="bp">self</span><span class="si">}</span><span class="s1"> is already entered&#39;</span><span class="p">)</span>
<span class="n">ts</span><span class="p">:</span> <span class="n">PyThreadState</span> <span class="o">=</span> <span class="n">PyThreadState_Get</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_prev_context</span> <span class="o">=</span> <span class="n">ts</span><span class="o">.</span><span class="n">context</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">ts</span><span class="o">.</span><span class="n">context</span> <span class="o">=</span> <span class="bp">self</span>
<span class="k">return</span> <span class="nb">callable</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<span class="k">finally</span><span class="p">:</span>
<span class="n">ts</span><span class="o">.</span><span class="n">context</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_prev_context</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_prev_context</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">def</span> <span class="nf">copy</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">new</span> <span class="o">=</span> <span class="n">Context</span><span class="p">()</span>
<span class="n">new</span><span class="o">.</span><span class="n">_data</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_data</span>
<span class="k">return</span> <span class="n">new</span>
<span class="c1"># Implement abstract Mapping.__getitem__</span>
<span class="k">def</span> <span class="fm">__getitem__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">var</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_data</span><span class="p">[</span><span class="n">var</span><span class="p">]</span>
<span class="c1"># Implement abstract Mapping.__contains__</span>
<span class="k">def</span> <span class="fm">__contains__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">var</span><span class="p">):</span>
<span class="k">return</span> <span class="n">var</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">_data</span>
<span class="c1"># Implement abstract Mapping.__len__</span>
<span class="k">def</span> <span class="fm">__len__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_data</span><span class="p">)</span>
<span class="c1"># Implement abstract Mapping.__iter__</span>
<span class="k">def</span> <span class="fm">__iter__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">iter</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_data</span><span class="p">)</span>
<span class="c1"># The rest of the Mapping methods are implemented</span>
<span class="c1"># by collections.abc.Mapping.</span>
</pre></div>
</div>
<p><code class="docutils literal notranslate"><span class="pre">contextvars.copy_context()</span></code> is implemented as follows:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">copy_context</span><span class="p">():</span>
<span class="n">ts</span><span class="p">:</span> <span class="n">PyThreadState</span> <span class="o">=</span> <span class="n">PyThreadState_Get</span><span class="p">()</span>
<span class="k">return</span> <span class="n">ts</span><span class="o">.</span><span class="n">context</span><span class="o">.</span><span class="n">copy</span><span class="p">()</span>
</pre></div>
</div>
<p><code class="docutils literal notranslate"><span class="pre">contextvars.ContextVar</span></code> interacts with <code class="docutils literal notranslate"><span class="pre">PyThreadState.context</span></code>
directly:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">ContextVar</span><span class="p">:</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="n">_NO_DEFAULT</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_name</span> <span class="o">=</span> <span class="n">name</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_default</span> <span class="o">=</span> <span class="n">default</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">name</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_name</span>
<span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="n">_NO_DEFAULT</span><span class="p">):</span>
<span class="n">ts</span><span class="p">:</span> <span class="n">PyThreadState</span> <span class="o">=</span> <span class="n">PyThreadState_Get</span><span class="p">()</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="n">ts</span><span class="o">.</span><span class="n">context</span><span class="p">[</span><span class="bp">self</span><span class="p">]</span>
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
<span class="k">pass</span>
<span class="k">if</span> <span class="n">default</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">_NO_DEFAULT</span><span class="p">:</span>
<span class="k">return</span> <span class="n">default</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_default</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">_NO_DEFAULT</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_default</span>
<span class="k">raise</span> <span class="ne">LookupError</span>
<span class="k">def</span> <span class="nf">set</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="n">ts</span><span class="p">:</span> <span class="n">PyThreadState</span> <span class="o">=</span> <span class="n">PyThreadState_Get</span><span class="p">()</span>
<span class="n">data</span><span class="p">:</span> <span class="n">_ContextData</span> <span class="o">=</span> <span class="n">ts</span><span class="o">.</span><span class="n">context</span><span class="o">.</span><span class="n">_data</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">old_value</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="bp">self</span><span class="p">]</span>
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
<span class="n">old_value</span> <span class="o">=</span> <span class="n">Token</span><span class="o">.</span><span class="n">MISSING</span>
<span class="n">updated_data</span> <span class="o">=</span> <span class="n">data</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
<span class="n">ts</span><span class="o">.</span><span class="n">context</span><span class="o">.</span><span class="n">_data</span> <span class="o">=</span> <span class="n">updated_data</span>
<span class="k">return</span> <span class="n">Token</span><span class="p">(</span><span class="n">ts</span><span class="o">.</span><span class="n">context</span><span class="p">,</span> <span class="bp">self</span><span class="p">,</span> <span class="n">old_value</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">reset</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">token</span><span class="p">):</span>
<span class="k">if</span> <span class="n">token</span><span class="o">.</span><span class="n">_used</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">RuntimeError</span><span class="p">(</span><span class="s2">&quot;Token has already been used once&quot;</span><span class="p">)</span>
<span class="k">if</span> <span class="n">token</span><span class="o">.</span><span class="n">_var</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">self</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
<span class="s2">&quot;Token was created by a different ContextVar&quot;</span><span class="p">)</span>
<span class="n">ts</span><span class="p">:</span> <span class="n">PyThreadState</span> <span class="o">=</span> <span class="n">PyThreadState_Get</span><span class="p">()</span>
<span class="k">if</span> <span class="n">token</span><span class="o">.</span><span class="n">_context</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">ts</span><span class="o">.</span><span class="n">context</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
<span class="s2">&quot;Token was created in a different Context&quot;</span><span class="p">)</span>
<span class="k">if</span> <span class="n">token</span><span class="o">.</span><span class="n">_old_value</span> <span class="ow">is</span> <span class="n">Token</span><span class="o">.</span><span class="n">MISSING</span><span class="p">:</span>
<span class="n">ts</span><span class="o">.</span><span class="n">context</span><span class="o">.</span><span class="n">_data</span> <span class="o">=</span> <span class="n">ts</span><span class="o">.</span><span class="n">context</span><span class="o">.</span><span class="n">_data</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="n">token</span><span class="o">.</span><span class="n">_var</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">ts</span><span class="o">.</span><span class="n">context</span><span class="o">.</span><span class="n">_data</span> <span class="o">=</span> <span class="n">ts</span><span class="o">.</span><span class="n">context</span><span class="o">.</span><span class="n">_data</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="n">token</span><span class="o">.</span><span class="n">_var</span><span class="p">,</span>
<span class="n">token</span><span class="o">.</span><span class="n">_old_value</span><span class="p">)</span>
<span class="n">token</span><span class="o">.</span><span class="n">_used</span> <span class="o">=</span> <span class="kc">True</span>
</pre></div>
</div>
<p>Note that the in the reference implementation, <code class="docutils literal notranslate"><span class="pre">ContextVar.get()</span></code>
has an internal cache for the most recent value, which allows to
bypass a hash lookup. This is similar to the optimization the
<code class="docutils literal notranslate"><span class="pre">decimal</span></code> module implements to retrieve its context from
<code class="docutils literal notranslate"><span class="pre">PyThreadState_GetDict()</span></code>. See <a class="pep reference internal" href="../pep-0550/" title="PEP 550 Execution Context">PEP 550</a> which explains the
implementation of the cache in great detail.</p>
<p>The <code class="docutils literal notranslate"><span class="pre">Token</span></code> class is implemented as follows:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Token</span><span class="p">:</span>
<span class="n">MISSING</span> <span class="o">=</span> <span class="nb">object</span><span class="p">()</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">context</span><span class="p">,</span> <span class="n">var</span><span class="p">,</span> <span class="n">old_value</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_context</span> <span class="o">=</span> <span class="n">context</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_var</span> <span class="o">=</span> <span class="n">var</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_old_value</span> <span class="o">=</span> <span class="n">old_value</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_used</span> <span class="o">=</span> <span class="kc">False</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">var</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_var</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">old_value</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_old_value</span>
</pre></div>
</div>
</section>
<section id="summary-of-the-new-apis">
<h2><a class="toc-backref" href="#summary-of-the-new-apis" role="doc-backlink">Summary of the New APIs</a></h2>
<section id="python-api">
<h3><a class="toc-backref" href="#python-api" role="doc-backlink">Python API</a></h3>
<ol class="arabic simple">
<li>A new <code class="docutils literal notranslate"><span class="pre">contextvars</span></code> module with <code class="docutils literal notranslate"><span class="pre">ContextVar</span></code>, <code class="docutils literal notranslate"><span class="pre">Context</span></code>,
and <code class="docutils literal notranslate"><span class="pre">Token</span></code> classes, and a <code class="docutils literal notranslate"><span class="pre">copy_context()</span></code> function.</li>
<li><code class="docutils literal notranslate"><span class="pre">asyncio.Loop.call_at()</span></code>, <code class="docutils literal notranslate"><span class="pre">asyncio.Loop.call_later()</span></code>,
<code class="docutils literal notranslate"><span class="pre">asyncio.Loop.call_soon()</span></code>, and
<code class="docutils literal notranslate"><span class="pre">asyncio.Future.add_done_callback()</span></code> run callback functions in
the context they were called in. A new <em>context</em> keyword-only
parameter can be used to specify a custom context.</li>
<li><code class="docutils literal notranslate"><span class="pre">asyncio.Task</span></code> is modified internally to maintain its own
context.</li>
</ol>
</section>
<section id="c-api">
<h3><a class="toc-backref" href="#c-api" role="doc-backlink">C API</a></h3>
<ol class="arabic">
<li><code class="docutils literal notranslate"><span class="pre">PyObject</span> <span class="pre">*</span> <span class="pre">PyContextVar_New(char</span> <span class="pre">*name,</span> <span class="pre">PyObject</span> <span class="pre">*default)</span></code>:
create a <code class="docutils literal notranslate"><span class="pre">ContextVar</span></code> object. The <em>default</em> argument can be
<code class="docutils literal notranslate"><span class="pre">NULL</span></code>, which means that the variable has no default value.</li>
<li><code class="docutils literal notranslate"><span class="pre">int</span> <span class="pre">PyContextVar_Get(PyObject</span> <span class="pre">*,</span> <span class="pre">PyObject</span> <span class="pre">*default_value,</span> <span class="pre">PyObject</span> <span class="pre">**value)</span></code>:
return <code class="docutils literal notranslate"><span class="pre">-1</span></code> if an error occurs during the lookup, <code class="docutils literal notranslate"><span class="pre">0</span></code> otherwise.
If a value for the context variable is found, it will be set to the
<code class="docutils literal notranslate"><span class="pre">value</span></code> pointer. Otherwise, <code class="docutils literal notranslate"><span class="pre">value</span></code> will be set to
<code class="docutils literal notranslate"><span class="pre">default_value</span></code> when it is not <code class="docutils literal notranslate"><span class="pre">NULL</span></code>. If <code class="docutils literal notranslate"><span class="pre">default_value</span></code> is
<code class="docutils literal notranslate"><span class="pre">NULL</span></code>, <code class="docutils literal notranslate"><span class="pre">value</span></code> will be set to the default value of the
variable, which can be <code class="docutils literal notranslate"><span class="pre">NULL</span></code> too. <code class="docutils literal notranslate"><span class="pre">value</span></code> is always a new
reference.</li>
<li><code class="docutils literal notranslate"><span class="pre">PyObject</span> <span class="pre">*</span> <span class="pre">PyContextVar_Set(PyObject</span> <span class="pre">*,</span> <span class="pre">PyObject</span> <span class="pre">*)</span></code>:
set the value of the variable in the current context.</li>
<li><code class="docutils literal notranslate"><span class="pre">PyContextVar_Reset(PyObject</span> <span class="pre">*,</span> <span class="pre">PyObject</span> <span class="pre">*)</span></code>:
reset the value of the context variable.</li>
<li><code class="docutils literal notranslate"><span class="pre">PyObject</span> <span class="pre">*</span> <span class="pre">PyContext_New()</span></code>: create a new empty context.</li>
<li><code class="docutils literal notranslate"><span class="pre">PyObject</span> <span class="pre">*</span> <span class="pre">PyContext_Copy(PyObject</span> <span class="pre">*)</span></code>: return a shallow
copy of the passed context object.</li>
<li><code class="docutils literal notranslate"><span class="pre">PyObject</span> <span class="pre">*</span> <span class="pre">PyContext_CopyCurrent()</span></code>: get a copy of the current
context.</li>
<li><code class="docutils literal notranslate"><span class="pre">int</span> <span class="pre">PyContext_Enter(PyObject</span> <span class="pre">*)</span></code> and
<code class="docutils literal notranslate"><span class="pre">int</span> <span class="pre">PyContext_Exit(PyObject</span> <span class="pre">*)</span></code> allow to set and restore
the context for the current OS thread. It is required to always
restore the previous context:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">PyObject</span> <span class="o">*</span><span class="n">old_ctx</span> <span class="o">=</span> <span class="n">PyContext_Copy</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">old_ctx</span> <span class="o">==</span> <span class="n">NULL</span><span class="p">)</span> <span class="n">goto</span> <span class="n">error</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">PyContext_Enter</span><span class="p">(</span><span class="n">new_ctx</span><span class="p">))</span> <span class="n">goto</span> <span class="n">error</span><span class="p">;</span>
<span class="o">//</span> <span class="n">run</span> <span class="n">some</span> <span class="n">code</span>
<span class="k">if</span> <span class="p">(</span><span class="n">PyContext_Exit</span><span class="p">(</span><span class="n">old_ctx</span><span class="p">))</span> <span class="n">goto</span> <span class="n">error</span><span class="p">;</span>
</pre></div>
</div>
</li>
</ol>
</section>
</section>
<section id="rejected-ideas">
<h2><a class="toc-backref" href="#rejected-ideas" role="doc-backlink">Rejected Ideas</a></h2>
<section id="replicating-threading-local-interface">
<h3><a class="toc-backref" href="#replicating-threading-local-interface" role="doc-backlink">Replicating threading.local() interface</a></h3>
<p>Please refer to <a class="pep reference internal" href="../pep-0550/" title="PEP 550 Execution Context">PEP 550</a> where this topic is covered in detail: <a class="footnote-reference brackets" href="#id8" id="id3">[2]</a>.</p>
</section>
<section id="replacing-token-with-contextvar-unset">
<h3><a class="toc-backref" href="#replacing-token-with-contextvar-unset" role="doc-backlink">Replacing Token with ContextVar.unset()</a></h3>
<p>The Token API allows to get around having a <code class="docutils literal notranslate"><span class="pre">ContextVar.unset()</span></code>
method, which is incompatible with chained contexts design of
<a class="pep reference internal" href="../pep-0550/" title="PEP 550 Execution Context">PEP 550</a>. Future compatibility with <a class="pep reference internal" href="../pep-0550/" title="PEP 550 Execution Context">PEP 550</a> is desired
in case there is demand to support context variables in generators
and asynchronous generators.</p>
<p>The Token API also offers better usability: the user does not have
to special-case absence of a value. Compare:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">token</span> <span class="o">=</span> <span class="n">cv</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="n">new_value</span><span class="p">)</span>
<span class="k">try</span><span class="p">:</span>
<span class="c1"># cv.get() is new_value</span>
<span class="k">finally</span><span class="p">:</span>
<span class="n">cv</span><span class="o">.</span><span class="n">reset</span><span class="p">(</span><span class="n">token</span><span class="p">)</span>
</pre></div>
</div>
<p>with:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">_deleted</span> <span class="o">=</span> <span class="nb">object</span><span class="p">()</span>
<span class="n">old</span> <span class="o">=</span> <span class="n">cv</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="n">_deleted</span><span class="p">)</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">cv</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="n">blah</span><span class="p">)</span>
<span class="c1"># code</span>
<span class="k">finally</span><span class="p">:</span>
<span class="k">if</span> <span class="n">old</span> <span class="ow">is</span> <span class="n">_deleted</span><span class="p">:</span>
<span class="n">cv</span><span class="o">.</span><span class="n">unset</span><span class="p">()</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">cv</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="n">old</span><span class="p">)</span>
</pre></div>
</div>
</section>
<section id="having-token-reset-instead-of-contextvar-reset">
<h3><a class="toc-backref" href="#having-token-reset-instead-of-contextvar-reset" role="doc-backlink">Having Token.reset() instead of ContextVar.reset()</a></h3>
<p>Nathaniel Smith suggested to implement the <code class="docutils literal notranslate"><span class="pre">ContextVar.reset()</span></code>
method directly on the <code class="docutils literal notranslate"><span class="pre">Token</span></code> class, so instead of:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">token</span> <span class="o">=</span> <span class="n">var</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
<span class="c1"># ...</span>
<span class="n">var</span><span class="o">.</span><span class="n">reset</span><span class="p">(</span><span class="n">token</span><span class="p">)</span>
</pre></div>
</div>
<p>we would write:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">token</span> <span class="o">=</span> <span class="n">var</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
<span class="c1"># ...</span>
<span class="n">token</span><span class="o">.</span><span class="n">reset</span><span class="p">()</span>
</pre></div>
</div>
<p>Having <code class="docutils literal notranslate"><span class="pre">Token.reset()</span></code> would make it impossible for a user to
attempt to reset a variable with a token object created by another
variable.</p>
<p>This proposal was rejected for the reason of <code class="docutils literal notranslate"><span class="pre">ContextVar.reset()</span></code>
being clearer to the human reader of the code which variable is
being reset.</p>
</section>
<section id="making-context-objects-picklable">
<h3><a class="toc-backref" href="#making-context-objects-picklable" role="doc-backlink">Making Context objects picklable</a></h3>
<p>Proposed by Antoine Pitrou, this could enable transparent
cross-process use of <code class="docutils literal notranslate"><span class="pre">Context</span></code> objects, so the
<a class="reference internal" href="#offloading-execution-to-other-threads">Offloading execution to other threads</a> example would work with
a <code class="docutils literal notranslate"><span class="pre">ProcessPoolExecutor</span></code> too.</p>
<p>Enabling this is problematic because of the following reasons:</p>
<ol class="arabic simple">
<li><code class="docutils literal notranslate"><span class="pre">ContextVar</span></code> objects do not have <code class="docutils literal notranslate"><span class="pre">__module__</span></code> and
<code class="docutils literal notranslate"><span class="pre">__qualname__</span></code> attributes, making straightforward pickling
of <code class="docutils literal notranslate"><span class="pre">Context</span></code> objects impossible. This is solvable by modifying
the API to either auto detect the module where a context variable
is defined, or by adding a new keyword-only “module” parameter
to <code class="docutils literal notranslate"><span class="pre">ContextVar</span></code> constructor.</li>
<li>Not all context variables refer to picklable objects. Making a
<code class="docutils literal notranslate"><span class="pre">ContextVar</span></code> picklable must be an opt-in.</li>
</ol>
<p>Given the time frame of the Python 3.7 release schedule it was decided
to defer this proposal to Python 3.8.</p>
</section>
<section id="making-context-a-mutablemapping">
<h3><a class="toc-backref" href="#making-context-a-mutablemapping" role="doc-backlink">Making Context a MutableMapping</a></h3>
<p>Making the <code class="docutils literal notranslate"><span class="pre">Context</span></code> class implement the <code class="docutils literal notranslate"><span class="pre">abc.MutableMapping</span></code>
interface would mean that it is possible to set and unset variables
using <code class="docutils literal notranslate"><span class="pre">Context[var]</span> <span class="pre">=</span> <span class="pre">value</span></code> and <code class="docutils literal notranslate"><span class="pre">del</span> <span class="pre">Context[var]</span></code> operations.</p>
<p>This proposal was deferred to Python 3.8+ because of the following:</p>
<ol class="arabic">
<li>If in Python 3.8 it is decided that generators should support
context variables (see <a class="pep reference internal" href="../pep-0550/" title="PEP 550 Execution Context">PEP 550</a> and <a class="pep reference internal" href="../pep-0568/" title="PEP 568 Generator-sensitivity for Context Variables">PEP 568</a>), then <code class="docutils literal notranslate"><span class="pre">Context</span></code>
would be transformed into a chain-map of context variables mappings
(as every generator would have its own mapping). That would make
mutation operations like <code class="docutils literal notranslate"><span class="pre">Context.__delitem__</span></code> confusing, as
they would operate only on the topmost mapping of the chain.</li>
<li>Having a single way of mutating the context
(<code class="docutils literal notranslate"><span class="pre">ContextVar.set()</span></code> and <code class="docutils literal notranslate"><span class="pre">ContextVar.reset()</span></code> methods) makes
the API more straightforward.<p>For example, it would be non-obvious why the below code fragment
does not work as expected:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">var</span> <span class="o">=</span> <span class="n">ContextVar</span><span class="p">(</span><span class="s1">&#39;var&#39;</span><span class="p">)</span>
<span class="n">ctx</span> <span class="o">=</span> <span class="n">copy_context</span><span class="p">()</span>
<span class="n">ctx</span><span class="p">[</span><span class="n">var</span><span class="p">]</span> <span class="o">=</span> <span class="s1">&#39;value&#39;</span>
<span class="nb">print</span><span class="p">(</span><span class="n">ctx</span><span class="p">[</span><span class="n">var</span><span class="p">])</span> <span class="c1"># Prints &#39;value&#39;</span>
<span class="nb">print</span><span class="p">(</span><span class="n">var</span><span class="o">.</span><span class="n">get</span><span class="p">())</span> <span class="c1"># Raises a LookupError</span>
</pre></div>
</div>
<p>While the following code would work:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">ctx</span> <span class="o">=</span> <span class="n">copy_context</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">func</span><span class="p">():</span>
<span class="n">ctx</span><span class="p">[</span><span class="n">var</span><span class="p">]</span> <span class="o">=</span> <span class="s1">&#39;value&#39;</span>
<span class="c1"># Contrary to the previous example, this would work</span>
<span class="c1"># because &#39;func()&#39; is running within &#39;ctx&#39;.</span>
<span class="nb">print</span><span class="p">(</span><span class="n">ctx</span><span class="p">[</span><span class="n">var</span><span class="p">])</span>
<span class="nb">print</span><span class="p">(</span><span class="n">var</span><span class="o">.</span><span class="n">get</span><span class="p">())</span>
<span class="n">ctx</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">func</span><span class="p">)</span>
</pre></div>
</div>
</li>
<li>If <code class="docutils literal notranslate"><span class="pre">Context</span></code> was mutable it would mean that context variables
could be mutated separately (or concurrently) from the code that
runs within the context. That would be similar to obtaining a
reference to a running Python frame object and modifying its
<code class="docutils literal notranslate"><span class="pre">f_locals</span></code> from another OS thread. Having one single way to
assign values to context variables makes contexts conceptually
simpler and more predictable, while keeping the door open for
future performance optimizations.</li>
</ol>
</section>
<section id="having-initial-values-for-contextvars">
<h3><a class="toc-backref" href="#having-initial-values-for-contextvars" role="doc-backlink">Having initial values for ContextVars</a></h3>
<p>Nathaniel Smith proposed to have a required <code class="docutils literal notranslate"><span class="pre">initial_value</span></code>
keyword-only argument for the <code class="docutils literal notranslate"><span class="pre">ContextVar</span></code> constructor.</p>
<p>The main argument against this proposal is that for some types
there is simply no sensible “initial value” except <code class="docutils literal notranslate"><span class="pre">None</span></code>.
E.g. consider a web framework that stores the current HTTP
request object in a context variable. With the current semantics
it is possible to create a context variable without a default value:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Framework:</span>
<span class="n">current_request</span><span class="p">:</span> <span class="n">ContextVar</span><span class="p">[</span><span class="n">Request</span><span class="p">]</span> <span class="o">=</span> \
<span class="n">ContextVar</span><span class="p">(</span><span class="s1">&#39;current_request&#39;</span><span class="p">)</span>
<span class="c1"># Later, while handling an HTTP request:</span>
<span class="n">request</span><span class="p">:</span> <span class="n">Request</span> <span class="o">=</span> <span class="n">current_request</span><span class="o">.</span><span class="n">get</span><span class="p">()</span>
<span class="c1"># Work with the &#39;request&#39; object:</span>
<span class="k">return</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span>
</pre></div>
</div>
<p>Note that in the above example there is no need to check if
<code class="docutils literal notranslate"><span class="pre">request</span></code> is <code class="docutils literal notranslate"><span class="pre">None</span></code>. It is simply expected that the framework
always sets the <code class="docutils literal notranslate"><span class="pre">current_request</span></code> variable, or it is a bug (in
which case <code class="docutils literal notranslate"><span class="pre">current_request.get()</span></code> would raise a <code class="docutils literal notranslate"><span class="pre">LookupError</span></code>).</p>
<p>If, however, we had a required initial value, we would have
to guard against <code class="docutils literal notranslate"><span class="pre">None</span></code> values explicitly:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Framework:</span>
<span class="n">current_request</span><span class="p">:</span> <span class="n">ContextVar</span><span class="p">[</span><span class="n">Optional</span><span class="p">[</span><span class="n">Request</span><span class="p">]]</span> <span class="o">=</span> \
<span class="n">ContextVar</span><span class="p">(</span><span class="s1">&#39;current_request&#39;</span><span class="p">,</span> <span class="n">initial_value</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span>
<span class="c1"># Later, while handling an HTTP request:</span>
<span class="n">request</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="n">Request</span><span class="p">]</span> <span class="o">=</span> <span class="n">current_request</span><span class="o">.</span><span class="n">get</span><span class="p">()</span>
<span class="c1"># Check if the current request object was set:</span>
<span class="k">if</span> <span class="n">request</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">RuntimeError</span>
<span class="c1"># Work with the &#39;request&#39; object:</span>
<span class="k">return</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span>
</pre></div>
</div>
<p>Moreover, we can loosely compare context variables to regular
Python variables and to <code class="docutils literal notranslate"><span class="pre">threading.local()</span></code> objects. Both
of them raise errors on failed lookups (<code class="docutils literal notranslate"><span class="pre">NameError</span></code> and
<code class="docutils literal notranslate"><span class="pre">AttributeError</span></code> respectively).</p>
</section>
</section>
<section id="backwards-compatibility">
<h2><a class="toc-backref" href="#backwards-compatibility" role="doc-backlink">Backwards Compatibility</a></h2>
<p>This proposal preserves 100% backwards compatibility.</p>
<p>Libraries that use <code class="docutils literal notranslate"><span class="pre">threading.local()</span></code> to store context-related
values, currently work correctly only for synchronous code. Switching
them to use the proposed API will keep their behavior for synchronous
code unmodified, but will automatically enable support for
asynchronous code.</p>
</section>
<section id="examples">
<h2><a class="toc-backref" href="#examples" role="doc-backlink">Examples</a></h2>
<section id="converting-code-that-uses-threading-local">
<h3><a class="toc-backref" href="#converting-code-that-uses-threading-local" role="doc-backlink">Converting code that uses threading.local()</a></h3>
<p>A typical code fragment that uses <code class="docutils literal notranslate"><span class="pre">threading.local()</span></code> usually
looks like the following:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">PrecisionStorage</span><span class="p">(</span><span class="n">threading</span><span class="o">.</span><span class="n">local</span><span class="p">):</span>
<span class="c1"># Subclass threading.local to specify a default value.</span>
<span class="n">value</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="n">precision</span> <span class="o">=</span> <span class="n">PrecisionStorage</span><span class="p">()</span>
<span class="c1"># To set a new precision:</span>
<span class="n">precision</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="mf">0.5</span>
<span class="c1"># To read the current precision:</span>
<span class="nb">print</span><span class="p">(</span><span class="n">precision</span><span class="o">.</span><span class="n">value</span><span class="p">)</span>
</pre></div>
</div>
<p>Such code can be converted to use the <code class="docutils literal notranslate"><span class="pre">contextvars</span></code> module:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">precision</span> <span class="o">=</span> <span class="n">contextvars</span><span class="o">.</span><span class="n">ContextVar</span><span class="p">(</span><span class="s1">&#39;precision&#39;</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="mf">0.0</span><span class="p">)</span>
<span class="c1"># To set a new precision:</span>
<span class="n">precision</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="mf">0.5</span><span class="p">)</span>
<span class="c1"># To read the current precision:</span>
<span class="nb">print</span><span class="p">(</span><span class="n">precision</span><span class="o">.</span><span class="n">get</span><span class="p">())</span>
</pre></div>
</div>
</section>
<section id="offloading-execution-to-other-threads">
<h3><a class="toc-backref" href="#offloading-execution-to-other-threads" role="doc-backlink">Offloading execution to other threads</a></h3>
<p>It is possible to run code in a separate OS thread using a copy
of the current thread context:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">executor</span> <span class="o">=</span> <span class="n">ThreadPoolExecutor</span><span class="p">()</span>
<span class="n">current_context</span> <span class="o">=</span> <span class="n">contextvars</span><span class="o">.</span><span class="n">copy_context</span><span class="p">()</span>
<span class="n">executor</span><span class="o">.</span><span class="n">submit</span><span class="p">(</span><span class="n">current_context</span><span class="o">.</span><span class="n">run</span><span class="p">,</span> <span class="n">some_function</span><span class="p">)</span>
</pre></div>
</div>
</section>
</section>
<section id="reference-implementation">
<h2><a class="toc-backref" href="#reference-implementation" role="doc-backlink">Reference Implementation</a></h2>
<p>The reference implementation can be found here: <a class="footnote-reference brackets" href="#id9" id="id4">[3]</a>.
See also issue 32436 <a class="footnote-reference brackets" href="#id10" id="id5">[4]</a>.</p>
</section>
<section id="acceptance">
<h2><a class="toc-backref" href="#acceptance" role="doc-backlink">Acceptance</a></h2>
<p><a class="pep reference internal" href="../pep-0567/" title="PEP 567 Context Variables">PEP 567</a> was accepted by Guido on Monday, January 22, 2018 <a class="footnote-reference brackets" href="#id11" id="id6">[5]</a>.
The reference implementation was merged on the same day.</p>
</section>
<section id="references">
<h2><a class="toc-backref" href="#references" role="doc-backlink">References</a></h2>
<aside class="footnote-list brackets">
<aside class="footnote brackets" id="id7" role="doc-footnote">
<dt class="label" id="id7">[<a href="#id2">1</a>]</dt>
<dd><a class="pep reference internal" href="../pep-0550/#appendix-hamt-performance-analysis" title="PEP 550 Execution Context § Appendix: HAMT Performance Analysis">PEP 550</a></aside>
<aside class="footnote brackets" id="id8" role="doc-footnote">
<dt class="label" id="id8">[<a href="#id3">2</a>]</dt>
<dd><a class="pep reference internal" href="../pep-0550/#replication-of-threading-local-interface" title="PEP 550 Execution Context § Replication of threading.local() interface">PEP 550</a></aside>
<aside class="footnote brackets" id="id9" role="doc-footnote">
<dt class="label" id="id9">[<a href="#id4">3</a>]</dt>
<dd><a class="reference external" href="https://github.com/python/cpython/pull/5027">https://github.com/python/cpython/pull/5027</a></aside>
<aside class="footnote brackets" id="id10" role="doc-footnote">
<dt class="label" id="id10">[<a href="#id5">4</a>]</dt>
<dd><a class="reference external" href="https://bugs.python.org/issue32436">https://bugs.python.org/issue32436</a></aside>
<aside class="footnote brackets" id="id11" role="doc-footnote">
<dt class="label" id="id11">[<a href="#id6">5</a>]</dt>
<dd><a class="reference external" href="https://mail.python.org/pipermail/python-dev/2018-January/151878.html">https://mail.python.org/pipermail/python-dev/2018-January/151878.html</a></aside>
<aside class="footnote brackets" id="id12" role="doc-footnote">
<dt class="label" id="id12">[<a href="#id1">6</a>]</dt>
<dd><a class="reference external" href="https://bugs.python.org/issue34762">https://bugs.python.org/issue34762</a></aside>
</aside>
</section>
<section id="acknowledgments">
<h2><a class="toc-backref" href="#acknowledgments" role="doc-backlink">Acknowledgments</a></h2>
<p>I thank Guido van Rossum, Nathaniel Smith, Victor Stinner,
Elvis Pranskevichus, Alyssa Coghlan, Antoine Pitrou, INADA Naoki,
Paul Moore, Eric Snow, Greg Ewing, and many others for their feedback,
ideas, edits, criticism, code reviews, and discussions around
this PEP.</p>
</section>
<section id="copyright">
<h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2>
<p>This document has been placed in the public domain.</p>
</section>
</section>
<hr class="docutils" />
<p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0567.rst">https://github.com/python/peps/blob/main/peps/pep-0567.rst</a></p>
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0567.rst">2023-10-11 12:05:51 GMT</a></p>
</article>
<nav id="pep-sidebar">
<h2>Contents</h2>
<ul>
<li><a class="reference internal" href="#abstract">Abstract</a></li>
<li><a class="reference internal" href="#api-design-and-implementation-revisions">API Design and Implementation Revisions</a></li>
<li><a class="reference internal" href="#rationale">Rationale</a></li>
<li><a class="reference internal" href="#introduction">Introduction</a></li>
<li><a class="reference internal" href="#specification">Specification</a><ul>
<li><a class="reference internal" href="#contextvars-contextvar">contextvars.ContextVar</a></li>
<li><a class="reference internal" href="#contextvars-token">contextvars.Token</a></li>
<li><a class="reference internal" href="#contextvars-context">contextvars.Context</a></li>
<li><a class="reference internal" href="#asyncio">asyncio</a></li>
</ul>
</li>
<li><a class="reference internal" href="#implementation">Implementation</a></li>
<li><a class="reference internal" href="#summary-of-the-new-apis">Summary of the New APIs</a><ul>
<li><a class="reference internal" href="#python-api">Python API</a></li>
<li><a class="reference internal" href="#c-api">C API</a></li>
</ul>
</li>
<li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a><ul>
<li><a class="reference internal" href="#replicating-threading-local-interface">Replicating threading.local() interface</a></li>
<li><a class="reference internal" href="#replacing-token-with-contextvar-unset">Replacing Token with ContextVar.unset()</a></li>
<li><a class="reference internal" href="#having-token-reset-instead-of-contextvar-reset">Having Token.reset() instead of ContextVar.reset()</a></li>
<li><a class="reference internal" href="#making-context-objects-picklable">Making Context objects picklable</a></li>
<li><a class="reference internal" href="#making-context-a-mutablemapping">Making Context a MutableMapping</a></li>
<li><a class="reference internal" href="#having-initial-values-for-contextvars">Having initial values for ContextVars</a></li>
</ul>
</li>
<li><a class="reference internal" href="#backwards-compatibility">Backwards Compatibility</a></li>
<li><a class="reference internal" href="#examples">Examples</a><ul>
<li><a class="reference internal" href="#converting-code-that-uses-threading-local">Converting code that uses threading.local()</a></li>
<li><a class="reference internal" href="#offloading-execution-to-other-threads">Offloading execution to other threads</a></li>
</ul>
</li>
<li><a class="reference internal" href="#reference-implementation">Reference Implementation</a></li>
<li><a class="reference internal" href="#acceptance">Acceptance</a></li>
<li><a class="reference internal" href="#references">References</a></li>
<li><a class="reference internal" href="#acknowledgments">Acknowledgments</a></li>
<li><a class="reference internal" href="#copyright">Copyright</a></li>
</ul>
<br>
<a id="source" href="https://github.com/python/peps/blob/main/peps/pep-0567.rst">Page Source (GitHub)</a>
</nav>
</section>
<script src="../_static/colour_scheme.js"></script>
<script src="../_static/wrap_tables.js"></script>
<script src="../_static/sticky_banner.js"></script>
</body>
</html>