780 lines
64 KiB
HTML
780 lines
64 KiB
HTML
|
||
<!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 563 – Postponed Evaluation of Annotations | peps.python.org</title>
|
||
<link rel="shortcut icon" href="../_static/py.png">
|
||
<link rel="canonical" href="https://peps.python.org/pep-0563/">
|
||
<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 563 – Postponed Evaluation of Annotations | peps.python.org'>
|
||
<meta property="og:description" content="PEP 3107 introduced syntax for function annotations, but the semantics were deliberately left undefined. PEP 484 introduced a standard meaning to annotations: type hints. PEP 526 defined variable annotations, explicitly tying them with the type hintin...">
|
||
<meta property="og:type" content="website">
|
||
<meta property="og:url" content="https://peps.python.org/pep-0563/">
|
||
<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="PEP 3107 introduced syntax for function annotations, but the semantics were deliberately left undefined. PEP 484 introduced a standard meaning to annotations: type hints. PEP 526 defined variable annotations, explicitly tying them with the type hintin...">
|
||
<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> » </li>
|
||
<li><a href="../pep-0000/">PEP Index</a> » </li>
|
||
<li>PEP 563</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 563 – Postponed Evaluation of Annotations</h1>
|
||
<dl class="rfc2822 field-list simple">
|
||
<dt class="field-odd">Author<span class="colon">:</span></dt>
|
||
<dd class="field-odd">Łukasz Langa <lukasz at python.org></dd>
|
||
<dt class="field-even">Discussions-To<span class="colon">:</span></dt>
|
||
<dd class="field-even"><a class="reference external" href="https://mail.python.org/archives/list/python-dev@python.org/">Python-Dev list</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">08-Sep-2017</dd>
|
||
<dt class="field-odd">Python-Version<span class="colon">:</span></dt>
|
||
<dd class="field-odd">3.7</dd>
|
||
<dt class="field-even">Post-History<span class="colon">:</span></dt>
|
||
<dd class="field-even">01-Nov-2017, 21-Nov-2017</dd>
|
||
<dt class="field-odd">Superseded-By<span class="colon">:</span></dt>
|
||
<dd class="field-odd"><a class="reference external" href="../pep-0649/">649</a></dd>
|
||
<dt class="field-even">Resolution<span class="colon">:</span></dt>
|
||
<dd class="field-even"><a class="reference external" href="https://mail.python.org/pipermail/python-dev/2017-December/151042.html">Python-Dev message</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="#rationale-and-goals">Rationale and Goals</a><ul>
|
||
<li><a class="reference internal" href="#non-goals">Non-goals</a></li>
|
||
<li><a class="reference internal" href="#non-typing-usage-of-annotations">Non-typing usage of annotations</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#implementation">Implementation</a><ul>
|
||
<li><a class="reference internal" href="#enabling-the-future-behavior-in-python-3-7">Enabling the future behavior in Python 3.7</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#resolving-type-hints-at-runtime">Resolving Type Hints at Runtime</a><ul>
|
||
<li><a class="reference internal" href="#runtime-annotation-resolution-and-class-decorators">Runtime annotation resolution and class decorators</a></li>
|
||
<li><a class="reference internal" href="#runtime-annotation-resolution-and-type-checking">Runtime annotation resolution and <code class="docutils literal notranslate"><span class="pre">TYPE_CHECKING</span></code></a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#backwards-compatibility">Backwards Compatibility</a><ul>
|
||
<li><a class="reference internal" href="#deprecation-policy">Deprecation policy</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#forward-references">Forward References</a></li>
|
||
<li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a><ul>
|
||
<li><a class="reference internal" href="#keeping-the-ability-to-use-function-local-state-when-defining-annotations">Keeping the ability to use function local state when defining annotations</a></li>
|
||
<li><a class="reference internal" href="#disallowing-local-state-usage-for-classes-too">Disallowing local state usage for classes, too</a></li>
|
||
<li><a class="reference internal" href="#introducing-a-new-dictionary-for-the-string-literal-form-instead">Introducing a new dictionary for the string literal form instead</a></li>
|
||
<li><a class="reference internal" href="#dropping-annotations-with-o">Dropping annotations with -O</a></li>
|
||
<li><a class="reference internal" href="#passing-string-literals-in-annotations-verbatim-to-annotations">Passing string literals in annotations verbatim to <code class="docutils literal notranslate"><span class="pre">__annotations__</span></code></a></li>
|
||
<li><a class="reference internal" href="#making-the-name-of-the-future-import-more-verbose">Making the name of the future import more verbose</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#prior-discussion">Prior discussion</a><ul>
|
||
<li><a class="reference internal" href="#in-pep-484">In PEP 484</a></li>
|
||
<li><a class="reference internal" href="#python-typing-400">python/typing#400</a></li>
|
||
<li><a class="reference internal" href="#first-draft-discussion-on-python-ideas">First draft discussion on python-ideas</a></li>
|
||
<li><a class="reference internal" href="#second-draft-discussion-on-python-dev">Second draft discussion on python-dev</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#acknowledgements">Acknowledgements</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><a class="pep reference internal" href="../pep-3107/" title="PEP 3107 – Function Annotations">PEP 3107</a> introduced syntax for function annotations, but the semantics
|
||
were deliberately left undefined. <a class="pep reference internal" href="../pep-0484/" title="PEP 484 – Type Hints">PEP 484</a> introduced a standard meaning
|
||
to annotations: type hints. <a class="pep reference internal" href="../pep-0526/" title="PEP 526 – Syntax for Variable Annotations">PEP 526</a> defined variable annotations,
|
||
explicitly tying them with the type hinting use case.</p>
|
||
<p>This PEP proposes changing function annotations and variable annotations
|
||
so that they are no longer evaluated at function definition time.
|
||
Instead, they are preserved in <code class="docutils literal notranslate"><span class="pre">__annotations__</span></code> in string form.</p>
|
||
<p>This change is being introduced gradually, starting with a
|
||
<code class="docutils literal notranslate"><span class="pre">__future__</span></code> import in Python 3.7.</p>
|
||
</section>
|
||
<section id="rationale-and-goals">
|
||
<h2><a class="toc-backref" href="#rationale-and-goals" role="doc-backlink">Rationale and Goals</a></h2>
|
||
<p><a class="pep reference internal" href="../pep-3107/" title="PEP 3107 – Function Annotations">PEP 3107</a> added support for arbitrary annotations on parts of a function
|
||
definition. Just like default values, annotations are evaluated at
|
||
function definition time. This creates a number of issues for the type
|
||
hinting use case:</p>
|
||
<ul class="simple">
|
||
<li>forward references: when a type hint contains names that have not been
|
||
defined yet, that definition needs to be expressed as a string
|
||
literal;</li>
|
||
<li>type hints are executed at module import time, which is not
|
||
computationally free.</li>
|
||
</ul>
|
||
<p>Postponing the evaluation of annotations solves both problems.
|
||
NOTE: <a class="pep reference internal" href="../pep-0649/" title="PEP 649 – Deferred Evaluation Of Annotations Using Descriptors">PEP 649</a> proposes an alternative solution to the above issues,
|
||
putting this PEP in danger of being superseded.</p>
|
||
<section id="non-goals">
|
||
<h3><a class="toc-backref" href="#non-goals" role="doc-backlink">Non-goals</a></h3>
|
||
<p>Just like in <a class="pep reference internal" href="../pep-0484/" title="PEP 484 – Type Hints">PEP 484</a> and <a class="pep reference internal" href="../pep-0526/" title="PEP 526 – Syntax for Variable Annotations">PEP 526</a>, it should be emphasized that <strong>Python
|
||
will remain a dynamically typed language, and the authors have no desire
|
||
to ever make type hints mandatory, even by convention.</strong></p>
|
||
<p>This PEP is meant to solve the problem of forward references in type
|
||
annotations. There are still cases outside of annotations where
|
||
forward references will require usage of string literals. Those are
|
||
listed in a later section of this document.</p>
|
||
<p>Annotations without forced evaluation enable opportunities to improve
|
||
the syntax of type hints. This idea will require its own separate PEP
|
||
and is not discussed further in this document.</p>
|
||
</section>
|
||
<section id="non-typing-usage-of-annotations">
|
||
<h3><a class="toc-backref" href="#non-typing-usage-of-annotations" role="doc-backlink">Non-typing usage of annotations</a></h3>
|
||
<p>While annotations are still available for arbitrary use besides type
|
||
checking, it is worth mentioning that the design of this PEP, as well
|
||
as its precursors (<a class="pep reference internal" href="../pep-0484/" title="PEP 484 – Type Hints">PEP 484</a> and <a class="pep reference internal" href="../pep-0526/" title="PEP 526 – Syntax for Variable Annotations">PEP 526</a>), is predominantly motivated by
|
||
the type hinting use case.</p>
|
||
<p>In Python 3.8 <a class="pep reference internal" href="../pep-0484/" title="PEP 484 – Type Hints">PEP 484</a> will graduate from provisional status. Other
|
||
enhancements to the Python programming language like <a class="pep reference internal" href="../pep-0544/" title="PEP 544 – Protocols: Structural subtyping (static duck typing)">PEP 544</a>, <a class="pep reference internal" href="../pep-0557/" title="PEP 557 – Data Classes">PEP 557</a>,
|
||
or <a class="pep reference internal" href="../pep-0560/" title="PEP 560 – Core support for typing module and generic types">PEP 560</a>, are already being built on this basis as they depend on
|
||
type annotations and the <code class="docutils literal notranslate"><span class="pre">typing</span></code> module as defined by <a class="pep reference internal" href="../pep-0484/" title="PEP 484 – Type Hints">PEP 484</a>.
|
||
In fact, the reason <a class="pep reference internal" href="../pep-0484/" title="PEP 484 – Type Hints">PEP 484</a> is staying provisional in Python 3.7 is to
|
||
enable rapid evolution for another release cycle that some of the
|
||
aforementioned enhancements require.</p>
|
||
<p>With this in mind, uses for annotations incompatible with the
|
||
aforementioned PEPs should be considered deprecated.</p>
|
||
</section>
|
||
</section>
|
||
<section id="implementation">
|
||
<h2><a class="toc-backref" href="#implementation" role="doc-backlink">Implementation</a></h2>
|
||
<p>With this PEP, function and variable annotations will no longer be
|
||
evaluated at definition time. Instead, a string form will be preserved
|
||
in the respective <code class="docutils literal notranslate"><span class="pre">__annotations__</span></code> dictionary. Static type checkers
|
||
will see no difference in behavior, whereas tools using annotations at
|
||
runtime will have to perform postponed evaluation.</p>
|
||
<p>The string form is obtained from the AST during the compilation step,
|
||
which means that the string form might not preserve the exact formatting
|
||
of the source. Note: if an annotation was a string literal already, it
|
||
will still be wrapped in a string.</p>
|
||
<p>Annotations need to be syntactically valid Python expressions, also when
|
||
passed as literal strings (i.e. <code class="docutils literal notranslate"><span class="pre">compile(literal,</span> <span class="pre">'',</span> <span class="pre">'eval')</span></code>).
|
||
Annotations can only use names present in the module scope as postponed
|
||
evaluation using local names is not reliable (with the sole exception of
|
||
class-level names resolved by <code class="docutils literal notranslate"><span class="pre">typing.get_type_hints()</span></code>).</p>
|
||
<p>Note that as per <a class="pep reference internal" href="../pep-0526/" title="PEP 526 – Syntax for Variable Annotations">PEP 526</a>, local variable annotations are not evaluated
|
||
at all since they are not accessible outside of the function’s closure.</p>
|
||
<section id="enabling-the-future-behavior-in-python-3-7">
|
||
<h3><a class="toc-backref" href="#enabling-the-future-behavior-in-python-3-7" role="doc-backlink">Enabling the future behavior in Python 3.7</a></h3>
|
||
<p>The functionality described above can be enabled starting from Python
|
||
3.7 using the following special import:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">__future__</span> <span class="kn">import</span> <span class="n">annotations</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>A reference implementation of this functionality is available
|
||
<a class="reference external" href="https://github.com/python/cpython/pull/4390">on GitHub</a>.</p>
|
||
</section>
|
||
</section>
|
||
<section id="resolving-type-hints-at-runtime">
|
||
<h2><a class="toc-backref" href="#resolving-type-hints-at-runtime" role="doc-backlink">Resolving Type Hints at Runtime</a></h2>
|
||
<p>To resolve an annotation at runtime from its string form to the result
|
||
of the enclosed expression, user code needs to evaluate the string.</p>
|
||
<p>For code that uses type hints, the
|
||
<code class="docutils literal notranslate"><span class="pre">typing.get_type_hints(obj,</span> <span class="pre">globalns=None,</span> <span class="pre">localns=None)</span></code> function
|
||
correctly evaluates expressions back from its string form. Note that
|
||
all valid code currently using <code class="docutils literal notranslate"><span class="pre">__annotations__</span></code> should already be
|
||
doing that since a type annotation can be expressed as a string literal.</p>
|
||
<p>For code which uses annotations for other purposes, a regular
|
||
<code class="docutils literal notranslate"><span class="pre">eval(ann,</span> <span class="pre">globals,</span> <span class="pre">locals)</span></code> call is enough to resolve the
|
||
annotation.</p>
|
||
<p>In both cases it’s important to consider how globals and locals affect
|
||
the postponed evaluation. An annotation is no longer evaluated at the
|
||
time of definition and, more importantly, <em>in the same scope</em> where it
|
||
was defined. Consequently, using local state in annotations is no
|
||
longer possible in general. As for globals, the module where the
|
||
annotation was defined is the correct context for postponed evaluation.</p>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">get_type_hints()</span></code> function automatically resolves the correct
|
||
value of <code class="docutils literal notranslate"><span class="pre">globalns</span></code> for functions and classes. It also automatically
|
||
provides the correct <code class="docutils literal notranslate"><span class="pre">localns</span></code> for classes.</p>
|
||
<p>When running <code class="docutils literal notranslate"><span class="pre">eval()</span></code>,
|
||
the value of globals can be gathered in the following way:</p>
|
||
<ul>
|
||
<li>function objects hold a reference to their respective globals in an
|
||
attribute called <code class="docutils literal notranslate"><span class="pre">__globals__</span></code>;</li>
|
||
<li>classes hold the name of the module they were defined in, this can be
|
||
used to retrieve the respective globals:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">cls_globals</span> <span class="o">=</span> <span class="nb">vars</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">modules</span><span class="p">[</span><span class="n">SomeClass</span><span class="o">.</span><span class="vm">__module__</span><span class="p">])</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Note that this needs to be repeated for base classes to evaluate all
|
||
<code class="docutils literal notranslate"><span class="pre">__annotations__</span></code>.</p>
|
||
</li>
|
||
<li>modules should use their own <code class="docutils literal notranslate"><span class="pre">__dict__</span></code>.</li>
|
||
</ul>
|
||
<p>The value of <code class="docutils literal notranslate"><span class="pre">localns</span></code> cannot be reliably retrieved for functions
|
||
because in all likelihood the stack frame at the time of the call no
|
||
longer exists.</p>
|
||
<p>For classes, <code class="docutils literal notranslate"><span class="pre">localns</span></code> can be composed by chaining vars of the given
|
||
class and its base classes (in the method resolution order). Since slots
|
||
can only be filled after the class was defined, we don’t need to consult
|
||
them for this purpose.</p>
|
||
<section id="runtime-annotation-resolution-and-class-decorators">
|
||
<h3><a class="toc-backref" href="#runtime-annotation-resolution-and-class-decorators" role="doc-backlink">Runtime annotation resolution and class decorators</a></h3>
|
||
<p>Metaclasses and class decorators that need to resolve annotations for
|
||
the current class will fail for annotations that use the name of the
|
||
current class. Example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">class_decorator</span><span class="p">(</span><span class="bp">cls</span><span class="p">):</span>
|
||
<span class="n">annotations</span> <span class="o">=</span> <span class="n">get_type_hints</span><span class="p">(</span><span class="bp">cls</span><span class="p">)</span> <span class="c1"># raises NameError on 'C'</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s1">'Annotations for </span><span class="si">{</span><span class="bp">cls</span><span class="si">}</span><span class="s1">: </span><span class="si">{</span><span class="n">annotations</span><span class="si">}</span><span class="s1">'</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="bp">cls</span>
|
||
|
||
<span class="nd">@class_decorator</span>
|
||
<span class="k">class</span> <span class="nc">C</span><span class="p">:</span>
|
||
<span class="n">singleton</span><span class="p">:</span> <span class="s1">'C'</span> <span class="o">=</span> <span class="kc">None</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This was already true before this PEP. The class decorator acts on
|
||
the class before it’s assigned a name in the current definition scope.</p>
|
||
</section>
|
||
<section id="runtime-annotation-resolution-and-type-checking">
|
||
<h3><a class="toc-backref" href="#runtime-annotation-resolution-and-type-checking" role="doc-backlink">Runtime annotation resolution and <code class="docutils literal notranslate"><span class="pre">TYPE_CHECKING</span></code></a></h3>
|
||
<p>Sometimes there’s code that must be seen by a type checker but should
|
||
not be executed. For such situations the <code class="docutils literal notranslate"><span class="pre">typing</span></code> module defines a
|
||
constant, <code class="docutils literal notranslate"><span class="pre">TYPE_CHECKING</span></code>, that is considered <code class="docutils literal notranslate"><span class="pre">True</span></code> during type
|
||
checking but <code class="docutils literal notranslate"><span class="pre">False</span></code> at runtime. Example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">typing</span>
|
||
|
||
<span class="k">if</span> <span class="n">typing</span><span class="o">.</span><span class="n">TYPE_CHECKING</span><span class="p">:</span>
|
||
<span class="kn">import</span> <span class="nn">expensive_mod</span>
|
||
|
||
<span class="k">def</span> <span class="nf">a_func</span><span class="p">(</span><span class="n">arg</span><span class="p">:</span> <span class="n">expensive_mod</span><span class="o">.</span><span class="n">SomeClass</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="n">a_var</span><span class="p">:</span> <span class="n">expensive_mod</span><span class="o">.</span><span class="n">SomeClass</span> <span class="o">=</span> <span class="n">arg</span>
|
||
<span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This approach is also useful when handling import cycles.</p>
|
||
<p>Trying to resolve annotations of <code class="docutils literal notranslate"><span class="pre">a_func</span></code> at runtime using
|
||
<code class="docutils literal notranslate"><span class="pre">typing.get_type_hints()</span></code> will fail since the name <code class="docutils literal notranslate"><span class="pre">expensive_mod</span></code>
|
||
is not defined (<code class="docutils literal notranslate"><span class="pre">TYPE_CHECKING</span></code> variable being <code class="docutils literal notranslate"><span class="pre">False</span></code> at runtime).
|
||
This was already true before this PEP.</p>
|
||
</section>
|
||
</section>
|
||
<section id="backwards-compatibility">
|
||
<h2><a class="toc-backref" href="#backwards-compatibility" role="doc-backlink">Backwards Compatibility</a></h2>
|
||
<p>This is a backwards incompatible change. Applications depending on
|
||
arbitrary objects to be directly present in annotations will break
|
||
if they are not using <code class="docutils literal notranslate"><span class="pre">typing.get_type_hints()</span></code> or <code class="docutils literal notranslate"><span class="pre">eval()</span></code>.</p>
|
||
<p>Annotations that depend on locals at the time of the function
|
||
definition will not be resolvable later. Example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">generate</span><span class="p">():</span>
|
||
<span class="n">A</span> <span class="o">=</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span>
|
||
<span class="k">class</span> <span class="nc">C</span><span class="p">:</span>
|
||
<span class="n">field</span><span class="p">:</span> <span class="n">A</span> <span class="o">=</span> <span class="mi">1</span>
|
||
<span class="k">def</span> <span class="nf">method</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">arg</span><span class="p">:</span> <span class="n">A</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span> <span class="o">...</span>
|
||
<span class="k">return</span> <span class="n">C</span>
|
||
<span class="n">X</span> <span class="o">=</span> <span class="n">generate</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Trying to resolve annotations of <code class="docutils literal notranslate"><span class="pre">X</span></code> later by using
|
||
<code class="docutils literal notranslate"><span class="pre">get_type_hints(X)</span></code> will fail because <code class="docutils literal notranslate"><span class="pre">A</span></code> and its enclosing scope no
|
||
longer exists. Python will make no attempt to disallow such annotations
|
||
since they can often still be successfully statically analyzed, which is
|
||
the predominant use case for annotations.</p>
|
||
<p>Annotations using nested classes and their respective state are still
|
||
valid. They can use local names or the fully qualified name. Example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">C</span><span class="p">:</span>
|
||
<span class="n">field</span> <span class="o">=</span> <span class="s1">'c_field'</span>
|
||
<span class="k">def</span> <span class="nf">method</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="n">C</span><span class="o">.</span><span class="n">field</span><span class="p">:</span> <span class="c1"># this is OK</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="k">def</span> <span class="nf">method</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="n">field</span><span class="p">:</span> <span class="c1"># this is OK</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="k">def</span> <span class="nf">method</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="n">C</span><span class="o">.</span><span class="n">D</span><span class="p">:</span> <span class="c1"># this is OK</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="k">def</span> <span class="nf">method</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="n">D</span><span class="p">:</span> <span class="c1"># this is OK</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="k">class</span> <span class="nc">D</span><span class="p">:</span>
|
||
<span class="n">field2</span> <span class="o">=</span> <span class="s1">'d_field'</span>
|
||
<span class="k">def</span> <span class="nf">method</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="n">C</span><span class="o">.</span><span class="n">D</span><span class="o">.</span><span class="n">field2</span><span class="p">:</span> <span class="c1"># this is OK</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="k">def</span> <span class="nf">method</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="n">D</span><span class="o">.</span><span class="n">field2</span><span class="p">:</span> <span class="c1"># this FAILS, class D is local to C</span>
|
||
<span class="o">...</span> <span class="c1"># and is therefore only available</span>
|
||
<span class="c1"># as C.D. This was already true</span>
|
||
<span class="c1"># before the PEP.</span>
|
||
|
||
<span class="k">def</span> <span class="nf">method</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="n">field2</span><span class="p">:</span> <span class="c1"># this is OK</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="k">def</span> <span class="nf">method</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="n">field</span><span class="p">:</span> <span class="c1"># this FAILS, field is local to C and</span>
|
||
<span class="c1"># is therefore not visible to D unless</span>
|
||
<span class="c1"># accessed as C.field. This was already</span>
|
||
<span class="c1"># true before the PEP.</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>In the presence of an annotation that isn’t a syntactically valid
|
||
expression, SyntaxError is raised at compile time. However, since names
|
||
aren’t resolved at that time, no attempt is made to validate whether
|
||
used names are correct or not.</p>
|
||
<section id="deprecation-policy">
|
||
<h3><a class="toc-backref" href="#deprecation-policy" role="doc-backlink">Deprecation policy</a></h3>
|
||
<p>Starting with Python 3.7, a <code class="docutils literal notranslate"><span class="pre">__future__</span></code> import is required to use the
|
||
described functionality. No warnings are raised.</p>
|
||
<p>NOTE: Whether this will eventually become the default behavior is currently unclear
|
||
pending decision on <a class="pep reference internal" href="../pep-0649/" title="PEP 649 – Deferred Evaluation Of Annotations Using Descriptors">PEP 649</a>. In any case, use of annotations that depend upon
|
||
their eager evaluation is incompatible with both proposals and is no longer
|
||
supported.</p>
|
||
</section>
|
||
</section>
|
||
<section id="forward-references">
|
||
<h2><a class="toc-backref" href="#forward-references" role="doc-backlink">Forward References</a></h2>
|
||
<p>Deliberately using a name before it was defined in the module is called
|
||
a forward reference. For the purpose of this section, we’ll call
|
||
any name imported or defined within a <code class="docutils literal notranslate"><span class="pre">if</span> <span class="pre">TYPE_CHECKING:</span></code> block
|
||
a forward reference, too.</p>
|
||
<p>This PEP addresses the issue of forward references in <em>type annotations</em>.
|
||
The use of string literals will no longer be required in this case.
|
||
However, there are APIs in the <code class="docutils literal notranslate"><span class="pre">typing</span></code> module that use other syntactic
|
||
constructs of the language, and those will still require working around
|
||
forward references with string literals. The list includes:</p>
|
||
<ul>
|
||
<li>type definitions:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">T</span> <span class="o">=</span> <span class="n">TypeVar</span><span class="p">(</span><span class="s1">'T'</span><span class="p">,</span> <span class="n">bound</span><span class="o">=</span><span class="s1">'<type>'</span><span class="p">)</span>
|
||
<span class="n">UserId</span> <span class="o">=</span> <span class="n">NewType</span><span class="p">(</span><span class="s1">'UserId'</span><span class="p">,</span> <span class="s1">'<type>'</span><span class="p">)</span>
|
||
<span class="n">Employee</span> <span class="o">=</span> <span class="n">NamedTuple</span><span class="p">(</span><span class="s1">'Employee'</span><span class="p">,</span> <span class="p">[(</span><span class="s1">'name'</span><span class="p">,</span> <span class="s1">'<type>'</span><span class="p">),</span> <span class="p">(</span><span class="s1">'id'</span><span class="p">,</span> <span class="s1">'<type>'</span><span class="p">)])</span>
|
||
</pre></div>
|
||
</div>
|
||
</li>
|
||
<li>aliases:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">Alias</span> <span class="o">=</span> <span class="n">Optional</span><span class="p">[</span><span class="s1">'<type>'</span><span class="p">]</span>
|
||
<span class="n">AnotherAlias</span> <span class="o">=</span> <span class="n">Union</span><span class="p">[</span><span class="s1">'<type>'</span><span class="p">,</span> <span class="s1">'<type>'</span><span class="p">]</span>
|
||
<span class="n">YetAnotherAlias</span> <span class="o">=</span> <span class="s1">'<type>'</span>
|
||
</pre></div>
|
||
</div>
|
||
</li>
|
||
<li>casting:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">cast</span><span class="p">(</span><span class="s1">'<type>'</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
</li>
|
||
<li>base classes:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">C</span><span class="p">(</span><span class="n">Tuple</span><span class="p">[</span><span class="s1">'<type>'</span><span class="p">,</span> <span class="s1">'<type>'</span><span class="p">]):</span> <span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
</li>
|
||
</ul>
|
||
<p>Depending on the specific case, some of the cases listed above might be
|
||
worked around by placing the usage in a <code class="docutils literal notranslate"><span class="pre">if</span> <span class="pre">TYPE_CHECKING:</span></code> block.
|
||
This will not work for any code that needs to be available at runtime,
|
||
notably for base classes and casting. For named tuples, using the new
|
||
class definition syntax introduced in Python 3.6 solves the issue.</p>
|
||
<p>In general, fixing the issue for <em>all</em> forward references requires
|
||
changing how module instantiation is performed in Python, from the
|
||
current single-pass top-down model. This would be a major change in the
|
||
language and is out of scope for this PEP.</p>
|
||
</section>
|
||
<section id="rejected-ideas">
|
||
<h2><a class="toc-backref" href="#rejected-ideas" role="doc-backlink">Rejected Ideas</a></h2>
|
||
<section id="keeping-the-ability-to-use-function-local-state-when-defining-annotations">
|
||
<h3><a class="toc-backref" href="#keeping-the-ability-to-use-function-local-state-when-defining-annotations" role="doc-backlink">Keeping the ability to use function local state when defining annotations</a></h3>
|
||
<p>With postponed evaluation, this would require keeping a reference to
|
||
the frame in which an annotation got created. This could be achieved
|
||
for example by storing all annotations as lambdas instead of strings.</p>
|
||
<p>This would be prohibitively expensive for highly annotated code as the
|
||
frames would keep all their objects alive. That includes predominantly
|
||
objects that won’t ever be accessed again.</p>
|
||
<p>To be able to address class-level scope, the lambda approach would
|
||
require a new kind of cell in the interpreter. This would proliferate
|
||
the number of types that can appear in <code class="docutils literal notranslate"><span class="pre">__annotations__</span></code>, as well as
|
||
wouldn’t be as introspectable as strings.</p>
|
||
<p>Note that in the case of nested classes, the functionality to get the
|
||
effective “globals” and “locals” at definition time is provided by
|
||
<code class="docutils literal notranslate"><span class="pre">typing.get_type_hints()</span></code>.</p>
|
||
<p>If a function generates a class or a function with annotations that
|
||
have to use local variables, it can populate the given generated
|
||
object’s <code class="docutils literal notranslate"><span class="pre">__annotations__</span></code> dictionary directly, without relying on
|
||
the compiler.</p>
|
||
</section>
|
||
<section id="disallowing-local-state-usage-for-classes-too">
|
||
<h3><a class="toc-backref" href="#disallowing-local-state-usage-for-classes-too" role="doc-backlink">Disallowing local state usage for classes, too</a></h3>
|
||
<p>This PEP originally proposed limiting names within annotations to only
|
||
allow names from the model-level scope, including for classes. The
|
||
author argued this makes name resolution unambiguous, including in cases
|
||
of conflicts between local names and module-level names.</p>
|
||
<p>This idea was ultimately rejected in case of classes. Instead,
|
||
<code class="docutils literal notranslate"><span class="pre">typing.get_type_hints()</span></code> got modified to populate the local namespace
|
||
correctly if class-level annotations are needed.</p>
|
||
<p>The reasons for rejecting the idea were that it goes against the
|
||
intuition of how scoping works in Python, and would break enough
|
||
existing type annotations to make the transition cumbersome. Finally,
|
||
local scope access is required for class decorators to be able to
|
||
evaluate type annotations. This is because class decorators are applied
|
||
before the class receives its name in the outer scope.</p>
|
||
</section>
|
||
<section id="introducing-a-new-dictionary-for-the-string-literal-form-instead">
|
||
<h3><a class="toc-backref" href="#introducing-a-new-dictionary-for-the-string-literal-form-instead" role="doc-backlink">Introducing a new dictionary for the string literal form instead</a></h3>
|
||
<p>Yury Selivanov shared the following idea:</p>
|
||
<ol class="arabic simple">
|
||
<li>Add a new special attribute to functions: <code class="docutils literal notranslate"><span class="pre">__annotations_text__</span></code>.</li>
|
||
<li>Make <code class="docutils literal notranslate"><span class="pre">__annotations__</span></code> a lazy dynamic mapping, evaluating
|
||
expressions from the corresponding key in <code class="docutils literal notranslate"><span class="pre">__annotations_text__</span></code>
|
||
just-in-time.</li>
|
||
</ol>
|
||
<p>This idea is supposed to solve the backwards compatibility issue,
|
||
removing the need for a new <code class="docutils literal notranslate"><span class="pre">__future__</span></code> import. Sadly, this is not
|
||
enough. Postponed evaluation changes which state the annotation has
|
||
access to. While postponed evaluation fixes the forward reference
|
||
problem, it also makes it impossible to access function-level locals
|
||
anymore. This alone is a source of backwards incompatibility which
|
||
justifies a deprecation period.</p>
|
||
<p>A <code class="docutils literal notranslate"><span class="pre">__future__</span></code> import is an obvious and explicit indicator of opting
|
||
in for the new functionality. It also makes it trivial for external
|
||
tools to recognize the difference between a Python files using the old
|
||
or the new approach. In the former case, that tool would recognize that
|
||
local state access is allowed, whereas in the latter case it would
|
||
recognize that forward references are allowed.</p>
|
||
<p>Finally, just-in-time evaluation in <code class="docutils literal notranslate"><span class="pre">__annotations__</span></code> is an
|
||
unnecessary step if <code class="docutils literal notranslate"><span class="pre">get_type_hints()</span></code> is used later.</p>
|
||
</section>
|
||
<section id="dropping-annotations-with-o">
|
||
<h3><a class="toc-backref" href="#dropping-annotations-with-o" role="doc-backlink">Dropping annotations with -O</a></h3>
|
||
<p>There are two reasons this is not satisfying for the purpose of this
|
||
PEP.</p>
|
||
<p>First, this only addresses runtime cost, not forward references, those
|
||
still cannot be safely used in source code. A library maintainer would
|
||
never be able to use forward references since that would force the
|
||
library users to use this new hypothetical -O switch.</p>
|
||
<p>Second, this throws the baby out with the bath water. Now <em>no</em> runtime
|
||
annotation use can be performed. <a class="pep reference internal" href="../pep-0557/" title="PEP 557 – Data Classes">PEP 557</a> is one example of a recent
|
||
development where evaluating type annotations at runtime is useful.</p>
|
||
<p>All that being said, a granular -O option to drop annotations is
|
||
a possibility in the future, as it’s conceptually compatible with
|
||
existing -O behavior (dropping docstrings and assert statements). This
|
||
PEP does not invalidate the idea.</p>
|
||
</section>
|
||
<section id="passing-string-literals-in-annotations-verbatim-to-annotations">
|
||
<h3><a class="toc-backref" href="#passing-string-literals-in-annotations-verbatim-to-annotations" role="doc-backlink">Passing string literals in annotations verbatim to <code class="docutils literal notranslate"><span class="pre">__annotations__</span></code></a></h3>
|
||
<p>This PEP originally suggested directly storing the contents of a string
|
||
literal under its respective key in <code class="docutils literal notranslate"><span class="pre">__annotations__</span></code>. This was
|
||
meant to simplify support for runtime type checkers.</p>
|
||
<p>Mark Shannon pointed out this idea was flawed since it wasn’t handling
|
||
situations where strings are only part of a type annotation.</p>
|
||
<p>The inconsistency of it was always apparent but given that it doesn’t
|
||
fully prevent cases of double-wrapping strings anyway, it is not worth
|
||
it.</p>
|
||
</section>
|
||
<section id="making-the-name-of-the-future-import-more-verbose">
|
||
<h3><a class="toc-backref" href="#making-the-name-of-the-future-import-more-verbose" role="doc-backlink">Making the name of the future import more verbose</a></h3>
|
||
<p>Instead of requiring the following import:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">__future__</span> <span class="kn">import</span> <span class="n">annotations</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>the PEP could call the feature more explicitly, for example
|
||
<code class="docutils literal notranslate"><span class="pre">string_annotations</span></code>, <code class="docutils literal notranslate"><span class="pre">stringify_annotations</span></code>,
|
||
<code class="docutils literal notranslate"><span class="pre">annotation_strings</span></code>, <code class="docutils literal notranslate"><span class="pre">annotations_as_strings</span></code>, <code class="docutils literal notranslate"><span class="pre">lazy_annotations</span></code>,
|
||
<code class="docutils literal notranslate"><span class="pre">static_annotations</span></code>, etc.</p>
|
||
<p>The problem with those names is that they are very verbose. Each of
|
||
them besides <code class="docutils literal notranslate"><span class="pre">lazy_annotations</span></code> would constitute the longest future
|
||
feature name in Python. They are long to type and harder to remember
|
||
than the single-word form.</p>
|
||
<p>There is precedence of a future import name that sounds overly generic
|
||
but in practice was obvious to users as to what it does:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">__future__</span> <span class="kn">import</span> <span class="n">division</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
</section>
|
||
<section id="prior-discussion">
|
||
<h2><a class="toc-backref" href="#prior-discussion" role="doc-backlink">Prior discussion</a></h2>
|
||
<section id="in-pep-484">
|
||
<h3><a class="toc-backref" href="#in-pep-484" role="doc-backlink">In PEP 484</a></h3>
|
||
<p>The forward reference problem was discussed when <a class="pep reference internal" href="../pep-0484/" title="PEP 484 – Type Hints">PEP 484</a> was originally
|
||
drafted, leading to the following statement in the document:</p>
|
||
<blockquote>
|
||
<div>A compromise is possible where a <code class="docutils literal notranslate"><span class="pre">__future__</span></code> import could enable
|
||
turning <em>all</em> annotations in a given module into string literals, as
|
||
follows:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">__future__</span> <span class="kn">import</span> <span class="n">annotations</span>
|
||
|
||
<span class="k">class</span> <span class="nc">ImSet</span><span class="p">:</span>
|
||
<span class="k">def</span> <span class="nf">add</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">a</span><span class="p">:</span> <span class="n">ImSet</span><span class="p">)</span> <span class="o">-></span> <span class="n">List</span><span class="p">[</span><span class="n">ImSet</span><span class="p">]:</span> <span class="o">...</span>
|
||
|
||
<span class="k">assert</span> <span class="n">ImSet</span><span class="o">.</span><span class="n">add</span><span class="o">.</span><span class="vm">__annotations__</span> <span class="o">==</span> <span class="p">{</span>
|
||
<span class="s1">'a'</span><span class="p">:</span> <span class="s1">'ImSet'</span><span class="p">,</span> <span class="s1">'return'</span><span class="p">:</span> <span class="s1">'List[ImSet]'</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Such a <code class="docutils literal notranslate"><span class="pre">__future__</span></code> import statement may be proposed in a separate
|
||
PEP.</p>
|
||
</div></blockquote>
|
||
</section>
|
||
<section id="python-typing-400">
|
||
<h3><a class="toc-backref" href="#python-typing-400" role="doc-backlink">python/typing#400</a></h3>
|
||
<p>The problem was discussed at length on the typing module’s GitHub
|
||
project, under <a class="reference external" href="https://github.com/python/typing/issues/400">Issue 400</a>.
|
||
The problem statement there includes critique of generic types requiring
|
||
imports from <code class="docutils literal notranslate"><span class="pre">typing</span></code>. This tends to be confusing to
|
||
beginners:</p>
|
||
<blockquote>
|
||
<div>Why this:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">List</span><span class="p">,</span> <span class="n">Set</span>
|
||
<span class="k">def</span> <span class="nf">dir</span><span class="p">(</span><span class="n">o</span><span class="p">:</span> <span class="nb">object</span> <span class="o">=</span> <span class="o">...</span><span class="p">)</span> <span class="o">-></span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">]:</span> <span class="o">...</span>
|
||
<span class="k">def</span> <span class="nf">add_friends</span><span class="p">(</span><span class="n">friends</span><span class="p">:</span> <span class="n">Set</span><span class="p">[</span><span class="n">Friend</span><span class="p">])</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span> <span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>But not this:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">dir</span><span class="p">(</span><span class="n">o</span><span class="p">:</span> <span class="nb">object</span> <span class="o">=</span> <span class="o">...</span><span class="p">)</span> <span class="o">-></span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]:</span> <span class="o">...</span>
|
||
<span class="k">def</span> <span class="nf">add_friends</span><span class="p">(</span><span class="n">friends</span><span class="p">:</span> <span class="nb">set</span><span class="p">[</span><span class="n">Friend</span><span class="p">])</span> <span class="o">-></span> <span class="kc">None</span> <span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Why this:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">up_to_ten</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">))</span>
|
||
<span class="n">friends</span> <span class="o">=</span> <span class="nb">set</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>But not this:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">List</span><span class="p">,</span> <span class="n">Set</span>
|
||
<span class="n">up_to_ten</span> <span class="o">=</span> <span class="n">List</span><span class="p">[</span><span class="nb">int</span><span class="p">](</span><span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">))</span>
|
||
<span class="n">friends</span> <span class="o">=</span> <span class="n">Set</span><span class="p">[</span><span class="n">Friend</span><span class="p">]()</span>
|
||
</pre></div>
|
||
</div>
|
||
</div></blockquote>
|
||
<p>While typing usability is an interesting problem, it is out of scope
|
||
of this PEP. Specifically, any extensions of the typing syntax
|
||
standardized in <a class="pep reference internal" href="../pep-0484/" title="PEP 484 – Type Hints">PEP 484</a> will require their own respective PEPs and
|
||
approval.</p>
|
||
<p>Issue 400 ultimately suggests postponing evaluation of annotations and
|
||
keeping them as strings in <code class="docutils literal notranslate"><span class="pre">__annotations__</span></code>, just like this PEP
|
||
specifies. This idea was received well. Ivan Levkivskyi supported
|
||
using the <code class="docutils literal notranslate"><span class="pre">__future__</span></code> import and suggested unparsing the AST in
|
||
<code class="docutils literal notranslate"><span class="pre">compile.c</span></code>. Jukka Lehtosalo pointed out that there are some cases
|
||
of forward references where types are used outside of annotations and
|
||
postponed evaluation will not help those. For those cases using the
|
||
string literal notation would still be required. Those cases are
|
||
discussed briefly in the “Forward References” section of this PEP.</p>
|
||
<p>The biggest controversy on the issue was Guido van Rossum’s concern
|
||
that untokenizing annotation expressions back to their string form has
|
||
no precedent in the Python programming language and feels like a hacky
|
||
workaround. He said:</p>
|
||
<blockquote>
|
||
<div>One thing that comes to mind is that it’s a very random change to
|
||
the language. It might be useful to have a more compact way to
|
||
indicate deferred execution of expressions (using less syntax than
|
||
<code class="docutils literal notranslate"><span class="pre">lambda:</span></code>). But why would the use case of type annotations be so
|
||
all-important to change the language to do it there first (rather
|
||
than proposing a more general solution), given that there’s already
|
||
a solution for this particular use case that requires very minimal
|
||
syntax?</div></blockquote>
|
||
<p>Eventually, Ethan Smith and schollii voiced that feedback gathered
|
||
during PyCon US suggests that the state of forward references needs
|
||
fixing. Guido van Rossum suggested coming back to the <code class="docutils literal notranslate"><span class="pre">__future__</span></code>
|
||
idea, pointing out that to prevent abuse, it’s important for the
|
||
annotations to be kept both syntactically valid and evaluating correctly
|
||
at runtime.</p>
|
||
</section>
|
||
<section id="first-draft-discussion-on-python-ideas">
|
||
<h3><a class="toc-backref" href="#first-draft-discussion-on-python-ideas" role="doc-backlink">First draft discussion on python-ideas</a></h3>
|
||
<p>Discussion happened largely in two threads, <a class="reference external" href="https://mail.python.org/pipermail/python-ideas/2017-September/thread.html#47031">the original announcement</a>
|
||
and a follow-up called <a class="reference external" href="https://mail.python.org/pipermail/python-ideas/2017-September/thread.html#47108">PEP 563 and expensive backwards compatibility</a>.</p>
|
||
<p>The PEP received rather warm feedback (4 strongly in favor,
|
||
2 in favor with concerns, 2 against). The biggest voice of concern on
|
||
the former thread being Steven D’Aprano’s review stating that the
|
||
problem definition of the PEP doesn’t justify breaking backwards
|
||
compatibility. In this response Steven seemed mostly concerned about
|
||
Python no longer supporting evaluation of annotations that depended on
|
||
local function/class state.</p>
|
||
<p>A few people voiced concerns that there are libraries using annotations
|
||
for non-typing purposes. However, none of the named libraries would be
|
||
invalidated by this PEP. They do require adapting to the new
|
||
requirement to call <code class="docutils literal notranslate"><span class="pre">eval()</span></code> on the annotation with the correct
|
||
<code class="docutils literal notranslate"><span class="pre">globals</span></code> and <code class="docutils literal notranslate"><span class="pre">locals</span></code> set.</p>
|
||
<p>This detail about <code class="docutils literal notranslate"><span class="pre">globals</span></code> and <code class="docutils literal notranslate"><span class="pre">locals</span></code> having to be correct was
|
||
picked up by a number of commenters. Alyssa (Nick) Coghlan benchmarked turning
|
||
annotations into lambdas instead of strings, sadly this proved to be
|
||
much slower at runtime than the current situation.</p>
|
||
<p>The latter thread was started by Jim J. Jewett who stressed that
|
||
the ability to properly evaluate annotations is an important requirement
|
||
and backwards compatibility in that regard is valuable. After some
|
||
discussion he admitted that side effects in annotations are a code smell
|
||
and modal support to either perform or not perform evaluation is
|
||
a messy solution. His biggest concern remained loss of functionality
|
||
stemming from the evaluation restrictions on global and local scope.</p>
|
||
<p>Alyssa Coghlan pointed out that some of those evaluation restrictions from
|
||
the PEP could be lifted by a clever implementation of an evaluation
|
||
helper, which could solve self-referencing classes even in the form of a
|
||
class decorator. She suggested the PEP should provide this helper
|
||
function in the standard library.</p>
|
||
</section>
|
||
<section id="second-draft-discussion-on-python-dev">
|
||
<h3><a class="toc-backref" href="#second-draft-discussion-on-python-dev" role="doc-backlink">Second draft discussion on python-dev</a></h3>
|
||
<p>Discussion happened mainly in the <a class="reference external" href="https://mail.python.org/pipermail/python-dev/2017-November/150062.html">announcement thread</a>,
|
||
followed by a brief discussion under <a class="reference external" href="https://mail.python.org/pipermail/python-dev/2017-November/150637.html">Mark Shannon’s post</a>.</p>
|
||
<p>Steven D’Aprano was concerned whether it’s acceptable for typos to be
|
||
allowed in annotations after the change proposed by the PEP. Brett
|
||
Cannon responded that type checkers and other static analyzers (like
|
||
linters or programming text editors) will catch this type of error.
|
||
Jukka Lehtosalo added that this situation is analogous to how names in
|
||
function bodies are not resolved until the function is called.</p>
|
||
<p>A major topic of discussion was Alyssa Coghlan’s suggestion to store
|
||
annotations in “thunk form”, in other words as a specialized lambda
|
||
which would be able to access class-level scope (and allow for scope
|
||
customization at call time). He presented a possible design for it
|
||
(<a class="reference external" href="https://mail.python.org/pipermail/python-dev/2017-November/150141.html">indirect attribute cells</a>).
|
||
This was later seen as equivalent to “special forms” in Lisp. Guido van
|
||
Rossum expressed worry that this sort of feature cannot be safely
|
||
implemented in twelve weeks (i.e. in time before the Python 3.7 beta
|
||
freeze).</p>
|
||
<p>After a while it became clear that the point of division between
|
||
supporters of the string form vs. supporters of the thunk form is
|
||
actually about whether annotations should be perceived as a general
|
||
syntactic element vs. something tied to the type checking use case.</p>
|
||
<p>Finally, Guido van Rossum declared he’s rejecting the thunk idea
|
||
based on the fact that it would require a new building block in the
|
||
interpreter. This block would be exposed in annotations, multiplying
|
||
possible types of values stored in <code class="docutils literal notranslate"><span class="pre">__annotations__</span></code> (arbitrary
|
||
objects, strings, and now thunks). Moreover, thunks aren’t as
|
||
introspectable as strings. Most importantly, Guido van Rossum
|
||
explicitly stated interest in gradually restricting the use of
|
||
annotations to static typing (with an optional runtime component).</p>
|
||
<p>Alyssa Coghlan got convinced to <a class="pep reference internal" href="../pep-0563/" title="PEP 563 – Postponed Evaluation of Annotations">PEP 563</a>, too, promptly beginning
|
||
the mandatory bike shedding session on the name of the <code class="docutils literal notranslate"><span class="pre">__future__</span></code>
|
||
import. Many debaters agreed that <code class="docutils literal notranslate"><span class="pre">annotations</span></code> seems like
|
||
an overly broad name for the feature name. Guido van Rossum briefly
|
||
decided to call it <code class="docutils literal notranslate"><span class="pre">string_annotations</span></code> but then changed his mind,
|
||
arguing that <code class="docutils literal notranslate"><span class="pre">division</span></code> is a precedent of a broad name with a clear
|
||
meaning.</p>
|
||
<p>The final improvement to the PEP suggested in the discussion by Mark
|
||
Shannon was the rejection of the temptation to pass string literals
|
||
through to <code class="docutils literal notranslate"><span class="pre">__annotations__</span></code> verbatim.</p>
|
||
<p>A side-thread of discussion started around the runtime penalty of
|
||
static typing, with topic like the import time of the <code class="docutils literal notranslate"><span class="pre">typing</span></code>
|
||
module (which is comparable to <code class="docutils literal notranslate"><span class="pre">re</span></code> without dependencies, and
|
||
three times as heavy as <code class="docutils literal notranslate"><span class="pre">re</span></code> when counting dependencies).</p>
|
||
</section>
|
||
</section>
|
||
<section id="acknowledgements">
|
||
<h2><a class="toc-backref" href="#acknowledgements" role="doc-backlink">Acknowledgements</a></h2>
|
||
<p>This document could not be completed without valuable input,
|
||
encouragement and advice from Guido van Rossum, Jukka Lehtosalo, and
|
||
Ivan Levkivskyi.</p>
|
||
<p>The implementation was thoroughly reviewed by Serhiy Storchaka who
|
||
found all sorts of issues, including bugs, bad readability, and
|
||
performance problems.</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-0563.rst">https://github.com/python/peps/blob/main/peps/pep-0563.rst</a></p>
|
||
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0563.rst">2024-03-24 01:43:58 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="#rationale-and-goals">Rationale and Goals</a><ul>
|
||
<li><a class="reference internal" href="#non-goals">Non-goals</a></li>
|
||
<li><a class="reference internal" href="#non-typing-usage-of-annotations">Non-typing usage of annotations</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#implementation">Implementation</a><ul>
|
||
<li><a class="reference internal" href="#enabling-the-future-behavior-in-python-3-7">Enabling the future behavior in Python 3.7</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#resolving-type-hints-at-runtime">Resolving Type Hints at Runtime</a><ul>
|
||
<li><a class="reference internal" href="#runtime-annotation-resolution-and-class-decorators">Runtime annotation resolution and class decorators</a></li>
|
||
<li><a class="reference internal" href="#runtime-annotation-resolution-and-type-checking">Runtime annotation resolution and <code class="docutils literal notranslate"><span class="pre">TYPE_CHECKING</span></code></a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#backwards-compatibility">Backwards Compatibility</a><ul>
|
||
<li><a class="reference internal" href="#deprecation-policy">Deprecation policy</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#forward-references">Forward References</a></li>
|
||
<li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a><ul>
|
||
<li><a class="reference internal" href="#keeping-the-ability-to-use-function-local-state-when-defining-annotations">Keeping the ability to use function local state when defining annotations</a></li>
|
||
<li><a class="reference internal" href="#disallowing-local-state-usage-for-classes-too">Disallowing local state usage for classes, too</a></li>
|
||
<li><a class="reference internal" href="#introducing-a-new-dictionary-for-the-string-literal-form-instead">Introducing a new dictionary for the string literal form instead</a></li>
|
||
<li><a class="reference internal" href="#dropping-annotations-with-o">Dropping annotations with -O</a></li>
|
||
<li><a class="reference internal" href="#passing-string-literals-in-annotations-verbatim-to-annotations">Passing string literals in annotations verbatim to <code class="docutils literal notranslate"><span class="pre">__annotations__</span></code></a></li>
|
||
<li><a class="reference internal" href="#making-the-name-of-the-future-import-more-verbose">Making the name of the future import more verbose</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#prior-discussion">Prior discussion</a><ul>
|
||
<li><a class="reference internal" href="#in-pep-484">In PEP 484</a></li>
|
||
<li><a class="reference internal" href="#python-typing-400">python/typing#400</a></li>
|
||
<li><a class="reference internal" href="#first-draft-discussion-on-python-ideas">First draft discussion on python-ideas</a></li>
|
||
<li><a class="reference internal" href="#second-draft-discussion-on-python-dev">Second draft discussion on python-dev</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#acknowledgements">Acknowledgements</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-0563.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> |