python-peps/pep-0692/index.html

698 lines
70 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

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

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="color-scheme" content="light dark">
<title>PEP 692 Using TypedDict for more precise **kwargs typing | peps.python.org</title>
<link rel="shortcut icon" href="../_static/py.png">
<link rel="canonical" href="https://peps.python.org/pep-0692/">
<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 692 Using TypedDict for more precise **kwargs typing | peps.python.org'>
<meta property="og:description" content="Currently **kwargs can be type hinted as long as all of the keyword arguments specified by them are of the same type. However, that behaviour can be very limiting. Therefore, in this PEP we propose a new way to enable more precise **kwargs typing. The n...">
<meta property="og:type" content="website">
<meta property="og:url" content="https://peps.python.org/pep-0692/">
<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="Currently **kwargs can be type hinted as long as all of the keyword arguments specified by them are of the same type. However, that behaviour can be very limiting. Therefore, in this PEP we propose a new way to enable more precise **kwargs typing. The n...">
<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 692</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 692 Using TypedDict for more precise **kwargs typing</h1>
<dl class="rfc2822 field-list simple">
<dt class="field-odd">Author<span class="colon">:</span></dt>
<dd class="field-odd">Franek Magiera &lt;framagie&#32;&#97;t&#32;gmail.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-692-using-typeddict-for-more-precise-kwargs-typing/17314">Discourse thread</a></dd>
<dt class="field-even">Status<span class="colon">:</span></dt>
<dd class="field-even"><abbr title="Accepted and implementation complete, or no longer active">Final</abbr></dd>
<dt class="field-odd">Type<span class="colon">:</span></dt>
<dd class="field-odd"><abbr title="Normative PEP with a new feature for Python, implementation change for CPython or interoperability standard for the ecosystem">Standards Track</abbr></dd>
<dt class="field-even">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">29-May-2022</dd>
<dt class="field-even">Python-Version<span class="colon">:</span></dt>
<dd class="field-even">3.12</dd>
<dt class="field-odd">Post-History<span class="colon">:</span></dt>
<dd class="field-odd"><a class="reference external" href="https://mail.python.org/archives/list/typing-sig&#64;python.org/thread/U42MJE6QZYWPVIFHJIGIT7OE52ZGIQV3/" title="Typing-SIG thread">29-May-2022</a>,
<a class="reference external" href="https://mail.python.org/archives/list/python-dev&#64;python.org/thread/PLCNW2XR4OOKAKHEZQM7R2AYVYUXPZGW/" title="Python-Dev thread">12-Jul-2022</a>,
<a class="reference external" href="https://discuss.python.org/t/pep-692-using-typeddict-for-more-precise-kwargs-typing/17314" title="Discourse thread">12-Jul-2022</a></dd>
<dt class="field-even">Resolution<span class="colon">:</span></dt>
<dd class="field-even"><a class="reference external" href="https://discuss.python.org/t/pep-692-using-typeddict-for-more-precise-kwargs-typing/17314/81">Discourse 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="#motivation">Motivation</a></li>
<li><a class="reference internal" href="#rationale">Rationale</a></li>
<li><a class="reference internal" href="#specification">Specification</a><ul>
<li><a class="reference internal" href="#function-calls-with-standard-dictionaries">Function calls with standard dictionaries</a></li>
<li><a class="reference internal" href="#keyword-collisions">Keyword collisions</a></li>
<li><a class="reference internal" href="#required-and-non-required-keys">Required and non-required keys</a></li>
<li><a class="reference internal" href="#assignment">Assignment</a><ul>
<li><a class="reference internal" href="#source-and-destination-contain-kwargs">Source and destination contain <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code></a></li>
<li><a class="reference internal" href="#source-contains-kwargs-and-destination-doesn-t">Source contains <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> and destination doesnt</a></li>
<li><a class="reference internal" href="#source-contains-untyped-kwargs">Source contains untyped <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code></a></li>
<li><a class="reference internal" href="#source-contains-traditionally-typed-kwargs-t">Source contains traditionally typed <code class="docutils literal notranslate"><span class="pre">**kwargs:</span> <span class="pre">T</span></code></a></li>
</ul>
</li>
<li><a class="reference internal" href="#passing-kwargs-inside-a-function-to-another-function">Passing kwargs inside a function to another function</a></li>
<li><a class="reference internal" href="#using-unpack-with-types-other-than-typeddict">Using <code class="docutils literal notranslate"><span class="pre">Unpack</span></code> with types other than <code class="docutils literal notranslate"><span class="pre">TypedDict</span></code></a></li>
<li><a class="reference internal" href="#changes-to-unpack">Changes to <code class="docutils literal notranslate"><span class="pre">Unpack</span></code></a></li>
</ul>
</li>
<li><a class="reference internal" href="#intended-usage">Intended Usage</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="#typeddict-unions"><code class="docutils literal notranslate"><span class="pre">TypedDict</span></code> unions</a></li>
<li><a class="reference internal" href="#changing-the-meaning-of-kwargs-annotations">Changing the meaning of <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> annotations</a></li>
<li><a class="reference internal" href="#introducing-a-new-syntax">Introducing a new syntax</a></li>
</ul>
</li>
<li><a class="reference internal" href="#copyright">Copyright</a></li>
</ul>
</details></section>
<div class="pep-banner canonical-typing-spec sticky-banner admonition attention">
<p class="admonition-title">Attention</p>
<p>This PEP is a historical document: see <a class="reference external" href="https://typing.readthedocs.io/en/latest/spec/callables.html#unpack-kwargs" title="(in typing)"><span>Unpack for keyword arguments</span></a> for up-to-date specs and documentation. Canonical typing specs are maintained at the <a class="reference external" href="https://typing.readthedocs.io/en/latest/spec/">typing specs site</a>; runtime typing behaviour is described in the CPython documentation.</p>
<p class="close-button">×</p>
<p>See the <a class="reference external" href="https://typing.readthedocs.io/en/latest/spec/meta.html">typing specification update process</a> for how to propose changes to the typing spec.</p>
</div>
<section id="abstract">
<h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2>
<p>Currently <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> can be type hinted as long as all of the keyword
arguments specified by them are of the same type. However, that behaviour can
be very limiting. Therefore, in this PEP we propose a new way to enable more
precise <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> typing. The new approach revolves around using
<code class="docutils literal notranslate"><span class="pre">TypedDict</span></code> to type <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> that comprise keyword arguments of different
types.</p>
</section>
<section id="motivation">
<h2><a class="toc-backref" href="#motivation" role="doc-backlink">Motivation</a></h2>
<p>Currently annotating <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> with a type <code class="docutils literal notranslate"><span class="pre">T</span></code> means that the <code class="docutils literal notranslate"><span class="pre">kwargs</span></code>
type is in fact <code class="docutils literal notranslate"><span class="pre">dict[str,</span> <span class="pre">T]</span></code>. For example:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span> <span class="o">...</span>
</pre></div>
</div>
<p>means that all keyword arguments in <code class="docutils literal notranslate"><span class="pre">foo</span></code> are strings (i.e., <code class="docutils literal notranslate"><span class="pre">kwargs</span></code> is
of type <code class="docutils literal notranslate"><span class="pre">dict[str,</span> <span class="pre">str]</span></code>). This behaviour limits the ability to type
annotate <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> only to the cases where all of them are of the same type.
However, it is often the case that keyword arguments conveyed by <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code>
have different types that are dependent on the keywords name. In those cases
type annotating <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> is not possible. This is especially a problem for
already existing codebases where the need of refactoring the code in order to
introduce proper type annotations may be considered not worth the effort. This
in turn prevents the project from getting all of the benefits that type hinting
can provide.</p>
<p>Moreover, <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> can be used to reduce the amount of code needed in
cases when there is a top-level function that is a part of a public API and it
calls a bunch of helper functions, all of which expect the same keyword
arguments. Unfortunately, if those helper functions were to use <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code>,
there is no way to properly type hint them if the keyword arguments they expect
are of different types. In addition, even if the keyword arguments are of the
same type, there is no way to check whether the function is being called with
keyword names that it actually expects.</p>
<p>As described in the <a class="reference internal" href="#intended-usage">Intended Usage</a> section,
using <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> is not always the best tool for the job. Despite that, it is
still a widely used pattern. As a consequence, there has been a lot of
discussion around supporting more precise <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> typing and it became a
feature that would be valuable for a large part of the Python community. This
is best illustrated by the <a class="reference external" href="https://github.com/python/mypy/issues/4441">mypy GitHub issue 4441</a> which
contains a lot of real world cases that could benefit from this propsal.</p>
<p>One more use case worth mentioning for which <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> are also convenient,
is when a function should accommodate optional keyword-only arguments that
dont have default values. A need for a pattern like that can arise when values
that are usually used as defaults to indicate no user input, such as <code class="docutils literal notranslate"><span class="pre">None</span></code>,
can be passed in by a user and should result in a valid, non-default behavior.
For example, this issue <a class="reference external" href="https://github.com/encode/httpx/issues/1384">came up</a> in the popular <code class="docutils literal notranslate"><span class="pre">httpx</span></code> library.</p>
</section>
<section id="rationale">
<h2><a class="toc-backref" href="#rationale" role="doc-backlink">Rationale</a></h2>
<p><a class="pep reference internal" href="../pep-0589/" title="PEP 589 TypedDict: Type Hints for Dictionaries with a Fixed Set of Keys">PEP 589</a> introduced the <code class="docutils literal notranslate"><span class="pre">TypedDict</span></code> type constructor that supports dictionary
types consisting of string keys and values of potentially different types. A
functions keyword arguments represented by a formal parameter that begins with
double asterisk, such as <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code>, are received as a dictionary.
Additionally, such functions are often called using unpacked dictionaries to
provide keyword arguments. This makes <code class="docutils literal notranslate"><span class="pre">TypedDict</span></code> a perfect candidate to be
used for more precise <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> typing. In addition, with <code class="docutils literal notranslate"><span class="pre">TypedDict</span></code>
keyword names can be taken into account during static type analysis. However,
specifying <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> type with a <code class="docutils literal notranslate"><span class="pre">TypedDict</span></code> means, as mentioned earlier,
that each keyword argument specified by <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> is a <code class="docutils literal notranslate"><span class="pre">TypedDict</span></code> itself.
For instance:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Movie</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">):</span>
<span class="n">name</span><span class="p">:</span> <span class="nb">str</span>
<span class="n">year</span><span class="p">:</span> <span class="nb">int</span>
<span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="n">Movie</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span> <span class="o">...</span>
</pre></div>
</div>
<p>means that each keyword argument in <code class="docutils literal notranslate"><span class="pre">foo</span></code> is itself a <code class="docutils literal notranslate"><span class="pre">Movie</span></code> dictionary
that has a <code class="docutils literal notranslate"><span class="pre">name</span></code> key with a string type value and a <code class="docutils literal notranslate"><span class="pre">year</span></code> key with an
integer type value. Therefore, in order to support specifying <code class="docutils literal notranslate"><span class="pre">kwargs</span></code> type
as a <code class="docutils literal notranslate"><span class="pre">TypedDict</span></code> without breaking current behaviour, a new construct has to
be introduced.</p>
<p>To support this use case, we propose reusing <code class="docutils literal notranslate"><span class="pre">Unpack</span></code> which
was initially introduced in <a class="pep reference internal" href="../pep-0646/" title="PEP 646 Variadic Generics">PEP 646</a>. There are several reasons for doing so:</p>
<ul class="simple">
<li>Its name is quite suitable and intuitive for the <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> typing use case
as our intention is to “unpack” the keywords arguments from the supplied
<code class="docutils literal notranslate"><span class="pre">TypedDict</span></code>.</li>
<li>The current way of typing <code class="docutils literal notranslate"><span class="pre">*args</span></code> would be extended to <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code>
and those are supposed to behave similarly.</li>
<li>There would be no need to introduce any new special forms.</li>
<li>The use of <code class="docutils literal notranslate"><span class="pre">Unpack</span></code> for the purposes described in this PEP does not
interfere with the use cases described in <a class="pep reference internal" href="../pep-0646/" title="PEP 646 Variadic Generics">PEP 646</a>.</li>
</ul>
</section>
<section id="specification">
<h2><a class="toc-backref" href="#specification" role="doc-backlink">Specification</a></h2>
<p>With <code class="docutils literal notranslate"><span class="pre">Unpack</span></code> we introduce a new way of annotating <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code>.
Continuing the previous example:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="n">Unpack</span><span class="p">[</span><span class="n">Movie</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span> <span class="o">...</span>
</pre></div>
</div>
<p>would mean that the <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> comprise two keyword arguments specified by
<code class="docutils literal notranslate"><span class="pre">Movie</span></code> (i.e. a <code class="docutils literal notranslate"><span class="pre">name</span></code> keyword of type <code class="docutils literal notranslate"><span class="pre">str</span></code> and a <code class="docutils literal notranslate"><span class="pre">year</span></code> keyword of
type <code class="docutils literal notranslate"><span class="pre">int</span></code>). This indicates that the function should be called as follows:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">kwargs</span><span class="p">:</span> <span class="n">Movie</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Life of Brian&quot;</span><span class="p">,</span> <span class="s2">&quot;year&quot;</span><span class="p">:</span> <span class="mi">1979</span><span class="p">}</span>
<span class="n">foo</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">)</span> <span class="c1"># OK!</span>
<span class="n">foo</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s2">&quot;The Meaning of Life&quot;</span><span class="p">,</span> <span class="n">year</span><span class="o">=</span><span class="mi">1983</span><span class="p">)</span> <span class="c1"># OK!</span>
</pre></div>
</div>
<p>When <code class="docutils literal notranslate"><span class="pre">Unpack</span></code> is used, type checkers treat <code class="docutils literal notranslate"><span class="pre">kwargs</span></code> inside the
function body as a <code class="docutils literal notranslate"><span class="pre">TypedDict</span></code>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="n">Unpack</span><span class="p">[</span><span class="n">Movie</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">assert_type</span><span class="p">(</span><span class="n">kwargs</span><span class="p">,</span> <span class="n">Movie</span><span class="p">)</span> <span class="c1"># OK!</span>
</pre></div>
</div>
<p>Using the new annotation will not have any runtime effect - it should only be
taken into account by type checkers. Any mention of errors in the following
sections relates to type checker errors.</p>
<section id="function-calls-with-standard-dictionaries">
<h3><a class="toc-backref" href="#function-calls-with-standard-dictionaries" role="doc-backlink">Function calls with standard dictionaries</a></h3>
<p>Passing a dictionary of type <code class="docutils literal notranslate"><span class="pre">dict[str,</span> <span class="pre">object]</span></code> as a <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> argument
to a function that has <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> annotated with <code class="docutils literal notranslate"><span class="pre">Unpack</span></code> must generate a
type checker error. On the other hand, the behaviour for functions using
standard, untyped dictionaries can depend on the type checker. For example:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="n">Unpack</span><span class="p">[</span><span class="n">Movie</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span> <span class="o">...</span>
<span class="n">movie</span><span class="p">:</span> <span class="nb">dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">object</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Life of Brian&quot;</span><span class="p">,</span> <span class="s2">&quot;year&quot;</span><span class="p">:</span> <span class="mi">1979</span><span class="p">}</span>
<span class="n">foo</span><span class="p">(</span><span class="o">**</span><span class="n">movie</span><span class="p">)</span> <span class="c1"># WRONG! Movie is of type dict[str, object]</span>
<span class="n">typed_movie</span><span class="p">:</span> <span class="n">Movie</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;The Meaning of Life&quot;</span><span class="p">,</span> <span class="s2">&quot;year&quot;</span><span class="p">:</span> <span class="mi">1983</span><span class="p">}</span>
<span class="n">foo</span><span class="p">(</span><span class="o">**</span><span class="n">typed_movie</span><span class="p">)</span> <span class="c1"># OK!</span>
<span class="n">another_movie</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Life of Brian&quot;</span><span class="p">,</span> <span class="s2">&quot;year&quot;</span><span class="p">:</span> <span class="mi">1979</span><span class="p">}</span>
<span class="n">foo</span><span class="p">(</span><span class="o">**</span><span class="n">another_movie</span><span class="p">)</span> <span class="c1"># Depends on the type checker.</span>
</pre></div>
</div>
</section>
<section id="keyword-collisions">
<h3><a class="toc-backref" href="#keyword-collisions" role="doc-backlink">Keyword collisions</a></h3>
<p>A <code class="docutils literal notranslate"><span class="pre">TypedDict</span></code> that is used to type <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> could potentially contain
keys that are already defined in the functions signature. If the duplicate
name is a standard parameter, an error should be reported by type checkers.
If the duplicate name is a positional-only parameter, no errors should be
generated. For example:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="n">Unpack</span><span class="p">[</span><span class="n">Movie</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span> <span class="o">...</span> <span class="c1"># WRONG! &quot;name&quot; will</span>
<span class="c1"># always bind to the</span>
<span class="c1"># first parameter.</span>
<span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="o">/</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="n">Unpack</span><span class="p">[</span><span class="n">Movie</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span> <span class="o">...</span> <span class="c1"># OK! &quot;name&quot; is a</span>
<span class="c1"># positional-only parameter,</span>
<span class="c1"># so **kwargs can contain</span>
<span class="c1"># a &quot;name&quot; keyword.</span>
</pre></div>
</div>
</section>
<section id="required-and-non-required-keys">
<h3><a class="toc-backref" href="#required-and-non-required-keys" role="doc-backlink">Required and non-required keys</a></h3>
<p>By default all keys in a <code class="docutils literal notranslate"><span class="pre">TypedDict</span></code> are required. This behaviour can be
overridden by setting the dictionarys <code class="docutils literal notranslate"><span class="pre">total</span></code> parameter as <code class="docutils literal notranslate"><span class="pre">False</span></code>.
Moreover, <a class="pep reference internal" href="../pep-0655/" title="PEP 655 Marking individual TypedDict items as required or potentially-missing">PEP 655</a> introduced new type qualifiers - <code class="docutils literal notranslate"><span class="pre">typing.Required</span></code> and
<code class="docutils literal notranslate"><span class="pre">typing.NotRequired</span></code> - that enable specifying whether a particular key is
required or not:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Movie</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">):</span>
<span class="n">title</span><span class="p">:</span> <span class="nb">str</span>
<span class="n">year</span><span class="p">:</span> <span class="n">NotRequired</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span>
</pre></div>
</div>
<p>When using a <code class="docutils literal notranslate"><span class="pre">TypedDict</span></code> to type <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> all of the required and
non-required keys should correspond to required and non-required function
keyword parameters. Therefore, if a required key is not supported by the
caller, then an error must be reported by type checkers.</p>
</section>
<section id="assignment">
<h3><a class="toc-backref" href="#assignment" role="doc-backlink">Assignment</a></h3>
<p>Assignments of a function typed with <code class="docutils literal notranslate"><span class="pre">**kwargs:</span> <span class="pre">Unpack[Movie]</span></code> and
another callable type should pass type checking only if they are compatible.
This can happen for the scenarios described below.</p>
<section id="source-and-destination-contain-kwargs">
<h4><a class="toc-backref" href="#source-and-destination-contain-kwargs" role="doc-backlink">Source and destination contain <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code></a></h4>
<p>Both destination and source functions have a <code class="docutils literal notranslate"><span class="pre">**kwargs:</span> <span class="pre">Unpack[TypedDict]</span></code>
parameter and the destination functions <code class="docutils literal notranslate"><span class="pre">TypedDict</span></code> is assignable to the
source functions <code class="docutils literal notranslate"><span class="pre">TypedDict</span></code> and the rest of the parameters are
compatible:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Animal</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">):</span>
<span class="n">name</span><span class="p">:</span> <span class="nb">str</span>
<span class="k">class</span> <span class="nc">Dog</span><span class="p">(</span><span class="n">Animal</span><span class="p">):</span>
<span class="n">breed</span><span class="p">:</span> <span class="nb">str</span>
<span class="k">def</span> <span class="nf">accept_animal</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="n">Unpack</span><span class="p">[</span><span class="n">Animal</span><span class="p">]):</span> <span class="o">...</span>
<span class="k">def</span> <span class="nf">accept_dog</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="n">Unpack</span><span class="p">[</span><span class="n">Dog</span><span class="p">]):</span> <span class="o">...</span>
<span class="n">accept_dog</span> <span class="o">=</span> <span class="n">accept_animal</span> <span class="c1"># OK! Expression of type Dog can be</span>
<span class="c1"># assigned to a variable of type Animal.</span>
<span class="n">accept_animal</span> <span class="o">=</span> <span class="n">accept_dog</span> <span class="c1"># WRONG! Expression of type Animal</span>
<span class="c1"># cannot be assigned to a variable of type Dog.</span>
</pre></div>
</div>
</section>
<section id="source-contains-kwargs-and-destination-doesn-t">
<span id="pep-692-assignment-dest-no-kwargs"></span><h4><a class="toc-backref" href="#source-contains-kwargs-and-destination-doesn-t" role="doc-backlink">Source contains <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> and destination doesnt</a></h4>
<p>The destination callable doesnt contain <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code>, the source callable
contains <code class="docutils literal notranslate"><span class="pre">**kwargs:</span> <span class="pre">Unpack[TypedDict]</span></code> and the destination functions keyword
arguments are assignable to the corresponding keys in source functions
<code class="docutils literal notranslate"><span class="pre">TypedDict</span></code>. Moreover, not required keys should correspond to optional
function arguments, whereas required keys should correspond to required
function arguments. Again, the rest of the parameters have to be compatible.
Continuing the previous example:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Example</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">):</span>
<span class="n">animal</span><span class="p">:</span> <span class="n">Animal</span>
<span class="n">string</span><span class="p">:</span> <span class="nb">str</span>
<span class="n">number</span><span class="p">:</span> <span class="n">NotRequired</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">src</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="n">Unpack</span><span class="p">[</span><span class="n">Example</span><span class="p">]):</span> <span class="o">...</span>
<span class="k">def</span> <span class="nf">dest</span><span class="p">(</span><span class="o">*</span><span class="p">,</span> <span class="n">animal</span><span class="p">:</span> <span class="n">Dog</span><span class="p">,</span> <span class="n">string</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">number</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="o">...</span><span class="p">):</span> <span class="o">...</span>
<span class="n">dest</span> <span class="o">=</span> <span class="n">src</span> <span class="c1"># OK!</span>
</pre></div>
</div>
<p>It is worth pointing out that the destination functions parameters that are to
be compatible with the keys and values from the <code class="docutils literal notranslate"><span class="pre">TypedDict</span></code> must be keyword
only:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">dest</span><span class="p">(</span><span class="n">dog</span><span class="p">:</span> <span class="n">Dog</span><span class="p">,</span> <span class="n">string</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">number</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="o">...</span><span class="p">):</span> <span class="o">...</span>
<span class="n">dog</span><span class="p">:</span> <span class="n">Dog</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Daisy&quot;</span><span class="p">,</span> <span class="s2">&quot;breed&quot;</span><span class="p">:</span> <span class="s2">&quot;labrador&quot;</span><span class="p">}</span>
<span class="n">dest</span><span class="p">(</span><span class="n">dog</span><span class="p">,</span> <span class="s2">&quot;some string&quot;</span><span class="p">)</span> <span class="c1"># OK!</span>
<span class="n">dest</span> <span class="o">=</span> <span class="n">src</span> <span class="c1"># Type checker error!</span>
<span class="n">dest</span><span class="p">(</span><span class="n">dog</span><span class="p">,</span> <span class="s2">&quot;some string&quot;</span><span class="p">)</span> <span class="c1"># The same call fails at</span>
<span class="c1"># runtime now because &#39;src&#39; expects</span>
<span class="c1"># keyword arguments.</span>
</pre></div>
</div>
<p>The reverse situation where the destination callable contains
<code class="docutils literal notranslate"><span class="pre">**kwargs:</span> <span class="pre">Unpack[TypedDict]</span></code> and the source callable doesnt contain
<code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> should be disallowed. This is because, we cannot be sure that
additional keyword arguments are not being passed in when an instance of a
subclass had been assigned to a variable with a base class type and then
unpacked in the destination callable invocation:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">dest</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="n">Unpack</span><span class="p">[</span><span class="n">Animal</span><span class="p">]):</span> <span class="o">...</span>
<span class="k">def</span> <span class="nf">src</span><span class="p">(</span><span class="n">name</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span> <span class="o">...</span>
<span class="n">dog</span><span class="p">:</span> <span class="n">Dog</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Daisy&quot;</span><span class="p">,</span> <span class="s2">&quot;breed&quot;</span><span class="p">:</span> <span class="s2">&quot;Labrador&quot;</span><span class="p">}</span>
<span class="n">animal</span><span class="p">:</span> <span class="n">Animal</span> <span class="o">=</span> <span class="n">dog</span>
<span class="n">dest</span> <span class="o">=</span> <span class="n">src</span> <span class="c1"># WRONG!</span>
<span class="n">dest</span><span class="p">(</span><span class="o">**</span><span class="n">animal</span><span class="p">)</span> <span class="c1"># Fails at runtime.</span>
</pre></div>
</div>
<p>Similar situation can happen even without inheritance as compatibility
between <code class="docutils literal notranslate"><span class="pre">TypedDict</span></code>s is based on structural subtyping.</p>
</section>
<section id="source-contains-untyped-kwargs">
<h4><a class="toc-backref" href="#source-contains-untyped-kwargs" role="doc-backlink">Source contains untyped <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code></a></h4>
<p>The destination callable contains <code class="docutils literal notranslate"><span class="pre">**kwargs:</span> <span class="pre">Unpack[TypedDict]</span></code> and the
source callable contains untyped <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">src</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">):</span> <span class="o">...</span>
<span class="k">def</span> <span class="nf">dest</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="n">Unpack</span><span class="p">[</span><span class="n">Movie</span><span class="p">]):</span> <span class="o">...</span>
<span class="n">dest</span> <span class="o">=</span> <span class="n">src</span> <span class="c1"># OK!</span>
</pre></div>
</div>
</section>
<section id="source-contains-traditionally-typed-kwargs-t">
<h4><a class="toc-backref" href="#source-contains-traditionally-typed-kwargs-t" role="doc-backlink">Source contains traditionally typed <code class="docutils literal notranslate"><span class="pre">**kwargs:</span> <span class="pre">T</span></code></a></h4>
<p>The destination callable contains <code class="docutils literal notranslate"><span class="pre">**kwargs:</span> <span class="pre">Unpack[TypedDict]</span></code>, the source
callable contains traditionally typed <code class="docutils literal notranslate"><span class="pre">**kwargs:</span> <span class="pre">T</span></code> and each of the
destination function <code class="docutils literal notranslate"><span class="pre">TypedDict</span></code>s fields is assignable to a variable of
type <code class="docutils literal notranslate"><span class="pre">T</span></code>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Vehicle</span><span class="p">:</span>
<span class="o">...</span>
<span class="k">class</span> <span class="nc">Car</span><span class="p">(</span><span class="n">Vehicle</span><span class="p">):</span>
<span class="o">...</span>
<span class="k">class</span> <span class="nc">Motorcycle</span><span class="p">(</span><span class="n">Vehicle</span><span class="p">):</span>
<span class="o">...</span>
<span class="k">class</span> <span class="nc">Vehicles</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">):</span>
<span class="n">car</span><span class="p">:</span> <span class="n">Car</span>
<span class="n">moto</span><span class="p">:</span> <span class="n">Motorcycle</span>
<span class="k">def</span> <span class="nf">dest</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="n">Unpack</span><span class="p">[</span><span class="n">Vehicles</span><span class="p">]):</span> <span class="o">...</span>
<span class="k">def</span> <span class="nf">src</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="n">Vehicle</span><span class="p">):</span> <span class="o">...</span>
<span class="n">dest</span> <span class="o">=</span> <span class="n">src</span> <span class="c1"># OK!</span>
</pre></div>
</div>
<p>On the other hand, if the destination callable contains either untyped or
traditionally typed <code class="docutils literal notranslate"><span class="pre">**kwargs:</span> <span class="pre">T</span></code> and the source callable is typed using
<code class="docutils literal notranslate"><span class="pre">**kwargs:</span> <span class="pre">Unpack[TypedDict]</span></code> then an error should be generated, because
traditionally typed <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> arent checked for keyword names.</p>
<p>To summarize, function parameters should behave contravariantly and function
return types should behave covariantly.</p>
</section>
</section>
<section id="passing-kwargs-inside-a-function-to-another-function">
<h3><a class="toc-backref" href="#passing-kwargs-inside-a-function-to-another-function" role="doc-backlink">Passing kwargs inside a function to another function</a></h3>
<p><a class="reference external" href="PEP692assignmentdestnokwargs">A previous point</a>
mentions the problem of possibly passing additional keyword arguments by
assigning a subclass instance to a variable that has a base class type. Lets
consider the following example:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Animal</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">):</span>
<span class="n">name</span><span class="p">:</span> <span class="nb">str</span>
<span class="k">class</span> <span class="nc">Dog</span><span class="p">(</span><span class="n">Animal</span><span class="p">):</span>
<span class="n">breed</span><span class="p">:</span> <span class="nb">str</span>
<span class="k">def</span> <span class="nf">takes_name</span><span class="p">(</span><span class="n">name</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span> <span class="o">...</span>
<span class="n">dog</span><span class="p">:</span> <span class="n">Dog</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Daisy&quot;</span><span class="p">,</span> <span class="s2">&quot;breed&quot;</span><span class="p">:</span> <span class="s2">&quot;Labrador&quot;</span><span class="p">}</span>
<span class="n">animal</span><span class="p">:</span> <span class="n">Animal</span> <span class="o">=</span> <span class="n">dog</span>
<span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="n">Unpack</span><span class="p">[</span><span class="n">Animal</span><span class="p">]):</span>
<span class="nb">print</span><span class="p">(</span><span class="n">kwargs</span><span class="p">[</span><span class="s2">&quot;name&quot;</span><span class="p">]</span><span class="o">.</span><span class="n">capitalize</span><span class="p">())</span>
<span class="k">def</span> <span class="nf">bar</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="n">Unpack</span><span class="p">[</span><span class="n">Animal</span><span class="p">]):</span>
<span class="n">takes_name</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">baz</span><span class="p">(</span><span class="n">animal</span><span class="p">:</span> <span class="n">Animal</span><span class="p">):</span>
<span class="n">takes_name</span><span class="p">(</span><span class="o">**</span><span class="n">animal</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">spam</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="n">Unpack</span><span class="p">[</span><span class="n">Animal</span><span class="p">]):</span>
<span class="n">baz</span><span class="p">(</span><span class="n">kwargs</span><span class="p">)</span>
<span class="n">foo</span><span class="p">(</span><span class="o">**</span><span class="n">animal</span><span class="p">)</span> <span class="c1"># OK! foo only expects and uses keywords of &#39;Animal&#39;.</span>
<span class="n">bar</span><span class="p">(</span><span class="o">**</span><span class="n">animal</span><span class="p">)</span> <span class="c1"># WRONG! This will fail at runtime because &#39;breed&#39; keyword</span>
<span class="c1"># will be passed to &#39;takes_name&#39; as well.</span>
<span class="n">spam</span><span class="p">(</span><span class="o">**</span><span class="n">animal</span><span class="p">)</span> <span class="c1"># WRONG! Again, &#39;breed&#39; keyword will be eventually passed</span>
<span class="c1"># to &#39;takes_name&#39;.</span>
</pre></div>
</div>
<p>In the example above, the call to <code class="docutils literal notranslate"><span class="pre">foo</span></code> will not cause any issues at
runtime. Even though <code class="docutils literal notranslate"><span class="pre">foo</span></code> expects <code class="docutils literal notranslate"><span class="pre">kwargs</span></code> of type <code class="docutils literal notranslate"><span class="pre">Animal</span></code> it doesnt
matter if it receives additional arguments because it only reads and uses what
it needs completely ignoring any additional values.</p>
<p>The calls to <code class="docutils literal notranslate"><span class="pre">bar</span></code> and <code class="docutils literal notranslate"><span class="pre">spam</span></code> will fail because an unexpected keyword
argument will be passed to the <code class="docutils literal notranslate"><span class="pre">takes_name</span></code> function.</p>
<p>Therefore, <code class="docutils literal notranslate"><span class="pre">kwargs</span></code> hinted with an unpacked <code class="docutils literal notranslate"><span class="pre">TypedDict</span></code> can only be passed
to another function if the function to which unpacked kwargs are being passed
to has <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> in its signature as well, because then additional keywords
would not cause errors at runtime during function invocation. Otherwise, the
type checker should generate an error.</p>
<p>In cases similar to the <code class="docutils literal notranslate"><span class="pre">bar</span></code> function above the problem could be worked
around by explicitly dereferencing desired fields and using them as arguments
to perform the function call:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">bar</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="n">Unpack</span><span class="p">[</span><span class="n">Animal</span><span class="p">]):</span>
<span class="n">name</span> <span class="o">=</span> <span class="n">kwargs</span><span class="p">[</span><span class="s2">&quot;name&quot;</span><span class="p">]</span>
<span class="n">takes_name</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
</pre></div>
</div>
</section>
<section id="using-unpack-with-types-other-than-typeddict">
<h3><a class="toc-backref" href="#using-unpack-with-types-other-than-typeddict" role="doc-backlink">Using <code class="docutils literal notranslate"><span class="pre">Unpack</span></code> with types other than <code class="docutils literal notranslate"><span class="pre">TypedDict</span></code></a></h3>
<p>As described in the <a class="reference internal" href="#rationale">Rationale</a> section,
<code class="docutils literal notranslate"><span class="pre">TypedDict</span></code> is the most natural candidate for typing <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code>.
Therefore, in the context of typing <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code>, using <code class="docutils literal notranslate"><span class="pre">Unpack</span></code> with types
other than <code class="docutils literal notranslate"><span class="pre">TypedDict</span></code> should not be allowed and type checkers should
generate errors in such cases.</p>
</section>
<section id="changes-to-unpack">
<h3><a class="toc-backref" href="#changes-to-unpack" role="doc-backlink">Changes to <code class="docutils literal notranslate"><span class="pre">Unpack</span></code></a></h3>
<p>Currently using <code class="docutils literal notranslate"><span class="pre">Unpack</span></code> in the context of
typing is interchangeable with using the asterisk syntax:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">Unpack</span><span class="p">[</span><span class="n">Movie</span><span class="p">]</span>
<span class="go">*&lt;class &#39;__main__.Movie&#39;&gt;</span>
</pre></div>
</div>
<p>Therefore, in order to be compatible with the new use case, <code class="docutils literal notranslate"><span class="pre">Unpack</span></code>s
<code class="docutils literal notranslate"><span class="pre">repr</span></code> should be changed to simply <code class="docutils literal notranslate"><span class="pre">Unpack[T]</span></code>.</p>
</section>
</section>
<section id="intended-usage">
<h2><a class="toc-backref" href="#intended-usage" role="doc-backlink">Intended Usage</a></h2>
<p>The intended use cases for this proposal are described in the
<a class="reference internal" href="#motivation">Motivation</a> section. In summary, more precise <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> typing
can bring benefits to already existing codebases that decided to use
<code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> initially, but now are mature enough to use a stricter contract
via type hints. Using <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> can also help in reducing code duplication
and the amount of copy-pasting needed when there is a bunch of functions that
require the same set of keyword arguments. Finally, <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> are useful for
cases when a function needs to facilitate optional keyword arguments that dont
have obvious default values.</p>
<p>However, it has to be pointed out that in some cases there are better tools
for the job than using <code class="docutils literal notranslate"><span class="pre">TypedDict</span></code> to type <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> as proposed in this
PEP. For example, when writing new code if all the keyword arguments are
required or have default values then writing everything explicitly is better
than using <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> and a <code class="docutils literal notranslate"><span class="pre">TypedDict</span></code>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="n">name</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">year</span><span class="p">:</span> <span class="nb">int</span><span class="p">):</span> <span class="o">...</span> <span class="c1"># Preferred way.</span>
<span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="n">Unpack</span><span class="p">[</span><span class="n">Movie</span><span class="p">]):</span> <span class="o">...</span>
</pre></div>
</div>
<p>Similarly, when type hinting third party libraries via stubs it is again better
to state the function signature explicitly - this is the only way to type such
a function if it has default arguments. Another issue that may arise in this
case when trying to type hint the function with a <code class="docutils literal notranslate"><span class="pre">TypedDict</span></code> is that some
standard function parameters may be treated as keyword only:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">year</span><span class="p">):</span> <span class="o">...</span> <span class="c1"># Function in a third party library.</span>
<span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="n">Unpack</span><span class="p">[</span><span class="n">Movie</span><span class="p">]):</span> <span class="o">...</span> <span class="c1"># Function signature in a stub file.</span>
<span class="n">foo</span><span class="p">(</span><span class="s2">&quot;Life of Brian&quot;</span><span class="p">,</span> <span class="mi">1979</span><span class="p">)</span> <span class="c1"># This would be now failing type</span>
<span class="c1"># checking but is fine.</span>
<span class="n">foo</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s2">&quot;Life of Brian&quot;</span><span class="p">,</span> <span class="n">year</span><span class="o">=</span><span class="mi">1979</span><span class="p">)</span> <span class="c1"># This would be the only way to call</span>
<span class="c1"># the function now that passes type</span>
<span class="c1"># checking.</span>
</pre></div>
</div>
<p>Therefore, in this case it is again preferred to type hint such function
explicitly as:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="n">name</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">year</span><span class="p">:</span> <span class="nb">int</span><span class="p">):</span> <span class="o">...</span>
</pre></div>
</div>
<p>Also, for the benefit of IDEs and documentation pages, functions that are part
of the public API should prefer explicit keyword parameters whenever possible.</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 PEP could be linked in the <code class="docutils literal notranslate"><span class="pre">typing</span></code> modules documentation. Moreover, a
new section on using <code class="docutils literal notranslate"><span class="pre">Unpack</span></code> could be added to the aforementioned docs.
Similar sections could be also added to the
<a class="reference external" href="https://mypy.readthedocs.io/">mypy documentation</a> and the
<a class="reference external" href="https://typing.readthedocs.io/">typing RTD documentation</a>.</p>
</section>
<section id="reference-implementation">
<h2><a class="toc-backref" href="#reference-implementation" role="doc-backlink">Reference Implementation</a></h2>
<p>The <a class="reference external" href="https://github.com/python/mypy">mypy type checker</a> already
<a class="reference external" href="https://github.com/python/mypy/pull/13471">supports</a> more precise
<code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> typing using <code class="docutils literal notranslate"><span class="pre">Unpack</span></code>.</p>
<p><a class="reference external" href="https://github.com/microsoft/pyright">Pyright type checker</a> also
<a class="reference external" href="https://github.com/microsoft/pyright/commit/5bee749eb171979e3f526cd8e5bf66b00593378a">provides provisional support</a>
for <a class="reference external" href="https://github.com/microsoft/pyright/issues/3002">this feature</a>.</p>
</section>
<section id="rejected-ideas">
<h2><a class="toc-backref" href="#rejected-ideas" role="doc-backlink">Rejected Ideas</a></h2>
<section id="typeddict-unions">
<h3><a class="toc-backref" href="#typeddict-unions" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">TypedDict</span></code> unions</a></h3>
<p>It is possible to create unions of typed dictionaries. However, supporting
typing <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> with a union of typed dicts would greatly increase the
complexity of the implementation of this PEP and there seems to be no
compelling use case to justify the support for this. Therefore, using unions of
typed dictionaries to type <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> as described in the context of this PEP
can result in an error:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Book</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">):</span>
<span class="n">genre</span><span class="p">:</span> <span class="nb">str</span>
<span class="n">pages</span><span class="p">:</span> <span class="nb">int</span>
<span class="n">TypedDictUnion</span> <span class="o">=</span> <span class="n">Movie</span> <span class="o">|</span> <span class="n">Book</span>
<span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="n">Unpack</span><span class="p">[</span><span class="n">TypedDictUnion</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span> <span class="o">...</span> <span class="c1"># WRONG! Unsupported use</span>
<span class="c1"># of a union of</span>
<span class="c1"># TypedDicts to type</span>
<span class="c1"># **kwargs</span>
</pre></div>
</div>
<p>Instead, a function that expects a union of <code class="docutils literal notranslate"><span class="pre">TypedDict</span></code>s can be
overloaded:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@overload</span>
<span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="n">Unpack</span><span class="p">[</span><span class="n">Movie</span><span class="p">]):</span> <span class="o">...</span>
<span class="nd">@overload</span>
<span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="n">Unpack</span><span class="p">[</span><span class="n">Book</span><span class="p">]):</span> <span class="o">...</span>
</pre></div>
</div>
</section>
<section id="changing-the-meaning-of-kwargs-annotations">
<h3><a class="toc-backref" href="#changing-the-meaning-of-kwargs-annotations" role="doc-backlink">Changing the meaning of <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> annotations</a></h3>
<p>One way to achieve the purpose of this PEP would be to change the
meaning of <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> annotations, so that the annotations would
apply to the entire <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> dict, not to individual elements.
For consistency, we would have to make an analogous change to <code class="docutils literal notranslate"><span class="pre">*args</span></code>
annotations.</p>
<p>This idea was discussed in a meeting of the typing community, and the
consensus was that the change would not be worth the cost. There is no
clear migration path, the current meaning of <code class="docutils literal notranslate"><span class="pre">*args</span></code> and <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code>
annotations is well-established in the ecosystem, and type checkers
would have to introduce new errors for code that is currently legal.</p>
</section>
<section id="introducing-a-new-syntax">
<h3><a class="toc-backref" href="#introducing-a-new-syntax" role="doc-backlink">Introducing a new syntax</a></h3>
<p>In the previous versions of this PEP, using a double asterisk syntax was
proposed to support more precise <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> typing. Using this syntax,
functions could be annotated as follows:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="o">**</span><span class="n">Movie</span><span class="p">):</span> <span class="o">...</span>
</pre></div>
</div>
<p>Which would have the same meaning as:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="n">Unpack</span><span class="p">[</span><span class="n">Movie</span><span class="p">]):</span> <span class="o">...</span>
</pre></div>
</div>
<p>This greatly increased the scope of the PEP, as it would require a grammar
change and adding a new dunder for the <code class="docutils literal notranslate"><span class="pre">Unpack</span></code> special form. At the same
the justification for introducing a new syntax was not strong enough and
became a blocker for the whole PEP. Therefore, we decided to abandon the idea
of introducing a new syntax as a part of this PEP and may propose it again in a
separate one.</p>
</section>
</section>
<section id="copyright">
<h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2>
<p>This document is placed in the public domain or under the
CC0-1.0-Universal license, whichever is more permissive.</p>
</section>
</section>
<hr class="docutils" />
<p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0692.rst">https://github.com/python/peps/blob/main/peps/pep-0692.rst</a></p>
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0692.rst">2024-02-16 16:12:21 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="#rationale">Rationale</a></li>
<li><a class="reference internal" href="#specification">Specification</a><ul>
<li><a class="reference internal" href="#function-calls-with-standard-dictionaries">Function calls with standard dictionaries</a></li>
<li><a class="reference internal" href="#keyword-collisions">Keyword collisions</a></li>
<li><a class="reference internal" href="#required-and-non-required-keys">Required and non-required keys</a></li>
<li><a class="reference internal" href="#assignment">Assignment</a><ul>
<li><a class="reference internal" href="#source-and-destination-contain-kwargs">Source and destination contain <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code></a></li>
<li><a class="reference internal" href="#source-contains-kwargs-and-destination-doesn-t">Source contains <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> and destination doesnt</a></li>
<li><a class="reference internal" href="#source-contains-untyped-kwargs">Source contains untyped <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code></a></li>
<li><a class="reference internal" href="#source-contains-traditionally-typed-kwargs-t">Source contains traditionally typed <code class="docutils literal notranslate"><span class="pre">**kwargs:</span> <span class="pre">T</span></code></a></li>
</ul>
</li>
<li><a class="reference internal" href="#passing-kwargs-inside-a-function-to-another-function">Passing kwargs inside a function to another function</a></li>
<li><a class="reference internal" href="#using-unpack-with-types-other-than-typeddict">Using <code class="docutils literal notranslate"><span class="pre">Unpack</span></code> with types other than <code class="docutils literal notranslate"><span class="pre">TypedDict</span></code></a></li>
<li><a class="reference internal" href="#changes-to-unpack">Changes to <code class="docutils literal notranslate"><span class="pre">Unpack</span></code></a></li>
</ul>
</li>
<li><a class="reference internal" href="#intended-usage">Intended Usage</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="#typeddict-unions"><code class="docutils literal notranslate"><span class="pre">TypedDict</span></code> unions</a></li>
<li><a class="reference internal" href="#changing-the-meaning-of-kwargs-annotations">Changing the meaning of <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> annotations</a></li>
<li><a class="reference internal" href="#introducing-a-new-syntax">Introducing a new syntax</a></li>
</ul>
</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-0692.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>