CINXE.COM

PEP 746 – Type checking Annotated metadata | 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 746 – Type checking Annotated metadata | peps.python.org</title> <link rel="shortcut icon" href="../_static/py.png"> <link rel="canonical" href="https://peps.python.org/pep-0746/"> <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 746 – Type checking Annotated metadata | peps.python.org'> <meta property="og:description" content="This PEP proposes a mechanism for type checking metadata that uses the typing.Annotated type. Metadata objects that implement the new __supports_annotated_base__ protocol will be type checked by static type checkers to ensure that the metadata is valid ..."> <meta property="og:type" content="website"> <meta property="og:url" content="https://peps.python.org/pep-0746/"> <meta property="og:site_name" content="Python Enhancement Proposals (PEPs)"> <meta property="og:image" content="https://peps.python.org/_static/og-image.png"> <meta property="og:image:alt" content="Python PEPs"> <meta property="og:image:width" content="200"> <meta property="og:image:height" content="200"> <meta name="description" content="This PEP proposes a mechanism for type checking metadata that uses the typing.Annotated type. Metadata objects that implement the new __supports_annotated_base__ protocol will be type checked by static type checkers to ensure that the metadata is valid ..."> <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 746</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 746 – Type checking Annotated metadata</h1> <dl class="rfc2822 field-list simple"> <dt class="field-odd">Author<span class="colon">:</span></dt> <dd class="field-odd">Adrian Garcia Badaracco &lt;adrian&#32;&#97;t&#32;adriangb.com&gt;</dd> <dt class="field-even">Sponsor<span class="colon">:</span></dt> <dd class="field-even">Jelle Zijlstra &lt;jelle.zijlstra&#32;&#97;t&#32;gmail.com&gt;</dd> <dt class="field-odd">Discussions-To<span class="colon">:</span></dt> <dd class="field-odd"><a class="reference external" href="https://discuss.python.org/t/pep-746-typedmetadata-for-type-checking-of-pep-593-annotated/53834">Discourse thread</a></dd> <dt class="field-even">Status<span class="colon">:</span></dt> <dd class="field-even"><abbr title="Proposal under active discussion and revision">Draft</abbr></dd> <dt class="field-odd">Type<span class="colon">:</span></dt> <dd class="field-odd"><abbr title="Normative PEP with a new feature for Python, implementation change for CPython or interoperability standard for the ecosystem">Standards Track</abbr></dd> <dt class="field-even">Topic<span class="colon">:</span></dt> <dd class="field-even"><a class="reference external" href="../topic/typing/">Typing</a></dd> <dt class="field-odd">Created<span class="colon">:</span></dt> <dd class="field-odd">20-May-2024</dd> <dt class="field-even">Python-Version<span class="colon">:</span></dt> <dd class="field-even">3.14</dd> <dt class="field-odd">Post-History<span class="colon">:</span></dt> <dd class="field-odd">20-May-2024</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="#motivation">Motivation</a></li> <li><a class="reference internal" href="#specification">Specification</a></li> <li><a class="reference internal" href="#backwards-compatibility">Backwards Compatibility</a></li> <li><a class="reference internal" href="#security-implications">Security Implications</a></li> <li><a class="reference internal" href="#how-to-teach-this">How to Teach This</a></li> <li><a class="reference internal" href="#reference-implementation">Reference Implementation</a></li> <li><a class="reference internal" href="#rejected-ideas">Rejected ideas</a><ul> <li><a class="reference internal" href="#introducing-a-type-variable-instead-of-a-generic-class">Introducing a type variable instead of a generic class</a></li> <li><a class="reference internal" href="#introducing-a-new-type-to-typing-py-that-all-metadata-objects-should-subclass">Introducing a new type to <code class="docutils literal notranslate"><span class="pre">typing.py</span></code> that all metadata objects should subclass</a></li> <li><a class="reference internal" href="#using-a-method-instead-of-an-attribute-for-supports-annotated-base">Using a method instead of an attribute for <code class="docutils literal notranslate"><span class="pre">__supports_annotated_base__</span></code></a></li> </ul> </li> <li><a class="reference internal" href="#acknowledgments">Acknowledgments</a></li> <li><a class="reference internal" href="#copyright">Copyright</a></li> </ul> </details></section> <section id="abstract"> <h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2> <p>This PEP proposes a mechanism for type checking metadata that uses the <a class="reference external" href="https://docs.python.org/3/library/typing.html#typing.Annotated" title="(in Python v3.13)"><code class="xref py py-data docutils literal notranslate"><span class="pre">typing.Annotated</span></code></a> type. Metadata objects that implement the new <code class="docutils literal notranslate"><span class="pre">__supports_annotated_base__</span></code> protocol will be type checked by static type checkers to ensure that the metadata is valid for the given type.</p> </section> <section id="motivation"> <h2><a class="toc-backref" href="#motivation" role="doc-backlink">Motivation</a></h2> <p><a class="pep reference internal" href="../pep-0593/" title="PEP 593 – Flexible function and variable annotations">PEP 593</a> introduced <code class="docutils literal notranslate"><span class="pre">Annotated</span></code> as a way to attach runtime metadata to types. In general, the metadata is not meant for static type checkers, but even so, it is often useful to be able to check that the metadata makes sense for the given type.</p> <p>Take the first example in <a class="pep reference internal" href="../pep-0593/" title="PEP 593 – Flexible function and variable annotations">PEP 593</a>, which uses <code class="docutils literal notranslate"><span class="pre">Annotated</span></code> to attach serialization information to a field:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">Student</span><span class="p">(</span><span class="n">struct2</span><span class="o">.</span><span class="n">Packed</span><span class="p">):</span> <span class="n">name</span><span class="p">:</span> <span class="n">Annotated</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">struct2</span><span class="o">.</span><span class="n">ctype</span><span class="p">(</span><span class="s2">&quot;&lt;10s&quot;</span><span class="p">)]</span> </pre></div> </div> <p>Here, the <code class="docutils literal notranslate"><span class="pre">struct2.ctype(&quot;&lt;10s&quot;)</span></code> metadata is meant to be used by a serialization library to serialize the field. Such libraries can only serialize a subset of types: it would not make sense to write, for example, <code class="docutils literal notranslate"><span class="pre">Annotated[list[str],</span> <span class="pre">struct2.ctype(&quot;&lt;10s&quot;)]</span></code>. Yet the type system provides no way to enforce this. The metadata are completely ignored by type checkers.</p> <p>This use case comes up in libraries like <a class="extlink-pypi reference external" href="https://pypi.org/project/pydantic/">pydantic</a> and <a class="extlink-pypi reference external" href="https://pypi.org/project/msgspec/">msgspec</a>, which use <code class="docutils literal notranslate"><span class="pre">Annotated</span></code> to attach validation and conversion information to fields or <a class="extlink-pypi reference external" href="https://pypi.org/project/fastapi/">fastapi</a>, which uses <code class="docutils literal notranslate"><span class="pre">Annotated</span></code> to mark parameters as extracted from headers, query strings or dependency injection.</p> </section> <section id="specification"> <h2><a class="toc-backref" href="#specification" role="doc-backlink">Specification</a></h2> <p>This PEP introduces a protocol that can be used by static and runtime type checkers to validate the consistency between <code class="docutils literal notranslate"><span class="pre">Annotated</span></code> metadata and a given type. Objects that implement this protocol have an attribute called <code class="docutils literal notranslate"><span class="pre">__supports_annotated_base__</span></code> that specifies whether the metadata is valid for a given type:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">Int64</span><span class="p">:</span> <span class="n">__supports_annotated_base__</span><span class="p">:</span> <span class="nb">int</span> </pre></div> </div> <p>The attribute may also be marked as a <code class="docutils literal notranslate"><span class="pre">ClassVar</span></code> to avoid interaction with dataclasses:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">dataclasses</span><span class="w"> </span><span class="kn">import</span> <span class="n">dataclass</span> <span class="kn">from</span><span class="w"> </span><span class="nn">typing</span><span class="w"> </span><span class="kn">import</span> <span class="n">ClassVar</span> <span class="nd">@dataclass</span> <span class="k">class</span><span class="w"> </span><span class="nc">Gt</span><span class="p">:</span> <span class="n">value</span><span class="p">:</span> <span class="nb">int</span> <span class="n">__supports_annotated_base__</span><span class="p">:</span> <span class="n">ClassVar</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span> </pre></div> </div> <p>When a static type checker encounters a type expression of the form <code class="docutils literal notranslate"><span class="pre">Annotated[T,</span> <span class="pre">M1,</span> <span class="pre">M2,</span> <span class="pre">...]</span></code>, it should enforce that for each metadata element in <code class="docutils literal notranslate"><span class="pre">M1,</span> <span class="pre">M2,</span> <span class="pre">...</span></code>, one of the following is true:</p> <ul class="simple"> <li>The metadata element evaluates to an object that does not have a <code class="docutils literal notranslate"><span class="pre">__supports_annotated_base__</span></code> attribute; or</li> <li>The metadata element evaluates to an object <code class="docutils literal notranslate"><span class="pre">M</span></code> that has a <code class="docutils literal notranslate"><span class="pre">__supports_annotated_base__</span></code> attribute; and <code class="docutils literal notranslate"><span class="pre">T</span></code> is assignable to the type of <code class="docutils literal notranslate"><span class="pre">M.__supports_annotated_base__</span></code>.</li> </ul> <p>To support generic <code class="docutils literal notranslate"><span class="pre">Gt</span></code> metadata, one might write:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">typing</span><span class="w"> </span><span class="kn">import</span> <span class="n">Protocol</span> <span class="k">class</span><span class="w"> </span><span class="nc">SupportsGt</span><span class="p">[</span><span class="n">T</span><span class="p">](</span><span class="n">Protocol</span><span class="p">):</span> <span class="k">def</span><span class="w"> </span><span class="fm">__gt__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">__other</span><span class="p">:</span> <span class="n">T</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">bool</span><span class="p">:</span> <span class="o">...</span> <span class="k">class</span><span class="w"> </span><span class="nc">Gt</span><span class="p">[</span><span class="n">T</span><span class="p">]:</span> <span class="n">__supports_annotated_base__</span><span class="p">:</span> <span class="n">ClassVar</span><span class="p">[</span><span class="n">SupportsGt</span><span class="p">[</span><span class="n">T</span><span class="p">]]</span> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">:</span> <span class="n">T</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="n">value</span> <span class="n">x1</span><span class="p">:</span> <span class="n">Annotated</span><span class="p">[</span><span class="nb">int</span><span class="p">,</span> <span class="n">Gt</span><span class="p">(</span><span class="mi">0</span><span class="p">)]</span> <span class="o">=</span> <span class="mi">1</span> <span class="c1"># OK</span> <span class="n">x2</span><span class="p">:</span> <span class="n">Annotated</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">Gt</span><span class="p">(</span><span class="mi">0</span><span class="p">)]</span> <span class="o">=</span> <span class="mi">0</span> <span class="c1"># type checker error: str is not assignable to SupportsGt[int]</span> <span class="n">x3</span><span class="p">:</span> <span class="n">Annotated</span><span class="p">[</span><span class="nb">int</span><span class="p">,</span> <span class="n">Gt</span><span class="p">(</span><span class="mi">1</span><span class="p">)]</span> <span class="o">=</span> <span class="mi">0</span> <span class="c1"># OK for static type checkers; runtime type checkers may flag this</span> </pre></div> </div> </section> <section id="backwards-compatibility"> <h2><a class="toc-backref" href="#backwards-compatibility" role="doc-backlink">Backwards Compatibility</a></h2> <p>Metadata that does not implement the protocol will be considered valid for all types, so no breaking changes are introduced for existing code. The new checks only apply to metadata objects that explicitly implement the protocol specified by this PEP.</p> </section> <section id="security-implications"> <h2><a class="toc-backref" href="#security-implications" role="doc-backlink">Security Implications</a></h2> <p>None.</p> </section> <section id="how-to-teach-this"> <h2><a class="toc-backref" href="#how-to-teach-this" role="doc-backlink">How to Teach This</a></h2> <p>This protocol is intended mostly for libraries that provide <code class="docutils literal notranslate"><span class="pre">Annotated</span></code> metadata; end users of those libraries are unlikely to need to implement the protocol themselves. The protocol should be mentioned in the documentation for <a class="reference external" href="https://docs.python.org/3/library/typing.html#typing.Annotated" title="(in Python v3.13)"><code class="xref py py-data docutils literal notranslate"><span class="pre">typing.Annotated</span></code></a> and in the typing specification.</p> </section> <section id="reference-implementation"> <h2><a class="toc-backref" href="#reference-implementation" role="doc-backlink">Reference Implementation</a></h2> <p>None yet.</p> </section> <section id="rejected-ideas"> <h2><a class="toc-backref" href="#rejected-ideas" role="doc-backlink">Rejected ideas</a></h2> <section id="introducing-a-type-variable-instead-of-a-generic-class"> <h3><a class="toc-backref" href="#introducing-a-type-variable-instead-of-a-generic-class" role="doc-backlink">Introducing a type variable instead of a generic class</a></h3> <p>We considered using a special type variable, <code class="docutils literal notranslate"><span class="pre">AnnotatedT</span> <span class="pre">=</span> <span class="pre">TypeVar(&quot;AnnotatedT&quot;)</span></code>, to represent the type <code class="docutils literal notranslate"><span class="pre">T</span></code> of the inner type in <code class="docutils literal notranslate"><span class="pre">Annotated</span></code>; metadata would be type checked against this type variable. However, this would require using the old type variable syntax (before <a class="pep reference internal" href="../pep-0695/" title="PEP 695 – Type Parameter Syntax">PEP 695</a>), which is now a discouraged feature. In addition, this would use type variables in an unusual way that does not fit well with the rest of the type system.</p> </section> <section id="introducing-a-new-type-to-typing-py-that-all-metadata-objects-should-subclass"> <h3><a class="toc-backref" href="#introducing-a-new-type-to-typing-py-that-all-metadata-objects-should-subclass" role="doc-backlink">Introducing a new type to <code class="docutils literal notranslate"><span class="pre">typing.py</span></code> that all metadata objects should subclass</a></h3> <p>A previous version of this PEP suggested adding a new generic base class, <code class="docutils literal notranslate"><span class="pre">TypedMetadata[U]</span></code>, that metadata objects would subclass. If a metadata object is a subclass of <code class="docutils literal notranslate"><span class="pre">TypedMetadata[U]</span></code>, then type checkers would check that the annotation’s base type is assignable to <code class="docutils literal notranslate"><span class="pre">U</span></code>. However, this mechanism does not integrate as well with the rest of the language; Python does not generally use marker base classes. In addition, it provides less flexibility than the current proposal: it would not allow overloads, and it would require metadata objects to add a new base class, which may make their runtime implementation more complex.</p> </section> <section id="using-a-method-instead-of-an-attribute-for-supports-annotated-base"> <h3><a class="toc-backref" href="#using-a-method-instead-of-an-attribute-for-supports-annotated-base" role="doc-backlink">Using a method instead of an attribute for <code class="docutils literal notranslate"><span class="pre">__supports_annotated_base__</span></code></a></h3> <p>We considered using a method instead of an attribute for the protocol, so that this method can be used at runtime to check the validity of the metadata and to support overloads or returning boolean literals. However, using a method adds boilerplate to the implementation and the value of the runtime use cases or more complex scenarios involving overloads and returning boolean literals was not clear.</p> </section> </section> <section id="acknowledgments"> <h2><a class="toc-backref" href="#acknowledgments" role="doc-backlink">Acknowledgments</a></h2> <p>We thank Eric Traut for suggesting the idea of using a protocol and implementing provisional support in Pyright. Thank you to Jelle Zijlstra for sponsoring this PEP.</p> </section> <section id="copyright"> <h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2> <p>This document has been placed in the public domain.</p> </section> </section> <hr class="docutils" /> <p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0746.rst">https://github.com/python/peps/blob/main/peps/pep-0746.rst</a></p> <p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0746.rst">2025-02-01 07:28:42 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="#motivation">Motivation</a></li> <li><a class="reference internal" href="#specification">Specification</a></li> <li><a class="reference internal" href="#backwards-compatibility">Backwards Compatibility</a></li> <li><a class="reference internal" href="#security-implications">Security Implications</a></li> <li><a class="reference internal" href="#how-to-teach-this">How to Teach This</a></li> <li><a class="reference internal" href="#reference-implementation">Reference Implementation</a></li> <li><a class="reference internal" href="#rejected-ideas">Rejected ideas</a><ul> <li><a class="reference internal" href="#introducing-a-type-variable-instead-of-a-generic-class">Introducing a type variable instead of a generic class</a></li> <li><a class="reference internal" href="#introducing-a-new-type-to-typing-py-that-all-metadata-objects-should-subclass">Introducing a new type to <code class="docutils literal notranslate"><span class="pre">typing.py</span></code> that all metadata objects should subclass</a></li> <li><a class="reference internal" href="#using-a-method-instead-of-an-attribute-for-supports-annotated-base">Using a method instead of an attribute for <code class="docutils literal notranslate"><span class="pre">__supports_annotated_base__</span></code></a></li> </ul> </li> <li><a class="reference internal" href="#acknowledgments">Acknowledgments</a></li> <li><a class="reference internal" href="#copyright">Copyright</a></li> </ul> <br> <a id="source" href="https://github.com/python/peps/blob/main/peps/pep-0746.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