Skip to content

Commit beb5d15

Browse files
committed
Prepare PR
1 parent 514dfcc commit beb5d15

File tree

1 file changed

+56
-42
lines changed

1 file changed

+56
-42
lines changed

peps/pep-0821.rst

Lines changed: 56 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
PEP: 821
22
Title: Support for unpacking TypedDicts in Callable type hints
33
Author: Daniel Sperber <github.blurry@9ox.net>
4+
Sponsor: Pending
45
Discussions-To: Pending
56
Status: Draft
67
Type: Standards Track
78
Topic: Typing
89
Created: 30-Dec-2025
910
Python-Version: 3.15
10-
Post-History: `28-Jun-2025 <https://discuss.python.org/t/pep-idea-extend-spec-of-callable-to-accept-unpacked-typedicts-to-specify-keyword-only-parameters/96975>`__,
11-
11+
Post-History: `28-Jun-2025 <https://discuss.python.org/t/pep-idea-extend-spec-of-callable-to-accept-unpacked-typedicts-to-specify-keyword-only-parameters/96975>`__
1212

1313
Abstract
1414
========
@@ -53,6 +53,16 @@ more succinctly::
5353
type KwCallable = Callable[[Unpack[Signature]], Any]
5454

5555

56+
Rationale
57+
=========
58+
59+
This proposal extends the existing Callable semantics by reusing a ``TypedDict``'s
60+
keyed structure for keyword arguments. It avoids verbose Protocol-based
61+
callable definitions while remaining compatible with current typing concepts
62+
(:pep:`692` Unpack for ``kwargs``, and :pep:`728` ``extra_items``). It preserves backward
63+
compatibility by being purely a typing feature.
64+
65+
5666
Specification
5767
=============
5868

@@ -80,6 +90,7 @@ Semantics
8090
* Required keys must be accepted, but may correspond to parameters with a
8191
default value.
8292
* ``NotRequired`` keys must still be accepted, but may be omitted at call sites.
93+
This respectively applies to ``TypedDict`` with ``total=False``.
8394
* Functions with ``**kwargs`` are compatible if the annotation of ``**kwargs``
8495
matches or is a supertype of the ``TypedDict`` values.
8596
* ``extra_items`` from PEP 728 is respected: functions accepting additional
@@ -125,11 +136,11 @@ rejected.
125136
Optional arguments
126137
------------------
127138

128-
Keys marked ``NotRequired`` in the ``TypedDict`` correspond to optional
129-
keyword arguments.
130-
Meaning the callable must accept them, but callers may omit them.
131-
Functions that accept the keyword argument must also provide a default value that is compatible;
132-
functions that omit the parameter entirely are rejected.
139+
Keys marked ``NotRequired`` in the ``TypedDict`` correspond to optional
140+
keyword arguments.
141+
Meaning the callable must accept them, but callers may omit them.
142+
Functions that accept the keyword argument must also provide a default value that is compatible;
143+
functions that omit the parameter entirely are rejected.
133144

134145
.. code-block:: python
135146
@@ -196,7 +207,7 @@ arguments beyond those declared are expected.
196207
Interaction with ``extra_items`` (PEP 728)
197208
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
198209

199-
If a ``TypedDict`` specifies the ``extra_items`` parameter, the corresponding ``Callable``
210+
If a ``TypedDict`` specifies the ``extra_items`` parameter (with the exemption of ``extra_items=Never``), the corresponding ``Callable``
200211
must accept additional keyword arguments of the specified type.
201212

202213
For example:
@@ -220,11 +231,13 @@ For example:
220231
e1(a=1, b=2) # Rejected (b must be str)
221232
222233
223-
Interaction with ParamSpec:
234+
Interaction with ``ParamSpec`` and ``Concatenate``
235+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
224236

225-
``ParamSpec`` can be combined with ``Unpack[TypedDict]`` to define a
237+
A ``ParamSpec`` can be substituted by ``Unpack[TypedDict]`` to define a
226238
parameterized callable alias. Substituting ``Unpack[Signature]`` produces the
227239
same effect as writing the callable with an unpacked ``TypedDict`` directly.
240+
Using a ``TypedDict`` within ``Concatenate`` is not allowed.
228241

229242
.. code-block:: python
230243
@@ -278,26 +291,6 @@ Backwards Compatibility
278291
This feature is additive. Existing code is unaffected. Runtime behavior does
279292
not change; this is a typing-only feature.
280293

281-
282-
Reference Implementation
283-
========================
284-
285-
A prototype exists in mypy:
286-
https://github.com/python/mypy/pull/16083
287-
288-
Open Questions
289-
==============
290-
291-
* Should combining ``Unpack[TD]`` with ``Concatenate`` and ``ParamSpec`` be
292-
supported in the future? With such support, one could write
293-
``Callable[Concatenate[int, Unpack[TD], P], R]`` which in turn would allow a keyword-only parameter between ``*args`` and ``**kwargs``, i.e.
294-
``def func(*args: Any, a: int, **kwargs: Any) -> R: ...`` which is currently not allowed per PEP 612.
295-
To keep the initial implementation simple, this PEP does not propose such
296-
support.
297-
* Should multiple ``TypedDict`` unpacks be allowed to form a union, and if so, how to handle
298-
overlapping keys?
299-
300-
301294
How to Teach This
302295
=================
303296

@@ -317,24 +310,45 @@ taught that with
317310
using ``closed=True`` for definitions will create the more intuitive equivalence
318311
of ``__call__(self, a: int, b: str = ...) -> R``
319312

320-
Alternatives
321-
============
322313

323-
This (`discussion thread <https://discuss.python.org/t/pep-677-with-an-easier-to-parse-and-more-expressive-syntax/98408/33>`__)
324-
revisits the idea of the rejected :pep:`677`
325-
to introduce alternative and more expressive syntax for callable type hints.
314+
Reference Implementation
315+
========================
316+
317+
A prototype exists in mypy:
318+
https://github.com/python/mypy/pull/16083
319+
320+
321+
Rejected Ideas
322+
==============
323+
324+
- Combining ``Unpack[TD]`` with ``Concatenate``. With such support, one could write
325+
``Callable[Concatenate[int, Unpack[TD], P], R]`` which in turn would allow a keyword-only parameter between ``*args`` and ``**kwargs``, i.e.
326+
``def func(*args: Any, a: int, **kwargs: Any) -> R: ...`` which is currently not allowed per :pep:`612`.
327+
To keep the initial implementation simple, this PEP does not propose such
328+
support.
329+
330+
Open Questions
331+
==============
332+
333+
* Should multiple ``TypedDict`` unpacks be allowed to form a union, and if so, how to handle
334+
overlapping keys of non-identical types? Which restrictions should apply in such a case? Should the order matter?
335+
* Is there a necessity to differentiate between normal and ``ReadOnly`` keys?
336+
* Is it necessary to specify generic behavior for ``TypedDict`` and the resulting ``Callable`` when the ``TypedDict`` itself is generic?
337+
338+
339+
Acknowledgements
340+
================
341+
TODO
342+
326343

327344

328345
References
329346
==========
330347

331-
* PEP 484 - Type Hints
332-
* PEP 612 - ParamSpec
333-
* PEP 646 - Variadic Generics
334-
* PEP 692 - Using ``Unpack`` with ``**kwargs``
335-
* PEP 728 - ``extra_items`` in TypedDict
336-
* PEP 764 - Inline TypedDict
337-
* mypy PR #16083 - Prototype support
348+
* :pep:`692` - Using ``Unpack`` with ``**kwargs``
349+
* :pep:`728` - ``extra_items`` in TypedDict
350+
* :pep:`764` - Inline TypedDict
351+
* `mypy PR #16083 - Prototype support <https://github.com/python/mypy/pull/16083>`__
338352
* Revisiting PEP 677 (`discussion thread <https://discuss.python.org/t/pep-677-with-an-easier-to-parse-and-more-expressive-syntax/98408/33>`__)
339353

340354

0 commit comments

Comments
 (0)