CINXE.COM

PEP 649 – Deferred Evaluation Of Annotations Using Descriptors | peps.python.org

<!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 649 – Deferred Evaluation Of Annotations Using Descriptors | peps.python.org</title> <link rel="shortcut icon" href="../_static/py.png"> <link rel="canonical" href="https://peps.python.org/pep-0649/"> <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 649 – Deferred Evaluation Of Annotations Using Descriptors | peps.python.org'> <meta property="og:description" content="Annotations are a Python technology that allows expressing type information and other metadata about Python functions, classes, and modules. But Python’s original semantics for annotations required them to be eagerly evaluated, at the time the annotate..."> <meta property="og:type" content="website"> <meta property="og:url" content="https://peps.python.org/pep-0649/"> <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="Annotations are a Python technology that allows expressing type information and other metadata about Python functions, classes, and modules. But Python’s original semantics for annotations required them to be eagerly evaluated, at the time the annotate..."> <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 649</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 649 – Deferred Evaluation Of Annotations Using Descriptors</h1> <dl class="rfc2822 field-list simple"> <dt class="field-odd">Author<span class="colon">:</span></dt> <dd class="field-odd">Larry Hastings &lt;larry&#32;&#97;t&#32;hastings.org&gt;</dd> <dt class="field-even">Discussions-To<span class="colon">:</span></dt> <dd class="field-even"><a class="reference external" href="https://discuss.python.org/t/pep-649-deferred-evaluation-of-annotations-tentatively-accepted/21331/">Discourse thread</a></dd> <dt class="field-odd">Status<span class="colon">:</span></dt> <dd class="field-odd"><abbr title="Normative proposal accepted for implementation">Accepted</abbr></dd> <dt class="field-even">Type<span class="colon">:</span></dt> <dd class="field-even"><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-odd">Topic<span class="colon">:</span></dt> <dd class="field-odd"><a class="reference external" href="../topic/typing/">Typing</a></dd> <dt class="field-even">Created<span class="colon">:</span></dt> <dd class="field-even">11-Jan-2021</dd> <dt class="field-odd">Python-Version<span class="colon">:</span></dt> <dd class="field-odd">3.14</dd> <dt class="field-even">Post-History<span class="colon">:</span></dt> <dd class="field-even"><a class="reference external" href="https://mail.python.org/archives/list/python-dev&#64;python.org/thread/5QMMCRF4HTRRNJV56CGHVI5GRHVBDGQO/" title="Python-Dev thread">11-Jan-2021</a>, <a class="reference external" href="https://mail.python.org/archives/list/python-dev&#64;python.org/thread/QSASX6PZ3LIIFIANHQQFS752BJYFUFPY/" title="Python-Dev thread">12-Apr-2021</a>, <a class="reference external" href="https://mail.python.org/archives/list/python-dev&#64;python.org/thread/WUZGTGE43T7XV3EUGT6AN2N52OD3U7AE/" title="Python-Dev thread">18-Apr-2021</a>, <a class="reference external" href="https://mail.python.org/archives/list/python-dev&#64;python.org/thread/2MEOWHCVDLPABOBLYUGRXVOOOBYOLLU6/" title="Python-Dev thread">09-Aug-2021</a>, <a class="reference external" href="https://mail.python.org/archives/list/python-dev&#64;python.org/thread/SZLWVYV2HPLU6AH7DOUD7DWFUGBJGQAY/" title="Python-Dev thread">20-Oct-2021</a>, <a class="reference external" href="https://discuss.python.org/t/type-annotations-pep-649-and-pep-563/11363" title="Discourse thread">20-Oct-2021</a>, <a class="reference external" href="https://mail.python.org/archives/list/python-dev&#64;python.org/thread/VIZEBX5EYMSYIJNDBF6DMUMZOCWHARSO/" title="Python-Dev thread">17-Nov-2021</a>, <a class="reference external" href="https://discuss.python.org/t/finding-edge-cases-for-peps-484-563-and-649-type-annotations/14314" title="Discourse thread">15-Mar-2022</a>, <a class="reference external" href="https://discuss.python.org/t/pep-649-deferred-evaluation-of-annotations-tentatively-accepted/21331" title="Discourse thread">23-Nov-2022</a>, <a class="reference external" href="https://discuss.python.org/t/two-polls-on-how-to-revise-pep-649/23628" title="Discourse thread">07-Feb-2023</a>, <a class="reference external" href="https://discuss.python.org/t/a-massive-pep-649-update-with-some-major-course-corrections/25672" title="Discourse thread">11-Apr-2023</a></dd> <dt class="field-odd">Replaces<span class="colon">:</span></dt> <dd class="field-odd"><a class="reference external" href="../pep-0563/">563</a></dd> <dt class="field-even">Resolution<span class="colon">:</span></dt> <dd class="field-even"><a class="reference external" href="https://discuss.python.org/t/pep-649-deferred-evaluation-of-annotations-tentatively-accepted/21331/43">08-May-2023</a></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="#overview">Overview</a><ul> <li><a class="reference internal" href="#comparison-of-annotation-semantics">Comparison Of Annotation Semantics</a></li> <li><a class="reference internal" href="#mistaken-rejection-of-this-approach-in-november-2017">Mistaken Rejection Of This Approach In November 2017</a></li> </ul> </li> <li><a class="reference internal" href="#motivation">Motivation</a><ul> <li><a class="reference internal" href="#a-history-of-annotations">A History Of Annotations</a></li> <li><a class="reference internal" href="#the-current-state-of-annotation-use-cases">The Current State Of Annotation Use Cases</a><ul> <li><a class="reference internal" href="#static-typing-users">Static typing users</a></li> <li><a class="reference internal" href="#runtime-annotation-users">Runtime annotation users</a></li> <li><a class="reference internal" href="#wrappers">Wrappers</a></li> <li><a class="reference internal" href="#documentation">Documentation</a></li> </ul> </li> <li><a class="reference internal" href="#motivation-for-this-pep">Motivation For This PEP</a></li> </ul> </li> <li><a class="reference internal" href="#implementation">Implementation</a><ul> <li><a class="reference internal" href="#observed-semantics-for-annotations-expressions">Observed semantics for annotations expressions</a></li> <li><a class="reference internal" href="#annotate-and-annotations">__annotate__ and __annotations__</a></li> <li><a class="reference internal" href="#changes-to-allowable-annotations-syntax">Changes to allowable annotations syntax</a></li> <li><a class="reference internal" href="#changes-to-inspect-get-annotations-and-typing-get-type-hints">Changes to <code class="docutils literal notranslate"><span class="pre">inspect.get_annotations</span></code> and <code class="docutils literal notranslate"><span class="pre">typing.get_type_hints</span></code></a></li> <li><a class="reference internal" href="#the-stringizer-and-the-fake-globals-environment">The <code class="docutils literal notranslate"><span class="pre">stringizer</span></code> and the <code class="docutils literal notranslate"><span class="pre">fake</span> <span class="pre">globals</span></code> environment</a></li> <li><a class="reference internal" href="#compiler-generated-annotate-functions">Compiler-generated <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> functions</a></li> <li><a class="reference internal" href="#third-party-annotate-functions">Third-party <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> functions</a></li> <li><a class="reference internal" href="#pseudocode">Pseudocode</a></li> <li><a class="reference internal" href="#other-modifications-to-the-python-runtime">Other modifications to the Python runtime</a></li> <li><a class="reference internal" href="#interactive-repl-shell">Interactive REPL Shell</a></li> <li><a class="reference internal" href="#annotations-on-local-variables-inside-functions">Annotations On Local Variables Inside Functions</a></li> <li><a class="reference internal" href="#prototype">Prototype</a></li> <li><a class="reference internal" href="#performance-comparison">Performance Comparison</a></li> </ul> </li> <li><a class="reference internal" href="#backwards-compatibility">Backwards Compatibility</a><ul> <li><a class="reference internal" href="#backwards-compatibility-with-stock-semantics">Backwards Compatibility With Stock Semantics</a></li> <li><a class="reference internal" href="#backwards-compatibility-with-pep-563-semantics">Backwards Compatibility With PEP 563 Semantics</a></li> </ul> </li> <li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a><ul> <li><a class="reference internal" href="#just-store-the-strings">“Just store the strings”</a></li> </ul> </li> <li><a class="reference internal" href="#acknowledgements">Acknowledgements</a></li> <li><a class="reference internal" href="#references">References</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>Annotations are a Python technology that allows expressing type information and other metadata about Python functions, classes, and modules. But Python’s original semantics for annotations required them to be eagerly evaluated, at the time the annotated object was bound. This caused chronic problems for static type analysis users using “type hints”, due to forward-reference and circular-reference problems.</p> <p>Python solved this by accepting <a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a>, incorporating a new approach called “stringized annotations” in which annotations were automatically converted into strings by Python. This solved the forward-reference and circular-reference problems, and also fostered intriguing new uses for annotation metadata. But stringized annotations in turn caused chronic problems for runtime users of annotations.</p> <p>This PEP proposes a new and comprehensive third approach for representing and computing annotations. It adds a new internal mechanism for lazily computing annotations on demand, via a new object method called <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code>. This approach, when combined with a novel technique for coercing annotation values into alternative formats, solves all the above problems, supports all existing use cases, and should foster future innovations in annotations.</p> </section> <section id="overview"> <h2><a class="toc-backref" href="#overview" role="doc-backlink">Overview</a></h2> <p>This PEP adds a new dunder attribute to the objects that support annotations–functions, classes, and modules. The new attribute is called <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code>, and is a reference to a function which computes and returns that object’s annotations dict.</p> <p>At compile time, if the definition of an object includes annotations, the Python compiler will write the expressions computing the annotations into its own function. When run, the function will return the annotations dict. The Python compiler then stores a reference to this function in <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> on the object.</p> <p>Furthermore, <code class="docutils literal notranslate"><span class="pre">__annotations__</span></code> is redefined to be a “data descriptor” which calls this annotation function once and caches the result.</p> <p>This mechanism delays the evaluation of annotations expressions until the annotations are examined, which solves many circular reference problems.</p> <p>This PEP also defines new functionality for two functions in the Python standard library: <code class="docutils literal notranslate"><span class="pre">inspect.get_annotations</span></code> and <code class="docutils literal notranslate"><span class="pre">typing.get_type_hints</span></code>. The functionality is accessed via a new keyword-only parameter, <code class="docutils literal notranslate"><span class="pre">format</span></code>. <code class="docutils literal notranslate"><span class="pre">format</span></code> allows the user to request the annotations from these functions in a specific format. Format identifiers are always predefined integer values. The formats defined by this PEP are:</p> <ul> <li><code class="docutils literal notranslate"><span class="pre">inspect.VALUE</span> <span class="pre">=</span> <span class="pre">1</span></code><p>The default value. The function will return the conventional Python values for the annotations. This format is identical to the return value for these functions under Python 3.11.</p> </li> <li><code class="docutils literal notranslate"><span class="pre">inspect.FORWARDREF</span> <span class="pre">=</span> <span class="pre">2</span></code><p>The function will attempt to return the conventional Python values for the annotations. However, if it encounters an undefined name, or a free variable that has not yet been associated with a value, it dynamically creates a proxy object (a <code class="docutils literal notranslate"><span class="pre">ForwardRef</span></code>) that substitutes for that value in the expression, then continues evaluation. The resulting dict may contain a mixture of proxies and real values. If all real values are defined at the time the function is called, <code class="docutils literal notranslate"><span class="pre">inspect.FORWARDREF</span></code> and <code class="docutils literal notranslate"><span class="pre">inspect.VALUE</span></code> produce identical results.</p> </li> <li><code class="docutils literal notranslate"><span class="pre">inspect.SOURCE</span> <span class="pre">=</span> <span class="pre">3</span></code><p>The function will produce an annotation dictionary where the values have been replaced by strings containing the original source code for the annotation expressions. These strings may only be approximate, as they may be reverse-engineered from another format, rather than preserving the original source code, but the differences will be minor.</p> </li> </ul> <p>If accepted, this PEP would <em>supersede</em> <a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a>, and <a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a>’s behavior would be deprecated and eventually removed.</p> <section id="comparison-of-annotation-semantics"> <h3><a class="toc-backref" href="#comparison-of-annotation-semantics" role="doc-backlink">Comparison Of Annotation Semantics</a></h3> <div class="admonition note"> <p class="admonition-title">Note</p> <p>The code presented in this section is simplified for clarity, and is intentionally inaccurate in some critical aspects. This example is intended merely to communicate the high-level concepts involved without getting lost in the details. But readers should note that the actual implementation is quite different in several important ways. See the <a class="reference internal" href="#implementation">Implementation</a> section later in this PEP for a far more accurate description of what this PEP proposes from a technical level.</p> </div> <p>Consider this example code:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">foo</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">3</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="n">MyType</span> <span class="o">=</span> <span class="kc">None</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">float</span><span class="p">:</span> <span class="o">...</span> <span class="k">class</span><span class="w"> </span><span class="nc">MyType</span><span class="p">:</span> <span class="o">...</span> <span class="n">foo_y_annotation</span> <span class="o">=</span> <span class="n">foo</span><span class="o">.</span><span class="vm">__annotations__</span><span class="p">[</span><span class="s1">&#39;y&#39;</span><span class="p">]</span> </pre></div> </div> <p>As we see here, annotations are available at runtime through an <code class="docutils literal notranslate"><span class="pre">__annotations__</span></code> attribute on functions, classes, and modules. When annotations are specified on one of these objects, <code class="docutils literal notranslate"><span class="pre">__annotations__</span></code> is a dictionary mapping the names of the fields to the value specified as that field’s annotation.</p> <p>The default behavior in Python is to evaluate the expressions for the annotations, and build the annotations dict, at the time the function, class, or module is bound. At runtime the above code actually works something like this:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">annotations</span> <span class="o">=</span> <span class="p">{</span><span class="s1">&#39;x&#39;</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="s1">&#39;y&#39;</span><span class="p">:</span> <span class="n">MyType</span><span class="p">,</span> <span class="s1">&#39;return&#39;</span><span class="p">:</span> <span class="nb">float</span><span class="p">}</span> <span class="k">def</span><span class="w"> </span><span class="nf">foo</span><span class="p">(</span><span class="n">x</span> <span class="o">=</span> <span class="mi">3</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="s2">&quot;abc&quot;</span><span class="p">):</span> <span class="o">...</span> <span class="n">foo</span><span class="o">.</span><span class="vm">__annotations__</span> <span class="o">=</span> <span class="n">annotations</span> <span class="k">class</span><span class="w"> </span><span class="nc">MyType</span><span class="p">:</span> <span class="o">...</span> <span class="n">foo_y_annotation</span> <span class="o">=</span> <span class="n">foo</span><span class="o">.</span><span class="vm">__annotations__</span><span class="p">[</span><span class="s1">&#39;y&#39;</span><span class="p">]</span> </pre></div> </div> <p>The crucial detail here is that the values <code class="docutils literal notranslate"><span class="pre">int</span></code>, <code class="docutils literal notranslate"><span class="pre">MyType</span></code>, and <code class="docutils literal notranslate"><span class="pre">float</span></code> are looked up at the time the function object is bound, and these values are stored in the annotations dict. But this code doesn’t run—it throws a <code class="docutils literal notranslate"><span class="pre">NameError</span></code> on the first line, because <code class="docutils literal notranslate"><span class="pre">MyType</span></code> hasn’t been defined yet.</p> <p><a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a>’s solution is to decompile the expressions back into strings during compilation and store those strings as the values in the annotations dict. The equivalent runtime code would look something like this:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">annotations</span> <span class="o">=</span> <span class="p">{</span><span class="s1">&#39;x&#39;</span><span class="p">:</span> <span class="s1">&#39;int&#39;</span><span class="p">,</span> <span class="s1">&#39;y&#39;</span><span class="p">:</span> <span class="s1">&#39;MyType&#39;</span><span class="p">,</span> <span class="s1">&#39;return&#39;</span><span class="p">:</span> <span class="s1">&#39;float&#39;</span><span class="p">}</span> <span class="k">def</span><span class="w"> </span><span class="nf">foo</span><span class="p">(</span><span class="n">x</span> <span class="o">=</span> <span class="mi">3</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="s2">&quot;abc&quot;</span><span class="p">):</span> <span class="o">...</span> <span class="n">foo</span><span class="o">.</span><span class="vm">__annotations__</span> <span class="o">=</span> <span class="n">annotations</span> <span class="k">class</span><span class="w"> </span><span class="nc">MyType</span><span class="p">:</span> <span class="o">...</span> <span class="n">foo_y_annotation</span> <span class="o">=</span> <span class="n">foo</span><span class="o">.</span><span class="vm">__annotations__</span><span class="p">[</span><span class="s1">&#39;y&#39;</span><span class="p">]</span> </pre></div> </div> <p>This code now runs successfully. However, <code class="docutils literal notranslate"><span class="pre">foo_y_annotation</span></code> is no longer a reference to <code class="docutils literal notranslate"><span class="pre">MyType</span></code>, it is the <em>string</em> <code class="docutils literal notranslate"><span class="pre">'MyType'</span></code>. To turn the string into the real value <code class="docutils literal notranslate"><span class="pre">MyType</span></code>, the user would need to evaluate the string using <code class="docutils literal notranslate"><span class="pre">eval</span></code>, <code class="docutils literal notranslate"><span class="pre">inspect.get_annotations</span></code>, or <code class="docutils literal notranslate"><span class="pre">typing.get_type_hints</span></code>.</p> <p>This PEP proposes a third approach, delaying the evaluation of the annotations by computing them in their own function. If this PEP was active, the generated code would work something like this:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">function</span><span class="p">:</span> <span class="c1"># __annotations__ on a function object is already a</span> <span class="c1"># &quot;data descriptor&quot; in Python, we&#39;re just changing</span> <span class="c1"># what it does</span> <span class="nd">@property</span> <span class="k">def</span><span class="w"> </span><span class="nf">__annotations__</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">__annotate__</span><span class="p">()</span> <span class="c1"># ...</span> <span class="k">def</span><span class="w"> </span><span class="nf">annotate_foo</span><span class="p">():</span> <span class="k">return</span> <span class="p">{</span><span class="s1">&#39;x&#39;</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="s1">&#39;y&#39;</span><span class="p">:</span> <span class="n">MyType</span><span class="p">,</span> <span class="s1">&#39;return&#39;</span><span class="p">:</span> <span class="nb">float</span><span class="p">}</span> <span class="k">def</span><span class="w"> </span><span class="nf">foo</span><span class="p">(</span><span class="n">x</span> <span class="o">=</span> <span class="mi">3</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="s2">&quot;abc&quot;</span><span class="p">):</span> <span class="o">...</span> <span class="n">foo</span><span class="o">.</span><span class="n">__annotate__</span> <span class="o">=</span> <span class="n">annotate_foo</span> <span class="k">class</span><span class="w"> </span><span class="nc">MyType</span><span class="p">:</span> <span class="o">...</span> <span class="n">foo_y_annotation</span> <span class="o">=</span> <span class="n">foo</span><span class="o">.</span><span class="vm">__annotations__</span><span class="p">[</span><span class="s1">&#39;y&#39;</span><span class="p">]</span> </pre></div> </div> <p>The important change is that the code constructing the annotations dict now lives in a function—here, called <code class="docutils literal notranslate"><span class="pre">annotate_foo()</span></code>. But this function isn’t called until we ask for the value of <code class="docutils literal notranslate"><span class="pre">foo.__annotations__</span></code>, and we don’t do that until <em>after</em> the definition of <code class="docutils literal notranslate"><span class="pre">MyType</span></code>. So this code also runs successfully, and <code class="docutils literal notranslate"><span class="pre">foo_y_annotation</span></code> now has the correct value–the class <code class="docutils literal notranslate"><span class="pre">MyType</span></code>–even though <code class="docutils literal notranslate"><span class="pre">MyType</span></code> wasn’t defined until <em>after</em> the annotation was defined.</p> </section> <section id="mistaken-rejection-of-this-approach-in-november-2017"> <h3><a class="toc-backref" href="#mistaken-rejection-of-this-approach-in-november-2017" role="doc-backlink">Mistaken Rejection Of This Approach In November 2017</a></h3> <p>During the early days of discussion around <a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a>, in a November 2017 thread in <code class="docutils literal notranslate"><span class="pre">comp.lang.python-dev</span></code>, the idea of using code to delay the evaluation of annotations was briefly discussed. At the time the technique was termed an “implicit lambda expression”.</p> <p>Guido van Rossum—Python’s BDFL at the time—replied, asserting that these “implicit lambda expression” wouldn’t work, because they’d only be able to resolve symbols at module-level scope:</p> <blockquote> <div>IMO the inability of referencing class-level definitions from annotations on methods pretty much kills this idea.</div></blockquote> <p><a class="reference external" href="https://mail.python.org/pipermail/python-dev/2017-November/150109.html">https://mail.python.org/pipermail/python-dev/2017-November/150109.html</a></p> <p>This led to a short discussion about extending lambda-ized annotations for methods to be able to refer to class-level definitions, by maintaining a reference to the class-level scope. This idea, too, was quickly rejected.</p> <p><a class="pep reference internal" href="../pep-0563/#keeping-the-ability-to-use-function-local-state-when-defining-annotations" title="PEP 563 – Postponed Evaluation of Annotations § Keeping the ability to use function local state when defining annotations">PEP 563 summarizes the above discussion</a></p> <p>The approach taken by this PEP doesn’t suffer from these restrictions. Annotations can access module-level definitions, class-level definitions, and even local and free variables.</p> </section> </section> <section id="motivation"> <h2><a class="toc-backref" href="#motivation" role="doc-backlink">Motivation</a></h2> <section id="a-history-of-annotations"> <h3><a class="toc-backref" href="#a-history-of-annotations" role="doc-backlink">A History Of Annotations</a></h3> <p>Python 3.0 shipped with a new syntax feature, “annotations”, defined in <a class="pep reference internal" href="../pep-3107/" title="PEP 3107 – Function Annotations">PEP 3107</a>. This allowed specifying a Python value that would be associated with a parameter of a Python function, or with the value that function returns. Said another way, annotations gave Python users an interface to provide rich metadata about a function parameter or return value, for example type information. All the annotations for a function were stored together in a new attribute <code class="docutils literal notranslate"><span class="pre">__annotations__</span></code>, in an “annotation dict” that mapped parameter names (or, in the case of the return annotation, using the name <code class="docutils literal notranslate"><span class="pre">'return'</span></code>) to their Python value.</p> <p>In an effort to foster experimentation, Python intentionally didn’t define what form this metadata should take, or what values should be used. User code began experimenting with this new facility almost immediately. But popular libraries that make use of this functionality were slow to emerge.</p> <p>After years of little progress, the BDFL chose a particular approach for expressing static type information, called <em>type hints,</em> as defined in <a class="pep reference internal" href="../pep-0484/" title="PEP 484 – Type Hints">PEP 484</a>. Python 3.5 shipped with a new <a class="reference external" href="https://docs.python.org/3/library/typing.html#module-typing" title="(in Python v3.13)"><code class="xref py py-mod docutils literal notranslate"><span class="pre">typing</span></code></a> module which quickly became very popular.</p> <p>Python 3.6 added syntax to annotate local variables, class attributes, and module attributes, using the approach proposed in <a class="pep reference internal" href="../pep-0526/" title="PEP 526 – Syntax for Variable Annotations">PEP 526</a>. Static type analysis continued to grow in popularity.</p> <p>However, static type analysis users were increasingly frustrated by an inconvenient problem: forward references. In classic Python, if a class C depends on a later-defined class D, it’s normally not a problem, because user code will usually wait until both are defined before trying to use either. But annotations added a new complication, because they were computed at the time the annotated object (function, class, or module) was bound. If methods on class C are annotated with type D, and these annotation expressions are computed at the time that the method is bound, D may not be defined yet. And if methods in D are also annotated with type C, you now have an unresolvable circular reference problem.</p> <p>Initially, static type users worked around this problem by defining their problematic annotations as strings. This worked because a string containing the type hint was just as usable for the static type analysis tool. And users of static type analysis tools rarely examine the annotations at runtime, so this representation wasn’t itself an inconvenience. But manually stringizing type hints was clumsy and error-prone. Also, code bases were adding more and more annotations, which consumed more and more CPU time to create and bind.</p> <p>To solve these problems, the BDFL accepted <a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a>, which added a new feature to Python 3.7: “stringized annotations”. It was activated with a future import:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">__future__</span><span class="w"> </span><span class="kn">import</span> <span class="n">annotations</span> </pre></div> </div> <p>Normally, annotation expressions were evaluated at the time the object was bound, with their values being stored in the annotations dict. When stringized annotations were active, these semantics changed: instead, at compile time, the compiler converted all annotations in that module into string representations of their source code–thus, <em>automatically</em> turning the users’s annotations into strings, obviating the need to <em>manually</em> stringize them as before. <a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a> suggested users could evaluate this string with <code class="docutils literal notranslate"><span class="pre">eval</span></code> if the actual value was needed at runtime.</p> <p>(From here on out, this PEP will refer to the classic semantics of <a class="pep reference internal" href="../pep-3107/" title="PEP 3107 – Function Annotations">PEP 3107</a> and <a class="pep reference internal" href="../pep-0526/" title="PEP 526 – Syntax for Variable Annotations">PEP 526</a>, where the values of annotation expressions are computed at the time the object is bound, as <em>“stock” semantics,</em> to differentiate them from the new <a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a> “stringized” annotation semantics.)</p> </section> <section id="the-current-state-of-annotation-use-cases"> <h3><a class="toc-backref" href="#the-current-state-of-annotation-use-cases" role="doc-backlink">The Current State Of Annotation Use Cases</a></h3> <p>Although there are many specific use cases for annotations, annotation users in the discussion around this PEP tended to fall into one of these four categories.</p> <section id="static-typing-users"> <h4><a class="toc-backref" href="#static-typing-users" role="doc-backlink">Static typing users</a></h4> <p>Static typing users use annotations to add type information to their code. But they largely don’t examine the annotations at runtime. Instead, they use static type analysis tools (mypy, pytype) to examine their source tree and determine whether or not their code is using types consistently. This is almost certainly the most popular use case for annotations today.</p> <p>Many of the annotations use <em>type hints,</em> a la <a class="pep reference internal" href="../pep-0484/" title="PEP 484 – Type Hints">PEP 484</a> (and many subsequent PEPs). Type hints are passive objects, mere representation of type information; they don’t do any actual work. Type hints are often parameterized with other types or other type hints. Since they’re agnostic about what these actual values are, type hints work fine with <code class="docutils literal notranslate"><span class="pre">ForwardRef</span></code> proxy objects. Users of static type hints discovered that extensive type hinting under stock semantics often created large-scale circular reference and circular import problems that could be difficult to solve. <a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a> was designed specifically to solve this problem, and the solution worked great for these users. The difficulty of rendering stringized annotations into real values largely didn’t inconvenience these users because of how infrequently they examine annotations at runtime.</p> <p>Static typing users often combine <a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a> with the <code class="docutils literal notranslate"><span class="pre">if</span> <span class="pre">typing.TYPE_CHECKING</span></code> idiom to prevent their type hints from being loaded at runtime. This means they often aren’t able to evaluate their stringized annotations and produce real values at runtime. On the rare occasion that they do examine annotations at runtime, they often forgo <code class="docutils literal notranslate"><span class="pre">eval</span></code>, instead using lexical analysis directly on the stringized annotations.</p> <p>Under this PEP, static typing users will probably prefer <code class="docutils literal notranslate"><span class="pre">FORWARDREF</span></code> or <code class="docutils literal notranslate"><span class="pre">SOURCE</span></code> format.</p> </section> <section id="runtime-annotation-users"> <h4><a class="toc-backref" href="#runtime-annotation-users" role="doc-backlink">Runtime annotation users</a></h4> <p>Runtime annotation users use annotations as a means of expressing rich metadata about their functions and classes, which they use as input to runtime behavior. Specific use cases include runtime type verification (Pydantic) and glue logic to expose Python APIs in another domain (FastAPI, Typer). The annotations may or may not be type hints.</p> <p>As runtime annotation users examine annotations at runtime, they were traditionally better served with stock semantics. This use case is largely incompatible with <a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a>, particularly with the <code class="docutils literal notranslate"><span class="pre">if</span> <span class="pre">typing.TYPE_CHECKING</span></code> idiom.</p> <p>Under this PEP, runtime annotation users will most likely prefer <code class="docutils literal notranslate"><span class="pre">VALUE</span></code> format, though some (e.g. if they evaluate annotations eagerly in a decorator and want to support forward references) may also use <code class="docutils literal notranslate"><span class="pre">FORWARDREF</span></code> format.</p> </section> <section id="wrappers"> <h4><a class="toc-backref" href="#wrappers" role="doc-backlink">Wrappers</a></h4> <p>Wrappers are functions or classes that wrap user functions or classes and add functionality. Examples of this would be <a class="reference external" href="https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass" title="(in Python v3.13)"><code class="xref py py-func docutils literal notranslate"><span class="pre">dataclass()</span></code></a>, <a class="reference external" href="https://docs.python.org/3/library/functools.html#functools.partial" title="(in Python v3.13)"><code class="xref py py-func docutils literal notranslate"><span class="pre">functools.partial()</span></code></a>, <code class="docutils literal notranslate"><span class="pre">attrs</span></code>, and <code class="docutils literal notranslate"><span class="pre">wrapt</span></code>.</p> <p>Wrappers are a distinct subcategory of runtime annotation users. Although they do use annotations at runtime, they may or may not actually examine the annotations of the objects they wrap–it depends on the functionality the wrapper provides. As a rule they should propagate the annotations of the wrapped object to the wrapper they create, although it’s possible they may modify those annotations.</p> <p>Wrappers were generally designed to work well under stock semantics. Whether or not they work well under <a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a> semantics depends on the degree to which they examine the wrapped object’s annotations. Often wrappers don’t care about the value per se, only needing specific information about the annotations. Even so, <a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a> and the <code class="docutils literal notranslate"><span class="pre">if</span> <span class="pre">typing.TYPE_CHECKING</span></code> idiom can make it difficult for wrappers to reliably determine the information they need at runtime. This is an ongoing, chronic problem. Under this PEP, wrappers will probably prefer <code class="docutils literal notranslate"><span class="pre">FORWARDREF</span></code> format for their internal logic. But the wrapped objects need to support all formats for their users.</p> </section> <section id="documentation"> <h4><a class="toc-backref" href="#documentation" role="doc-backlink">Documentation</a></h4> <p><a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a> stringized annotations were a boon for tools that mechanically construct documentation.</p> <p>Stringized type hints make for excellent documentation; type hints as expressed in source code are often succinct and readable. However, at runtime these same type hints can produce value at runtime whose repr is a sprawling, nested, unreadable mess. Thus documentation users were well-served by <a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a> but poorly served with stock semantics.</p> <p>Under this PEP, documentation users are expected to use <code class="docutils literal notranslate"><span class="pre">SOURCE</span></code> format.</p> </section> </section> <section id="motivation-for-this-pep"> <h3><a class="toc-backref" href="#motivation-for-this-pep" role="doc-backlink">Motivation For This PEP</a></h3> <p>Python’s original semantics for annotations made its use for static type analysis painful due to forward reference problems. <a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a> solved the forward reference problem, and many static type analysis users became happy early adopters of it. But its unconventional solution created new problems for two of the above cited use cases: runtime annotation users, and wrappers.</p> <p>First, stringized annotations didn’t permit referencing local or free variables, which meant many useful, reasonable approaches to creating annotations were no longer viable. This was particularly inconvenient for decorators that wrap existing functions and classes, as these decorators often use closures.</p> <p>Second, in order for <code class="docutils literal notranslate"><span class="pre">eval</span></code> to correctly look up globals in a stringized annotation, you must first obtain a reference to the correct module. But class objects don’t retain a reference to their globals. <a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a> suggests looking up a class’s module by name in <code class="docutils literal notranslate"><span class="pre">sys.modules</span></code>—a surprising requirement for a language-level feature.</p> <p>Additionally, complex but legitimate constructions can make it difficult to determine the correct globals and locals dicts to give to <code class="docutils literal notranslate"><span class="pre">eval</span></code> to properly evaluate a stringized annotation. Even worse, in some situations it may simply be infeasible.</p> <p>For example, some libraries (e.g. <code class="docutils literal notranslate"><span class="pre">typing.TypedDict</span></code>, <a class="reference external" href="https://docs.python.org/3/library/dataclasses.html#module-dataclasses" title="(in Python v3.13)"><code class="xref py py-mod docutils literal notranslate"><span class="pre">dataclasses</span></code></a>) wrap a user class, then merge all the annotations from all that class’s base classes together into one cumulative annotations dict. If those annotations were stringized, calling <code class="docutils literal notranslate"><span class="pre">eval</span></code> on them later may not work properly, because the globals dictionary used for the <code class="docutils literal notranslate"><span class="pre">eval</span></code> will be the module where the <em>user class</em> was defined, which may not be the same module where the <em>annotation</em> was defined. However, if the annotations were stringized because of forward-reference problems, calling <code class="docutils literal notranslate"><span class="pre">eval</span></code> on them early may not work either, due to the forward reference not being resolvable yet. This has proved to be difficult to reconcile; of the three bug reports linked to below, only one has been marked as fixed.</p> <ul class="simple"> <li><a class="reference external" href="https://github.com/python/cpython/issues/89687">https://github.com/python/cpython/issues/89687</a></li> <li><a class="reference external" href="https://github.com/python/cpython/issues/85421">https://github.com/python/cpython/issues/85421</a></li> <li><a class="reference external" href="https://github.com/python/cpython/issues/90531">https://github.com/python/cpython/issues/90531</a></li> </ul> <p>Even with proper globals <em>and</em> locals, <code class="docutils literal notranslate"><span class="pre">eval</span></code> can be unreliable on stringized annotations. <code class="docutils literal notranslate"><span class="pre">eval</span></code> can only succeed if all the symbols referenced in an annotations are defined. If a stringized annotation refers to a mixture of defined and undefined symbols, a simple <code class="docutils literal notranslate"><span class="pre">eval</span></code> of that string will fail. This is a problem for libraries with that need to examine the annotation, because they can’t reliably convert these stringized annotations into real values.</p> <ul class="simple"> <li>Some libraries (e.g. <a class="reference external" href="https://docs.python.org/3/library/dataclasses.html#module-dataclasses" title="(in Python v3.13)"><code class="xref py py-mod docutils literal notranslate"><span class="pre">dataclasses</span></code></a>) solved this by foregoing real values and performing lexical analysis of the stringized annotation, which requires a lot of work to get right.</li> <li>Other libraries still suffer with this problem, which can produce surprising runtime behavior. <a class="reference external" href="https://github.com/python/cpython/issues/97727">https://github.com/python/cpython/issues/97727</a></li> </ul> <p>Also, <code class="docutils literal notranslate"><span class="pre">eval()</span></code> is slow, and it isn’t always available; it’s sometimes removed for space reasons on certain platforms. <code class="docutils literal notranslate"><span class="pre">eval()</span></code> on MicroPython doesn’t support the <code class="docutils literal notranslate"><span class="pre">locals</span></code> argument, which makes converting stringized annotations into real values at runtime even harder.</p> <p>Finally, <a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a> requires Python implementations to stringize their annotations. This is surprising behavior—unprecedented for a language-level feature, with a complicated implementation, that must be updated whenever a new operator is added to the language.</p> <p>These problems motivated the research into finding a new approach to solve the problems facing annotations users, resulting in this PEP.</p> </section> </section> <section id="implementation"> <h2><a class="toc-backref" href="#implementation" role="doc-backlink">Implementation</a></h2> <section id="observed-semantics-for-annotations-expressions"> <h3><a class="toc-backref" href="#observed-semantics-for-annotations-expressions" role="doc-backlink">Observed semantics for annotations expressions</a></h3> <p>For any object <code class="docutils literal notranslate"><span class="pre">o</span></code> that supports annotations, provided that all names evaluated in the annotations expressions are bound before <code class="docutils literal notranslate"><span class="pre">o</span></code> is defined and never subsequently rebound, <code class="docutils literal notranslate"><span class="pre">o.__annotations__</span></code> will produce an identical annotations dict both when “stock” semantics are active and when this PEP is active. In particular, name resolution will be performed identically in both scenarios.</p> <p>When this PEP is active, the value of <code class="docutils literal notranslate"><span class="pre">o.__annotations__</span></code> won’t be calculated until the first time <code class="docutils literal notranslate"><span class="pre">o.__annotations__</span></code> itself is evaluated. All evaluation of the annotation expressions is delayed until this moment, which also means that</p> <ul class="simple"> <li>names referenced in the annotations expressions will use their <em>current</em> value at this moment, and</li> <li>if evaluating the annotations expressions raises an exception, that exception will be raised at this moment.</li> </ul> <p>Once <code class="docutils literal notranslate"><span class="pre">o.__annotations__</span></code> is successfully calculated for the first time, this value is cached and will be returned by future requests for <code class="docutils literal notranslate"><span class="pre">o.__annotations__</span></code>.</p> </section> <section id="annotate-and-annotations"> <h3><a class="toc-backref" href="#annotate-and-annotations" role="doc-backlink">__annotate__ and __annotations__</a></h3> <p>Python supports annotations on three different types: functions, classes, and modules. This PEP modifies the semantics on all three of these types in a similar way.</p> <p>First, this PEP adds a new “dunder” attribute, <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code>. <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> must be a “data descriptor”, implementing all three actions: get, set, and delete. The <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> attribute is always defined, and may only be set to either <code class="docutils literal notranslate"><span class="pre">None</span></code> or to a callable. (<code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> cannot be deleted.) If an object has no annotations, <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> should be initialized to <code class="docutils literal notranslate"><span class="pre">None</span></code>, rather than to a function that returns an empty dict.</p> <p>The <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> data descriptor must have dedicated storage inside the object to store the reference to its value. The location of this storage at runtime is an implementation detail. Even if it’s visible to Python code, it should still be considered an internal implementation detail, and Python code should prefer to interact with it only via the <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> attribute.</p> <p>The callable stored in <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> must accept a single required positional argument called <code class="docutils literal notranslate"><span class="pre">format</span></code>, which will always be an <code class="docutils literal notranslate"><span class="pre">int</span></code> (or a subclass of <code class="docutils literal notranslate"><span class="pre">int</span></code>). It must either return a dict (or subclass of dict) or raise <code class="docutils literal notranslate"><span class="pre">NotImplementedError()</span></code>.</p> <p>Here’s a formal definition of <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code>, as it will appear in the “Magic methods” section of the Python Language Reference:</p> <blockquote> <div><code class="docutils literal notranslate"><span class="pre">__annotate__(format:</span> <span class="pre">int)</span> <span class="pre">-&gt;</span> <span class="pre">dict</span></code><p>Returns a new dictionary object mapping attribute/parameter names to their annotation values.</p> <p>Takes a <code class="docutils literal notranslate"><span class="pre">format</span></code> parameter specifying the format in which annotations values should be provided. Must be one of the following:</p> <p><code class="docutils literal notranslate"><span class="pre">inspect.VALUE</span></code> (equivalent to the <code class="docutils literal notranslate"><span class="pre">int</span></code> constant <code class="docutils literal notranslate"><span class="pre">1</span></code>)</p> <blockquote> <div>Values are the result of evaluating the annotation expressions.</div></blockquote> <p><code class="docutils literal notranslate"><span class="pre">inspect.FORWARDREF</span></code> (equivalent to the <code class="docutils literal notranslate"><span class="pre">int</span></code> constant <code class="docutils literal notranslate"><span class="pre">2</span></code>)</p> <blockquote> <div>Values are real annotation values (as per <code class="docutils literal notranslate"><span class="pre">inspect.VALUE</span></code> format) for defined values, and <code class="docutils literal notranslate"><span class="pre">ForwardRef</span></code> proxies for undefined values. Real objects may be exposed to, or contain references to, <code class="docutils literal notranslate"><span class="pre">ForwardRef</span></code> proxy objects.</div></blockquote> <p><code class="docutils literal notranslate"><span class="pre">inspect.SOURCE</span></code> (equivalent to the <code class="docutils literal notranslate"><span class="pre">int</span></code> constant <code class="docutils literal notranslate"><span class="pre">3</span></code>)</p> <blockquote> <div>Values are the text string of the annotation as it appears in the source code. May only be approximate; whitespace may be normalized, and constant values may be optimized. It’s possible the exact values of these strings could change in future version of Python.</div></blockquote> <p>If an <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> function doesn’t support the requested format, it must raise <code class="docutils literal notranslate"><span class="pre">NotImplementedError()</span></code>. <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> functions must always support <code class="docutils literal notranslate"><span class="pre">1</span></code> (<code class="docutils literal notranslate"><span class="pre">inspect.VALUE</span></code>) format; they must not raise <code class="docutils literal notranslate"><span class="pre">NotImplementedError()</span></code> when called with <code class="docutils literal notranslate"><span class="pre">format=1</span></code>.</p> <p>When called with <code class="docutils literal notranslate"><span class="pre">format=1</span></code>, an <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> function may raise <code class="docutils literal notranslate"><span class="pre">NameError</span></code>; it must not raise <code class="docutils literal notranslate"><span class="pre">NameError</span></code> when called requesting any other format.</p> <p>If an object doesn’t have any annotations, <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> should preferably be set to <code class="docutils literal notranslate"><span class="pre">None</span></code> (it can’t be deleted), rather than set to a function that returns an empty dict.</p> </div></blockquote> <p>When the Python compiler compiles an object with annotations, it simultaneously compiles the appropriate annotate function. This function, called with the single positional argument <code class="docutils literal notranslate"><span class="pre">inspect.VALUE</span></code>, computes and returns the annotations dict as defined on that object. The Python compiler and runtime work in concert to ensure that the function is bound to the appropriate namespaces:</p> <ul class="simple"> <li>For functions and classes, the globals dictionary will be the module where the object was defined. If the object is itself a module, its globals dictionary will be its own dict.</li> <li>For methods on classes, and for classes, the locals dictionary will be the class dictionary.</li> <li>If the annotations refer to free variables, the closure will be the appropriate closure tuple containing cells for free variables.</li> </ul> <p>Second, this PEP requires that the existing <code class="docutils literal notranslate"><span class="pre">__annotations__</span></code> must be a “data descriptor”, implementing all three actions: get, set, and delete. <code class="docutils literal notranslate"><span class="pre">__annotations__</span></code> must also have its own internal storage it uses to cache a reference to the annotations dict:</p> <ul class="simple"> <li>Class and module objects must cache the annotations dict in their <code class="docutils literal notranslate"><span class="pre">__dict__</span></code>, using the key <code class="docutils literal notranslate"><span class="pre">__annotations__</span></code>. This is required for backwards compatibility reasons.</li> <li>For function objects, storage for the annotations dict cache is an implementation detail. It’s preferably internal to the function object and not visible in Python.</li> </ul> <p>This PEP defines semantics on how <code class="docutils literal notranslate"><span class="pre">__annotations__</span></code> and <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> interact, for all three types that implement them. In the following examples, <code class="docutils literal notranslate"><span class="pre">fn</span></code> represents a function, <code class="docutils literal notranslate"><span class="pre">cls</span></code> represents a class, <code class="docutils literal notranslate"><span class="pre">mod</span></code> represents a module, and <code class="docutils literal notranslate"><span class="pre">o</span></code> represents an object of any of these three types:</p> <ul class="simple"> <li>When <code class="docutils literal notranslate"><span class="pre">o.__annotations__</span></code> is evaluated, and the internal storage for <code class="docutils literal notranslate"><span class="pre">o.__annotations__</span></code> is unset, and <code class="docutils literal notranslate"><span class="pre">o.__annotate__</span></code> is set to a callable, the getter for <code class="docutils literal notranslate"><span class="pre">o.__annotations__</span></code> calls <code class="docutils literal notranslate"><span class="pre">o.__annotate__(1)</span></code>, then caches the result in its internal storage and returns the result.<ul> <li>To explicitly clarify one question that has come up multiple times: this <code class="docutils literal notranslate"><span class="pre">o.__annotations__</span></code> cache is the <em>only</em> caching mechanism defined in this PEP. There are <em>no other</em> caching mechanisms defined in this PEP. The <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> functions generated by the Python compiler explicitly don’t cache any of the values they compute.</li> </ul> </li> <li>Setting <code class="docutils literal notranslate"><span class="pre">o.__annotate__</span></code> to a callable invalidates the cached annotations dict.</li> <li>Setting <code class="docutils literal notranslate"><span class="pre">o.__annotate__</span></code> to <code class="docutils literal notranslate"><span class="pre">None</span></code> has no effect on the cached annotations dict.</li> <li>Deleting <code class="docutils literal notranslate"><span class="pre">o.__annotate__</span></code> raises <code class="docutils literal notranslate"><span class="pre">TypeError</span></code>. <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> must always be set; this prevents unannotated subclasses from inheriting the <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> method of one of their base classes.</li> <li>Setting <code class="docutils literal notranslate"><span class="pre">o.__annotations__</span></code> to a legal value automatically sets <code class="docutils literal notranslate"><span class="pre">o.__annotate__</span></code> to <code class="docutils literal notranslate"><span class="pre">None</span></code>.<ul> <li>Setting <code class="docutils literal notranslate"><span class="pre">cls.__annotations__</span></code> or <code class="docutils literal notranslate"><span class="pre">mod.__annotations__</span></code> to <code class="docutils literal notranslate"><span class="pre">None</span></code> otherwise works like any other attribute; the attribute is set to <code class="docutils literal notranslate"><span class="pre">None</span></code>.</li> <li>Setting <code class="docutils literal notranslate"><span class="pre">fn.__annotations__</span></code> to <code class="docutils literal notranslate"><span class="pre">None</span></code> invalidates the cached annotations dict. If <code class="docutils literal notranslate"><span class="pre">fn.__annotations__</span></code> doesn’t have a cached annotations value, and <code class="docutils literal notranslate"><span class="pre">fn.__annotate__</span></code> is <code class="docutils literal notranslate"><span class="pre">None</span></code>, the <code class="docutils literal notranslate"><span class="pre">fn.__annotations__</span></code> data descriptor creates, caches, and returns a new empty dict. (This is for backwards compatibility with <a class="pep reference internal" href="../pep-3107/" title="PEP 3107 – Function Annotations">PEP 3107</a> semantics.)</li> </ul> </li> </ul> </section> <section id="changes-to-allowable-annotations-syntax"> <h3><a class="toc-backref" href="#changes-to-allowable-annotations-syntax" role="doc-backlink">Changes to allowable annotations syntax</a></h3> <p><code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> now delays the evaluation of annotations until <code class="docutils literal notranslate"><span class="pre">__annotations__</span></code> is referenced in the future. It also means annotations are evaluated in a new function, rather than in the original context where the object they were defined on was bound. There are four operators with significant runtime side-effects that were permitted in stock semantics, but are disallowed when <code class="docutils literal notranslate"><span class="pre">from</span> <span class="pre">__future__</span> <span class="pre">import</span> <span class="pre">annotations</span></code> is active, and will have to be disallowed when this PEP is active:</p> <ul class="simple"> <li><code class="docutils literal notranslate"><span class="pre">:=</span></code></li> <li><code class="docutils literal notranslate"><span class="pre">yield</span></code></li> <li><code class="docutils literal notranslate"><span class="pre">yield</span> <span class="pre">from</span></code></li> <li><code class="docutils literal notranslate"><span class="pre">await</span></code></li> </ul> </section> <section id="changes-to-inspect-get-annotations-and-typing-get-type-hints"> <h3><a class="toc-backref" href="#changes-to-inspect-get-annotations-and-typing-get-type-hints" role="doc-backlink">Changes to <code class="docutils literal notranslate"><span class="pre">inspect.get_annotations</span></code> and <code class="docutils literal notranslate"><span class="pre">typing.get_type_hints</span></code></a></h3> <p>(This PEP makes frequent reference to these two functions. In the future it will refer to them collectively as “the helper functions”, as they help user code work with annotations.)</p> <p>These two functions extract and return the annotations from an object. <code class="docutils literal notranslate"><span class="pre">inspect.get_annotations</span></code> returns the annotations unchanged; for the convenience of static typing users, <code class="docutils literal notranslate"><span class="pre">typing.get_type_hints</span></code> makes some modifications to the annotations before it returns them.</p> <p>This PEP adds a new keyword-only parameter to these two functions, <code class="docutils literal notranslate"><span class="pre">format</span></code>. <code class="docutils literal notranslate"><span class="pre">format</span></code> specifies what format the values in the annotations dict should be returned in. The <code class="docutils literal notranslate"><span class="pre">format</span></code> parameter on these two functions accepts the same values as the <code class="docutils literal notranslate"><span class="pre">format</span></code> parameter on the <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> magic method defined above; however, these <code class="docutils literal notranslate"><span class="pre">format</span></code> parameters also have a default value of <code class="docutils literal notranslate"><span class="pre">inspect.VALUE</span></code>.</p> <p>When either <code class="docutils literal notranslate"><span class="pre">__annotations__</span></code> or <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> is updated on an object, the other of those two attributes is now out-of-date and should also either be updated or deleted (set to <code class="docutils literal notranslate"><span class="pre">None</span></code>, in the case of <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> which cannot be deleted). In general, the semantics established in the previous section ensure that this happens automatically. However, there’s one case which for all practical purposes can’t be handled automatically: when the dict cached by <code class="docutils literal notranslate"><span class="pre">o.__annotations__</span></code> is itself modified, or when mutable values inside that dict are modified.</p> <p>Since this can’t be handled in code, it must be handled in documentation. This PEP proposes amending the documentation for <code class="docutils literal notranslate"><span class="pre">inspect.get_annotations</span></code> (and similarly for <code class="docutils literal notranslate"><span class="pre">typing.get_type_hints</span></code>) as follows:</p> <blockquote> <div>If you directly modify the <code class="docutils literal notranslate"><span class="pre">__annotations__</span></code> dict on an object, by default these changes may not be reflected in the dictionary returned by <code class="docutils literal notranslate"><span class="pre">inspect.get_annotations</span></code> when requesting either <code class="docutils literal notranslate"><span class="pre">SOURCE</span></code> or <code class="docutils literal notranslate"><span class="pre">FORWARDREF</span></code> format on that object. Rather than modifying the <code class="docutils literal notranslate"><span class="pre">__annotations__</span></code> dict directly, consider replacing that object’s <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> method with a function computing the annotations dict with your desired values. Failing that, it’s best to overwrite the object’s <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> method with <code class="docutils literal notranslate"><span class="pre">None</span></code> to prevent <code class="docutils literal notranslate"><span class="pre">inspect.get_annotations</span></code> from generating stale results for <code class="docutils literal notranslate"><span class="pre">SOURCE</span></code> and <code class="docutils literal notranslate"><span class="pre">FORWARDREF</span></code> formats.</div></blockquote> </section> <section id="the-stringizer-and-the-fake-globals-environment"> <h3><a class="toc-backref" href="#the-stringizer-and-the-fake-globals-environment" role="doc-backlink">The <code class="docutils literal notranslate"><span class="pre">stringizer</span></code> and the <code class="docutils literal notranslate"><span class="pre">fake</span> <span class="pre">globals</span></code> environment</a></h3> <p>As originally proposed, this PEP supported many runtime annotation user use cases, and many static type user use cases. But this was insufficient–this PEP could not be accepted until it satisfied <em>all</em> extant use cases. This became a longtime blocker of this PEP until Carl Meyer proposed the “stringizer” and the “fake globals” environment as described below. These techniques allow this PEP to support both the <code class="docutils literal notranslate"><span class="pre">FORWARDREF</span></code> and <code class="docutils literal notranslate"><span class="pre">SOURCE</span></code> formats, ably satisfying all remaining uses cases.</p> <p>In a nutshell, this technique involves running a Python-compiler-generated <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> function in an exotic runtime environment. Its normal <code class="docutils literal notranslate"><span class="pre">globals</span></code> dict is replaced with what’s called a “fake globals” dict. A “fake globals” dict is a dict with one important difference: every time you “get” a key from it that isn’t mapped, it creates, caches, and returns a new value for that key (as per the <code class="docutils literal notranslate"><span class="pre">__missing__</span></code> callback for a dictionary). That value is a an instance of a novel type referred to as a “stringizer”.</p> <p>A “stringizer” is a Python class with highly unusual behavior. Every stringizer is initialized with its “value”, initially the name of the missing key in the “fake globals” dict. The stringizer then implements every Python “dunder” method used to implement operators, and the value returned by that method is a new stringizer whose value is a text representation of that operation.</p> <p>When these stringizers are used in expressions, the result of the expression is a new stringizer whose name textually represents that expression. For example, let’s say you have a variable <code class="docutils literal notranslate"><span class="pre">f</span></code>, which is a reference to a stringizer initialized with the value <code class="docutils literal notranslate"><span class="pre">'f'</span></code>. Here are some examples of operations you could perform on <code class="docutils literal notranslate"><span class="pre">f</span></code> and the values they would return:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">f</span> <span class="go">Stringizer(&#39;f&#39;)</span> <span class="gp">&gt;&gt;&gt; </span><span class="n">f</span> <span class="o">+</span> <span class="mi">3</span> <span class="go">Stringizer(&#39;f + 3&#39;)</span> <span class="go">&gt;&gt; f[&quot;key&quot;]</span> <span class="go">Stringizer(&#39;f[&quot;key&quot;]&#39;)</span> </pre></div> </div> <p>Bringing it all together: if we run a Python-generated <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> function, but we replace its globals with a “fake globals” dict, all undefined symbols it references will be replaced with stringizer proxy objects representing those symbols, and any operations performed on those proxies will in turn result in proxies representing that expression. This allows <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> to complete, and to return an annotations dict, with stringizer instances standing in for names and entire expressions that could not have otherwise been evaluated.</p> <p>In practice, the “stringizer” functionality will be implemented in the <code class="docutils literal notranslate"><span class="pre">ForwardRef</span></code> object currently defined in the <code class="docutils literal notranslate"><span class="pre">typing</span></code> module. <code class="docutils literal notranslate"><span class="pre">ForwardRef</span></code> will be extended to implement all stringizer functionality; it will also be extended to support evaluating the string it contains, to produce the real value (assuming all symbols referenced are defined). This means the <code class="docutils literal notranslate"><span class="pre">ForwardRef</span></code> object will retain references to the appropriate “globals”, “locals”, and even “closure” information needed to evaluate the expression.</p> <p>This technique is the core of how <code class="docutils literal notranslate"><span class="pre">inspect.get_annotations</span></code> supports <code class="docutils literal notranslate"><span class="pre">FORWARDREF</span></code> and <code class="docutils literal notranslate"><span class="pre">SOURCE</span></code> formats. Initially, <code class="docutils literal notranslate"><span class="pre">inspect.get_annotations</span></code> will call the object’s <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> method requesting the desired format. If that raises <code class="docutils literal notranslate"><span class="pre">NotImplementedError</span></code>, <code class="docutils literal notranslate"><span class="pre">inspect.get_annotations</span></code> will construct a “fake globals” environment, then call the object’s <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> method.</p> <ul class="simple"> <li><code class="docutils literal notranslate"><span class="pre">inspect.get_annotations</span></code> produces <code class="docutils literal notranslate"><span class="pre">SOURCE</span></code> format by creating a new empty “fake globals” dict, binding it to the object’s <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> method, calling that requesting <code class="docutils literal notranslate"><span class="pre">VALUE</span></code> format, and then extracting the string “value” from each <code class="docutils literal notranslate"><span class="pre">ForwardRef</span></code> object in the resulting dict.</li> <li><code class="docutils literal notranslate"><span class="pre">inspect.get_annotations</span></code> produces <code class="docutils literal notranslate"><span class="pre">FORWARDREF</span></code> format by creating a new empty “fake globals” dict, pre-populating it with the current contents of the <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> method’s globals dict, binding the “fake globals” dict to the object’s <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> method, calling that requesting <code class="docutils literal notranslate"><span class="pre">VALUE</span></code> format, and returning the result.</li> </ul> <p>This entire technique works because the <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> functions generated by the compiler are controlled by Python itself, and are simple and predictable. They’re effectively a single <code class="docutils literal notranslate"><span class="pre">return</span></code> statement, computing and returning the annotations dict. Since most operations needed to compute an annotation are implemented in Python using dunder methods, and the stringizer supports all the relevant dunder methods, this approach is a reliable, practical solution.</p> <p>However, it’s not reasonable to attempt this technique with just any <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> method. This PEP assumes that third-party libraries may implement their own <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> methods, and those functions would almost certainly work incorrectly when run in this “fake globals” environment. For that reason, this PEP allocates a flag on code objects, one of the unused bits in <code class="docutils literal notranslate"><span class="pre">co_flags</span></code>, to mean “This code object can be run in a ‘fake globals’ environment.” This makes the “fake globals” environment strictly opt-in, and it’s expected that only <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> methods generated by the Python compiler will set it.</p> <p>The weakness in this technique is in handling operators which don’t directly map to dunder methods on an object. These are all operators that implement some manner of flow control, either branching or iteration:</p> <ul class="simple"> <li>Short-circuiting <code class="docutils literal notranslate"><span class="pre">or</span></code></li> <li>Short-circuiting <code class="docutils literal notranslate"><span class="pre">and</span></code></li> <li>Ternary operator (the <code class="docutils literal notranslate"><span class="pre">if</span></code> / <code class="docutils literal notranslate"><span class="pre">then</span></code> operator)</li> <li>Generator expressions</li> <li>List / dict / set comprehensions</li> <li>Iterable unpacking</li> </ul> <p>As a rule these techniques aren’t used in annotations, so it doesn’t pose a problem in practice. However, the recent addition of <code class="docutils literal notranslate"><span class="pre">TypeVarTuple</span></code> to Python does use iterable unpacking. The dunder methods involved (<code class="docutils literal notranslate"><span class="pre">__iter__</span></code> and <code class="docutils literal notranslate"><span class="pre">__next__</span></code>) don’t permit distinguishing between iteration use cases; in order to correctly detect which use case was involved, mere “fake globals” and a “stringizer” wouldn’t be sufficient; this would require a custom bytecode interpreter designed specifically around producing <code class="docutils literal notranslate"><span class="pre">SOURCE</span></code> and <code class="docutils literal notranslate"><span class="pre">FORWARDREF</span></code> formats.</p> <p>Thankfully there’s a shortcut that will work fine: the stringizer will simply assume that when its iteration dunder methods are called, it’s in service of iterator unpacking being performed by <code class="docutils literal notranslate"><span class="pre">TypeVarTuple</span></code>. It will hard-code this behavior. This means no other technique using iteration will work, but in practice this won’t inconvenience real-world use cases.</p> <p>Finally, note that the “fake globals” environment will also require constructing a matching “fake locals” dictionary, which for <code class="docutils literal notranslate"><span class="pre">FORWARDREF</span></code> format will be pre-populated with the relevant locals dict. The “fake globals” environment will also have to create a fake “closure”, a tuple of <code class="docutils literal notranslate"><span class="pre">ForwardRef</span></code> objects pre-created with the names of the free variables referenced by the <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> method.</p> <p><code class="docutils literal notranslate"><span class="pre">ForwardRef</span></code> proxies created from <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> methods that reference free variables will map the names and closure values of those free variables into the locals dictionary, to ensure that <code class="docutils literal notranslate"><span class="pre">eval</span></code> uses the correct values for those names.</p> </section> <section id="compiler-generated-annotate-functions"> <h3><a class="toc-backref" href="#compiler-generated-annotate-functions" role="doc-backlink">Compiler-generated <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> functions</a></h3> <p>As mentioned in the previous section, the <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> functions generated by the compiler are simple. They’re mainly a single <code class="docutils literal notranslate"><span class="pre">return</span></code> statement, computing and returning the annotations dict.</p> <p>However, the protocol for <code class="docutils literal notranslate"><span class="pre">inspect.get_annotations</span></code> to request either <code class="docutils literal notranslate"><span class="pre">FORWARDREF</span></code> or <code class="docutils literal notranslate"><span class="pre">SOURCE</span></code> format requires first asking the <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> method to produce it. <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> methods generated by the Python compiler won’t support either of these formats and will raise <code class="docutils literal notranslate"><span class="pre">NotImplementedError()</span></code>.</p> </section> <section id="third-party-annotate-functions"> <h3><a class="toc-backref" href="#third-party-annotate-functions" role="doc-backlink">Third-party <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> functions</a></h3> <p>Third-party classes and functions will likely need to implement their own <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> methods, so that downstream users of those objects can take full advantage of annotations. In particular, wrappers will likely need to transform the annotation dicts produced by the wrapped object: adding, removing, or modifying the dictionary in some way.</p> <p>Most of the time, third-party code will implement their <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> methods by calling <code class="docutils literal notranslate"><span class="pre">inspect.get_annotations</span></code> on some existing upstream object. For example, wrappers will likely request the annotations dict for their wrapped object, in the format that was requested from them, then modify the returned annotations dict as appropriate and return that. This allows third-party code to leverage the “fake globals” technique without having to understand or participate in it.</p> <p>Third-party libraries that support both pre- and post-PEP-649 versions of Python will have to innovate their own best practices on how to support both. One sensible approach would be for their wrapper to always support <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code>, then call it requesting <code class="docutils literal notranslate"><span class="pre">VALUE</span></code> format and store the result as the <code class="docutils literal notranslate"><span class="pre">__annotations__</span></code> on their wrapper object. This would support pre-649 Python semantics, and be forward-compatible with post-649 semantics.</p> </section> <section id="pseudocode"> <h3><a class="toc-backref" href="#pseudocode" role="doc-backlink">Pseudocode</a></h3> <p>Here’s high-level pseudocode for <code class="docutils literal notranslate"><span class="pre">inspect.get_annotations</span></code>:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">get_annotations</span><span class="p">(</span><span class="n">o</span><span class="p">,</span> <span class="nb">format</span><span class="p">):</span> <span class="k">if</span> <span class="nb">format</span> <span class="o">==</span> <span class="n">VALUE</span><span class="p">:</span> <span class="k">return</span> <span class="nb">dict</span><span class="p">(</span><span class="n">o</span><span class="o">.</span><span class="vm">__annotations__</span><span class="p">)</span> <span class="k">if</span> <span class="nb">format</span> <span class="o">==</span> <span class="n">FORWARDREF</span><span class="p">:</span> <span class="k">try</span><span class="p">:</span> <span class="k">return</span> <span class="nb">dict</span><span class="p">(</span><span class="n">o</span><span class="o">.</span><span class="vm">__annotations__</span><span class="p">)</span> <span class="k">except</span> <span class="ne">NameError</span><span class="p">:</span> <span class="k">pass</span> <span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">o</span><span class="o">.</span><span class="n">__annotate__</span><span class="p">):</span> <span class="k">return</span> <span class="p">{}</span> <span class="n">c_a</span> <span class="o">=</span> <span class="n">o</span><span class="o">.</span><span class="n">__annotate__</span> <span class="k">try</span><span class="p">:</span> <span class="k">return</span> <span class="n">c_a</span><span class="p">(</span><span class="nb">format</span><span class="p">)</span> <span class="k">except</span> <span class="ne">NotImplementedError</span><span class="p">:</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">can_be_called_with_fake_globals</span><span class="p">(</span><span class="n">c_a</span><span class="p">):</span> <span class="k">return</span> <span class="p">{}</span> <span class="n">c_a_with_fake_globals</span> <span class="o">=</span> <span class="n">make_fake_globals_version</span><span class="p">(</span><span class="n">c_a</span><span class="p">,</span> <span class="nb">format</span><span class="p">)</span> <span class="k">return</span> <span class="n">c_a_with_fake_globals</span><span class="p">(</span><span class="n">VALUE</span><span class="p">)</span> </pre></div> </div> <p>Here’s what a Python compiler-generated <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> method might look like if it was written in Python:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">__annotate__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="nb">format</span><span class="p">):</span> <span class="k">if</span> <span class="nb">format</span> <span class="o">!=</span> <span class="mi">1</span><span class="p">:</span> <span class="k">raise</span> <span class="ne">NotImplementedError</span><span class="p">()</span> <span class="k">return</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span> </pre></div> </div> <p>Here’s how a third-party wrapper class might implement <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code>. In this example, the wrapper works like <code class="docutils literal notranslate"><span class="pre">functools.partial</span></code>, pre-binding one parameter of the wrapped callable, which for simplicity must be named <code class="docutils literal notranslate"><span class="pre">arg</span></code>:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">__annotate__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="nb">format</span><span class="p">):</span> <span class="n">ann</span> <span class="o">=</span> <span class="n">inspect</span><span class="o">.</span><span class="n">get_annotations</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">wrapped_fn</span><span class="p">,</span> <span class="nb">format</span><span class="p">)</span> <span class="k">if</span> <span class="s1">&#39;arg&#39;</span> <span class="ow">in</span> <span class="n">ann</span><span class="p">:</span> <span class="k">del</span> <span class="n">ann</span><span class="p">[</span><span class="s1">&#39;arg&#39;</span><span class="p">]</span> <span class="k">return</span> <span class="n">ann</span> </pre></div> </div> </section> <section id="other-modifications-to-the-python-runtime"> <h3><a class="toc-backref" href="#other-modifications-to-the-python-runtime" role="doc-backlink">Other modifications to the Python runtime</a></h3> <p>This PEP does not dictate exactly how it should be implemented; that is left up to the language implementation maintainers. However, the best implementation of this PEP may require adding additional information to existing Python objects, which is implicitly condoned by the acceptance of this PEP.</p> <p>For example, it may be necessary to add a <code class="docutils literal notranslate"><span class="pre">__globals__</span></code> attribute to class objects, so that the <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> function for that class can be lazily bound, only on demand. Also, <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> functions defined on methods defined in a class may need to retain a reference to the class’s <code class="docutils literal notranslate"><span class="pre">__dict__</span></code>, in order to correctly evaluate names bound in that class. It’s expected that the CPython implementation of this PEP will include both those new attributes.</p> <p>All such new information added to existing Python objects should be done with “dunder” attributes, as they will of course be implementation details.</p> </section> <section id="interactive-repl-shell"> <h3><a class="toc-backref" href="#interactive-repl-shell" role="doc-backlink">Interactive REPL Shell</a></h3> <p>The semantics established in this PEP also hold true when executing code in Python’s interactive REPL shell, except for module annotations in the interactive module (<code class="docutils literal notranslate"><span class="pre">__main__</span></code>) itself. Since that module is never “finished”, there’s no specific point where we can compile the <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> function.</p> <p>For the sake of simplicity, in this case we forego delayed evaluation. Module-level annotations in the REPL shell will continue to work exactly as they do with “stock semantics”, evaluating immediately and setting the result directly inside the <code class="docutils literal notranslate"><span class="pre">__annotations__</span></code> dict.</p> </section> <section id="annotations-on-local-variables-inside-functions"> <h3><a class="toc-backref" href="#annotations-on-local-variables-inside-functions" role="doc-backlink">Annotations On Local Variables Inside Functions</a></h3> <p>Python supports syntax for local variable annotations inside functions. However, these annotations have no runtime effect–they’re discarded at compile-time. Therefore, this PEP doesn’t need to do anything to support them, the same as stock semantics and <a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a>.</p> </section> <section id="prototype"> <h3><a class="toc-backref" href="#prototype" role="doc-backlink">Prototype</a></h3> <p>The original prototype implementation of this PEP can be found here:</p> <p><a class="reference external" href="https://github.com/larryhastings/co_annotations/">https://github.com/larryhastings/co_annotations/</a></p> <p>As of this writing, the implementation is severely out of date; it’s based on Python 3.10 and implements the semantics of the first draft of this PEP, from early 2021. It will be updated shortly.</p> </section> <section id="performance-comparison"> <h3><a class="toc-backref" href="#performance-comparison" role="doc-backlink">Performance Comparison</a></h3> <p>Performance with this PEP is generally favorable. There are four scenarios to consider:</p> <ul class="simple"> <li>the runtime cost when annotations aren’t defined,</li> <li>the runtime cost when annotations are defined but <em>not</em> referenced, and</li> <li>the runtime cost when annotations are defined and referenced as objects.</li> <li>the runtime cost when annotations are defined and referenced as strings.</li> </ul> <p>We’ll examine each of these scenarios in the context of all three semantics for annotations: stock, <a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a>, and this PEP.</p> <p>When there are no annotations, all three semantics have the same runtime cost: zero. No annotations dict is created and no code is generated for it. This requires no runtime processor time and consumes no memory.</p> <p>When annotations are defined but not referenced, the runtime cost of Python with this PEP is roughly the same as <a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a>, and improved over stock. The specifics depend on the object being annotated:</p> <ul class="simple"> <li>With stock semantics, the annotations dict is always built, and set as an attribute of the object being annotated.</li> <li>In <a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a> semantics, for function objects, a precompiled constant (a specially constructed tuple) is set as an attribute of the function. For class and module objects, the annotations dict is always built and set as an attribute of the class or module.</li> <li>With this PEP, a single object is set as an attribute of the object being annotated. Most of the time, this object is a constant (a code object), but when the annotations require a class namespace or closure, this object will be a tuple constructed at binding time.</li> </ul> <p>When annotations are both defined and referenced as objects, code using this PEP should be much faster than <a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a>, and be as fast or faster than stock. <a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a> semantics requires invoking <code class="docutils literal notranslate"><span class="pre">eval()</span></code> for every value inside an annotations dict which is enormously slow. And the implementation of this PEP generates measurably more efficient bytecode for class and module annotations than stock semantics; for function annotations, this PEP and stock semantics should be about the same speed.</p> <p>The one case where this PEP will be noticeably slower than <a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a> is when annotations are requested as strings; it’s hard to beat “they are already strings.” But stringified annotations are intended for online documentation use cases, where performance is less likely to be a key factor.</p> <p>Memory use should also be comparable in all three scenarios across all three semantic contexts. In the first and third scenarios, memory usage should be roughly equivalent in all cases. In the second scenario, when annotations are defined but not referenced, using this PEP’s semantics will mean the function/class/module will store one unused code object (possibly bound to an unused function object); with the other two semantics, they’ll store one unused dictionary or constant tuple.</p> </section> </section> <section id="backwards-compatibility"> <h2><a class="toc-backref" href="#backwards-compatibility" role="doc-backlink">Backwards Compatibility</a></h2> <section id="backwards-compatibility-with-stock-semantics"> <h3><a class="toc-backref" href="#backwards-compatibility-with-stock-semantics" role="doc-backlink">Backwards Compatibility With Stock Semantics</a></h3> <p>This PEP preserves nearly all existing behavior of annotations from stock semantics:</p> <ul class="simple"> <li>The format of the annotations dict stored in the <code class="docutils literal notranslate"><span class="pre">__annotations__</span></code> attribute is unchanged. Annotations dicts contain real values, not strings as per <a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a>.</li> <li>Annotations dicts are mutable, and any changes to them are preserved.</li> <li>The <code class="docutils literal notranslate"><span class="pre">__annotations__</span></code> attribute can be explicitly set, and any legal value set this way will be preserved.</li> <li>The <code class="docutils literal notranslate"><span class="pre">__annotations__</span></code> attribute can be deleted using the <code class="docutils literal notranslate"><span class="pre">del</span></code> statement.</li> </ul> <p>Most code that works with stock semantics should continue to work when this PEP is active without any modification necessary. But there are exceptions, as follows.</p> <p>First, there’s a well-known idiom for accessing class annotations which may not work correctly when this PEP is active. The original implementation of class annotations had what can only be called a bug: if a class didn’t define any annotations of its own, but one of its base classes did define annotations, the class would “inherit” those annotations. This behavior was never desirable, so user code found a workaround: instead of accessing the annotations on the class directly via <code class="docutils literal notranslate"><span class="pre">cls.__annotations__</span></code>, code would access the class’s annotations via its dict as in <code class="docutils literal notranslate"><span class="pre">cls.__dict__.get(&quot;__annotations__&quot;,</span> <span class="pre">{})</span></code>. This idiom worked because classes stored their annotations in their <code class="docutils literal notranslate"><span class="pre">__dict__</span></code>, and accessing them this way avoided the lookups in the base classes. The technique relied on implementation details of CPython, so it was never supported behavior–though it was necessary. However, when this PEP is active, a class may have annotations defined but hasn’t yet called <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> and cached the result, in which case this approach would lead to mistakenly assuming the class didn’t have annotations. In any case, the bug was fixed as of Python 3.10, and the idiom should no longer be used. Also as of Python 3.10, there’s an <a class="reference external" href="https://docs.python.org/3/howto/annotations.html">Annotations HOWTO</a> that defines best practices for working with annotations; code that follows these guidelines will work correctly even when this PEP is active, because it suggests using different approaches to get annotations from class objects based on the Python version the code runs under.</p> <p>Since delaying the evaluation of annotations until they are introspected changes the semantics of the language, it’s observable from within the language. Therefore it’s <em>possible</em> to write code that behaves differently based on whether annotations are evaluated at binding time or at access time, e.g.</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">mytype</span> <span class="o">=</span> <span class="nb">str</span> <span class="k">def</span><span class="w"> </span><span class="nf">foo</span><span class="p">(</span><span class="n">a</span><span class="p">:</span><span class="n">mytype</span><span class="p">):</span> <span class="k">pass</span> <span class="n">mytype</span> <span class="o">=</span> <span class="nb">int</span> <span class="nb">print</span><span class="p">(</span><span class="n">foo</span><span class="o">.</span><span class="vm">__annotations__</span><span class="p">[</span><span class="s1">&#39;a&#39;</span><span class="p">])</span> </pre></div> </div> <p>This will print <code class="docutils literal notranslate"><span class="pre">&lt;class</span> <span class="pre">'str'&gt;</span></code> with stock semantics and <code class="docutils literal notranslate"><span class="pre">&lt;class</span> <span class="pre">'int'&gt;</span></code> when this PEP is active. This is therefore a backwards-incompatible change. However, this example is poor programming style, so this change seems acceptable.</p> <p>There are two uncommon interactions possible with class and module annotations that work with stock semantics that would no longer work when this PEP was active. These two interactions would have to be prohibited. The good news is, neither is common, and neither is considered good practice. In fact, they’re rarely seen outside of Python’s own regression test suite. They are:</p> <ul class="simple"> <li><em>Code that sets annotations on module or class attributes from inside any kind of flow control statement.</em> It’s currently possible to set module and class attributes with annotations inside an <code class="docutils literal notranslate"><span class="pre">if</span></code> or <code class="docutils literal notranslate"><span class="pre">try</span></code> statement, and it works as one would expect. It’s untenable to support this behavior when this PEP is active.</li> <li><em>Code in module or class scope that references or modifies the local</em> <code class="docutils literal notranslate"><span class="pre">__annotations__</span></code> <em>dict directly.</em> Currently, when setting annotations on module or class attributes, the generated code simply creates a local <code class="docutils literal notranslate"><span class="pre">__annotations__</span></code> dict, then adds mappings to it as needed. It’s possible for user code to directly modify this dict, though this doesn’t seem to be an intentional feature. Although it would be possible to support this after a fashion once this PEP was active, the semantics would likely be surprising and wouldn’t make anyone happy.</li> </ul> <p>Note that these are both also pain points for static type checkers, and are unsupported by those tools. It seems reasonable to declare that both are at the very least unsupported, and their use results in undefined behavior. It might be worth making a small effort to explicitly prohibit them with compile-time checks.</p> <p>Finally, if this PEP is active, annotation values shouldn’t use the <code class="docutils literal notranslate"><span class="pre">if</span> <span class="pre">/</span> <span class="pre">else</span></code> ternary operator. Although this will work correctly when accessing <code class="docutils literal notranslate"><span class="pre">o.__annotations__</span></code> or requesting <code class="docutils literal notranslate"><span class="pre">inspect.VALUE</span></code> from a helper function, the boolean expression may not compute correctly with <code class="docutils literal notranslate"><span class="pre">inspect.FORWARDREF</span></code> when some names are defined, and would be far less correct with <code class="docutils literal notranslate"><span class="pre">inspect.SOURCE</span></code>.</p> </section> <section id="backwards-compatibility-with-pep-563-semantics"> <h3><a class="toc-backref" href="#backwards-compatibility-with-pep-563-semantics" role="doc-backlink">Backwards Compatibility With PEP 563 Semantics</a></h3> <p><a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a> changed the semantics of annotations. When its semantics are active, annotations must assume they will be evaluated in <em>module-level</em> or <em>class-level</em> scope. They may no longer refer directly to local variables in the current function or an enclosing function. This PEP removes that restriction, and annotations may refer any local variable.</p> <p><a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a> requires using <code class="docutils literal notranslate"><span class="pre">eval</span></code> (or a helper function like <code class="docutils literal notranslate"><span class="pre">typing.get_type_hints</span></code> or <code class="docutils literal notranslate"><span class="pre">inspect.get_annotations</span></code> that uses <code class="docutils literal notranslate"><span class="pre">eval</span></code> for you) to convert stringized annotations into their “real” values. Existing code that activates stringized annotations, and calls <code class="docutils literal notranslate"><span class="pre">eval()</span></code> directly to convert the strings back into real values, can simply remove the <code class="docutils literal notranslate"><span class="pre">eval()</span></code> call. Existing code using a helper function would continue to work unchanged, though use of those functions may become optional.</p> <p>Static typing users often have modules that only contain inert type hint definitions–but no live code. These modules are only needed when running static type checking; they aren’t used at runtime. But under stock semantics, these modules have to be imported in order for the runtime to evaluate and compute the annotations. Meanwhile, these modules often caused circular import problems that could be difficult or even impossible to solve. <a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a> allowed users to solve these circular import problems by doing two things. First, they activated <a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a> in their modules, which meant annotations were constant strings, and didn’t require the real symbols to be defined in order for the annotations to be computable. Second, this permitted users to only import the problematic modules in an <code class="docutils literal notranslate"><span class="pre">if</span> <span class="pre">typing.TYPE_CHECKING</span></code> block. This allowed the static type checkers to import the modules and the type definitions inside, but they wouldn’t be imported at runtime. So far, this approach will work unchanged when this PEP is active; <code class="docutils literal notranslate"><span class="pre">if</span> <span class="pre">typing.TYPE_CHECKING</span></code> is supported behavior.</p> <p>However, some codebases actually <em>did</em> examine their annotations at runtime, even when using the <code class="docutils literal notranslate"><span class="pre">if</span> <span class="pre">typing.TYPE_CHECKING</span></code> technique and not importing definitions used in their annotations. These codebases examined the annotation strings <em>without evaluating them,</em> instead relying on identity checks or simple lexical analysis on the strings.</p> <p>This PEP supports these techniques too. But users will need to port their code to it. First, user code will need to use <code class="docutils literal notranslate"><span class="pre">inspect.get_annotations</span></code> or <code class="docutils literal notranslate"><span class="pre">typing.get_type_hints</span></code> to access the annotations; they won’t be able to simply get the <code class="docutils literal notranslate"><span class="pre">__annotations__</span></code> attribute from their object. Second, they will need to specify either <code class="docutils literal notranslate"><span class="pre">inspect.FORWARDREF</span></code> or <code class="docutils literal notranslate"><span class="pre">inspect.SOURCE</span></code> for the <code class="docutils literal notranslate"><span class="pre">format</span></code> when calling that function. This means the helper function can succeed in producing the annotations dict, even when not all the symbols are defined. Code expecting stringized annotations should work unmodified with <code class="docutils literal notranslate"><span class="pre">inspect.SOURCE</span></code> formatted annotations dicts; however, users should consider switching to <code class="docutils literal notranslate"><span class="pre">inspect.FORWARDREF</span></code>, as it may make their analysis easier.</p> <p>Similarly, <a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a> permitted use of class decorators on annotated classes in a way that hadn’t previously been possible. Some class decorators (e.g. <a class="reference external" href="https://docs.python.org/3/library/dataclasses.html#module-dataclasses" title="(in Python v3.13)"><code class="xref py py-mod docutils literal notranslate"><span class="pre">dataclasses</span></code></a>) examine the annotations on the class. Because class decorators using the <code class="docutils literal notranslate"><span class="pre">&#64;</span></code> decorator syntax are run before the class name is bound, they can cause unsolvable circular-definition problems. If you annotate attributes of a class with references to the class itself, or annotate attributes in multiple classes with circular references to each other, you can’t decorate those classes with the <code class="docutils literal notranslate"><span class="pre">&#64;</span></code> decorator syntax using decorators that examine the annotations. <a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a> allowed this to work, as long as the decorators examined the strings lexically and didn’t use <code class="docutils literal notranslate"><span class="pre">eval</span></code> to evaluate them (or handled the <code class="docutils literal notranslate"><span class="pre">NameError</span></code> with further workarounds). When this PEP is active, decorators will be able to compute the annotations dict in <code class="docutils literal notranslate"><span class="pre">inspect.SOURCE</span></code> or <code class="docutils literal notranslate"><span class="pre">inspect.FORWARDREF</span></code> format using the helper functions. This will permit them to analyze annotations containing undefined symbols, in the format they prefer.</p> <p>Early adopters of <a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a> discovered that “stringized” annotations were useful for automatically-generated documentation. Users experimented with this use case, and Python’s <code class="docutils literal notranslate"><span class="pre">pydoc</span></code> has expressed some interest in this technique. This PEP supports this use case; the code generating the documentation will have to be updated to use a helper function to access the annotations in <code class="docutils literal notranslate"><span class="pre">inspect.SOURCE</span></code> format.</p> <p>Finally, the warnings about using the <code class="docutils literal notranslate"><span class="pre">if</span> <span class="pre">/</span> <span class="pre">else</span></code> ternary operator in annotations apply equally to users of <a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a>. It currently works for them, but could produce incorrect results when requesting some formats from the helper functions.</p> <p>If this PEP is accepted, <a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a> will be deprecated and eventually removed. To facilitate this transition for early adopters of <a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a>, who now depend on its semantics, <code class="docutils literal notranslate"><span class="pre">inspect.get_annotations</span></code> and <code class="docutils literal notranslate"><span class="pre">typing.get_type_hints</span></code> will implement a special affordance.</p> <p>The Python compiler won’t generate annotation code objects for objects defined in a module where <a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a> semantics are active, even if this PEP is accepted. So, under normal circumstances, requesting <code class="docutils literal notranslate"><span class="pre">inspect.SOURCE</span></code> format from a helper function would return an empty dict. As an affordance, to facilitate the transition, if the helper functions detect that an object was defined in a module with <a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a> active, and the user requests <code class="docutils literal notranslate"><span class="pre">inspect.SOURCE</span></code> format, they’ll return the current value of the <code class="docutils literal notranslate"><span class="pre">__annotations__</span></code> dict, which in this case will be the stringized annotations. This will allow <a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a> users who lexically analyze stringized annotations to immediately change over to requesting <code class="docutils literal notranslate"><span class="pre">inspect.SOURCE</span></code> format from the helper functions, which will hopefully smooth their transition away from <a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a>.</p> </section> </section> <section id="rejected-ideas"> <h2><a class="toc-backref" href="#rejected-ideas" role="doc-backlink">Rejected Ideas</a></h2> <section id="just-store-the-strings"> <h3><a class="toc-backref" href="#just-store-the-strings" role="doc-backlink">“Just store the strings”</a></h3> <p>One proposed idea for supporting <code class="docutils literal notranslate"><span class="pre">SOURCE</span></code> format was for the Python compiler to emit the actual source code for the annotation values somewhere, and to furnish that when the user requested <code class="docutils literal notranslate"><span class="pre">SOURCE</span></code> format.</p> <p>This idea wasn’t rejected so much as categorized as “not yet”. We already know we need to support <code class="docutils literal notranslate"><span class="pre">FORWARDREF</span></code> format, and that technique can be adapted to support <code class="docutils literal notranslate"><span class="pre">SOURCE</span></code> format in just a few lines. There are many unanswered questions about this approach:</p> <ul class="simple"> <li>Where would we store the strings? Would they always be loaded when the annotated object was created, or would they be lazy-loaded on demand? If so, how would the lazy-loading work?</li> <li>Would the “source code” include the newlines and comments of the original? Would it preserve all whitespace, including indents and extra spaces used purely for formatting?</li> </ul> <p>It’s possible we’ll revisit this topic in the future, if improving the fidelity of <code class="docutils literal notranslate"><span class="pre">SOURCE</span></code> values to the original source code is judged sufficiently important.</p> </section> </section> <section id="acknowledgements"> <h2><a class="toc-backref" href="#acknowledgements" role="doc-backlink">Acknowledgements</a></h2> <p>Thanks to Carl Meyer, Barry Warsaw, Eric V. Smith, Mark Shannon, Jelle Ziljstra, and Guido van Rossum for ongoing feedback and encouragement.</p> <p>Particular thanks to several individuals who contributed key ideas that became some of the best aspects of this proposal:</p> <ul class="simple"> <li>Carl Meyer suggested the “stringizer” technique that made <code class="docutils literal notranslate"><span class="pre">FORWARDREF</span></code> and <code class="docutils literal notranslate"><span class="pre">SOURCE</span></code> formats possible, which allowed making forward progress on this PEP possible after a year of languishing due to seemingly-unfixable problems. He also suggested the affordance for <a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a> users where <code class="docutils literal notranslate"><span class="pre">inspect.SOURCE</span></code> will return the stringized annotations, and many more suggestions besides. Carl was also the primary correspondent in private email threads discussing this PEP, and was a tireless resource and voice of sanity. This PEP would almost certainly not have been accepted it were it not for Carl’s contributions.</li> <li>Mark Shannon suggested building the entire annotations dict inside a single code object, and only binding it to a function on demand.</li> <li>Guido van Rossum suggested that <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> functions should duplicate the name visibility rules of annotations under “stock” semantics.</li> <li>Jelle Zijlstra contributed not only feedback–but code!</li> </ul> </section> <section id="references"> <h2><a class="toc-backref" href="#references" role="doc-backlink">References</a></h2> <ul class="simple"> <li><a class="reference external" href="https://github.com/larryhastings/co_annotations/issues">https://github.com/larryhastings/co_annotations/issues</a></li> <li><a class="reference external" href="https://discuss.python.org/t/two-polls-on-how-to-revise-pep-649/23628">https://discuss.python.org/t/two-polls-on-how-to-revise-pep-649/23628</a></li> <li><a class="reference external" href="https://discuss.python.org/t/a-massive-pep-649-update-with-some-major-course-corrections/25672">https://discuss.python.org/t/a-massive-pep-649-update-with-some-major-course-corrections/25672</a></li> </ul> </section> <section id="copyright"> <h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2> <p>This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive.</p> </section> </section> <hr class="docutils" /> <p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0649.rst">https://github.com/python/peps/blob/main/peps/pep-0649.rst</a></p> <p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0649.rst">2025-02-01 08:55:40 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="#overview">Overview</a><ul> <li><a class="reference internal" href="#comparison-of-annotation-semantics">Comparison Of Annotation Semantics</a></li> <li><a class="reference internal" href="#mistaken-rejection-of-this-approach-in-november-2017">Mistaken Rejection Of This Approach In November 2017</a></li> </ul> </li> <li><a class="reference internal" href="#motivation">Motivation</a><ul> <li><a class="reference internal" href="#a-history-of-annotations">A History Of Annotations</a></li> <li><a class="reference internal" href="#the-current-state-of-annotation-use-cases">The Current State Of Annotation Use Cases</a><ul> <li><a class="reference internal" href="#static-typing-users">Static typing users</a></li> <li><a class="reference internal" href="#runtime-annotation-users">Runtime annotation users</a></li> <li><a class="reference internal" href="#wrappers">Wrappers</a></li> <li><a class="reference internal" href="#documentation">Documentation</a></li> </ul> </li> <li><a class="reference internal" href="#motivation-for-this-pep">Motivation For This PEP</a></li> </ul> </li> <li><a class="reference internal" href="#implementation">Implementation</a><ul> <li><a class="reference internal" href="#observed-semantics-for-annotations-expressions">Observed semantics for annotations expressions</a></li> <li><a class="reference internal" href="#annotate-and-annotations">__annotate__ and __annotations__</a></li> <li><a class="reference internal" href="#changes-to-allowable-annotations-syntax">Changes to allowable annotations syntax</a></li> <li><a class="reference internal" href="#changes-to-inspect-get-annotations-and-typing-get-type-hints">Changes to <code class="docutils literal notranslate"><span class="pre">inspect.get_annotations</span></code> and <code class="docutils literal notranslate"><span class="pre">typing.get_type_hints</span></code></a></li> <li><a class="reference internal" href="#the-stringizer-and-the-fake-globals-environment">The <code class="docutils literal notranslate"><span class="pre">stringizer</span></code> and the <code class="docutils literal notranslate"><span class="pre">fake</span> <span class="pre">globals</span></code> environment</a></li> <li><a class="reference internal" href="#compiler-generated-annotate-functions">Compiler-generated <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> functions</a></li> <li><a class="reference internal" href="#third-party-annotate-functions">Third-party <code class="docutils literal notranslate"><span class="pre">__annotate__</span></code> functions</a></li> <li><a class="reference internal" href="#pseudocode">Pseudocode</a></li> <li><a class="reference internal" href="#other-modifications-to-the-python-runtime">Other modifications to the Python runtime</a></li> <li><a class="reference internal" href="#interactive-repl-shell">Interactive REPL Shell</a></li> <li><a class="reference internal" href="#annotations-on-local-variables-inside-functions">Annotations On Local Variables Inside Functions</a></li> <li><a class="reference internal" href="#prototype">Prototype</a></li> <li><a class="reference internal" href="#performance-comparison">Performance Comparison</a></li> </ul> </li> <li><a class="reference internal" href="#backwards-compatibility">Backwards Compatibility</a><ul> <li><a class="reference internal" href="#backwards-compatibility-with-stock-semantics">Backwards Compatibility With Stock Semantics</a></li> <li><a class="reference internal" href="#backwards-compatibility-with-pep-563-semantics">Backwards Compatibility With PEP 563 Semantics</a></li> </ul> </li> <li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a><ul> <li><a class="reference internal" href="#just-store-the-strings">“Just store the strings”</a></li> </ul> </li> <li><a class="reference internal" href="#acknowledgements">Acknowledgements</a></li> <li><a class="reference internal" href="#references">References</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-0649.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>

Pages: 1 2 3 4 5 6 7 8 9 10