Skip to content

Commit c8917dc

Browse files
PEP 835: Address Discourse feedback from encukou and tjreedy
1 parent f101f94 commit c8917dc

1 file changed

Lines changed: 47 additions & 40 deletions

File tree

peps/pep-0835.rst

Lines changed: 47 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -229,45 +229,15 @@ Supported Left-Hand Operands
229229
-----------------------------
230230

231231
The ``@`` operator is implemented by adding ``nb_matrix_multiply`` to the
232-
metatype (``type``) and to several typing-related types. The operator is
232+
metatype (``type``) and to several types used in type hinting (including
233+
``NoneType``). The operator is
233234
supported for any left-hand operand that currently supports the ``|``
234-
operator for making a union.
235+
operator for making a union (including ``type(None)``, which acts as the
236+
canonical equivalent to ``None`` in type hints).
235237

236238
For all other left-hand operands, the operator returns ``NotImplemented``,
237239
allowing normal ``__matmul__`` dispatch to proceed.
238240

239-
Parsing and Grammar
240-
===================
241-
242-
This proposal requires no changes to the Python grammar. Because ``@`` is
243-
already a valid operator, it is natively parsed as a binary operation. The
244-
shorthand is resolved during semantic analysis, entirely bypassing the need
245-
to patch grammar files or update the parser.
246-
247-
How to Teach This
248-
=================
249-
250-
In Python, the ``@`` symbol already has an established association with
251-
metadata through decorators. The annotation shorthand extends this
252-
intuition to the type system: ``int @ Field(gt=0)`` reads as "``int``,
253-
decorated with ``Field(gt=0)``."
254-
255-
For beginners, the key rule is simple: **in a type annotation, ``@`` means
256-
"with this metadata."** The full ``Annotated[int, Field(gt=0)]`` syntax
257-
remains available and is entirely equivalent for those who find it clearer.
258-
259-
For experienced developers, the precedence rules follow standard Python
260-
operator precedence (``@`` binds tighter than ``|``), and chaining
261-
``T @ m1 @ m2`` flattens exactly as nested ``Annotated`` does.
262-
263-
Documentation and teaching materials should introduce the shorthand alongside
264-
``Annotated``, not as a replacement. The longhand form is still preferred in
265-
contexts where multiple metadata items are passed as a group and chaining
266-
would be unwieldy.
267-
268-
Backwards Compatibility
269-
=======================
270-
271241
Forward References and Deferred Evaluation
272242
-------------------------------------------
273243

@@ -277,8 +247,9 @@ module provides several formats for retrieving annotations:
277247
- ``Format.VALUE``: Fully evaluates the annotation. Raises ``NameError``
278248
if any name is unresolvable.
279249
- ``Format.FORWARDREF``: Wraps unresolvable names in ``ForwardRef`` objects.
280-
However, compound expressions using operators (``@``, ``|``) produce an
281-
opaque ``ForwardRef`` string containing the entire expression.
250+
However, when operators like ``@`` or ``|`` fail to evaluate because of an
251+
unresolvable name, this format falls back to returning the entire expression
252+
as an opaque string wrapped in a single ``ForwardRef`` object.
282253
- ``Format.STRING``: Returns the raw source text with no evaluation.
283254

284255
For the ``@`` operator, ``Format.FORWARDREF`` is insufficient. Consider::
@@ -293,8 +264,8 @@ forward reference is resolved. This is a blocking issue for libraries like
293264
Pydantic and FastAPI, which inspect metadata at class-definition time.
294265

295266
This proposal introduces a new format, ``Format.FORWARDREF_STRUCTURAL``.
296-
This format assumes typing semantics and evaluates compound type expressions
297-
**structurally**. It always returns an ``AnnotatedType`` for ``@``, a union
267+
This format assumes typing semantics and evaluates type expressions involving
268+
operators (like ``@`` and ``|``) **structurally**. It always returns an ``AnnotatedType`` for ``@``, a union
298269
for ``|``, and a ``GenericAlias`` for subscripting. When a name cannot be
299270
resolved, only that name is wrapped in ``ForwardRef``; the surrounding
300271
operators are still evaluated. The example above produces::
@@ -304,7 +275,10 @@ operators are still evaluated. The example above produces::
304275
The metadata is immediately accessible. This format also resolves the
305276
pre-existing issue with ``|`` unions, where ``"Foo" | int`` under
306277
``Format.FORWARDREF`` produces ``ForwardRef('Foo | int')`` instead of the
307-
structural ``ForwardRef('Foo') | int``.
278+
structural ``ForwardRef('Foo') | int``. Notably, if ``FORWARDREF_STRUCTURAL``
279+
becomes the default evaluation strategy for type hints, it could eventually
280+
reduce or eliminate the need for runtime type objects to implement
281+
``__matmul__``.
308282

309283
Interaction with PEP 563
310284
^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -321,6 +295,38 @@ coarser than from :pep:`749` thunks. When a name is unresolvable, the
321295
rather than just a name. :pep:`749` provides a strictly better experience and
322296
is the recommended path forward.
323297

298+
Parsing and Grammar
299+
===================
300+
301+
This proposal requires no changes to the Python grammar. Because ``@`` is
302+
already a valid operator, it is natively parsed as a binary operation. The
303+
shorthand is resolved during semantic analysis, entirely bypassing the need
304+
to patch grammar files or update the parser.
305+
306+
How to Teach This
307+
=================
308+
309+
In Python, the ``@`` symbol already has an established association with
310+
metadata through decorators. The annotation shorthand extends this
311+
intuition to the type system: ``int @ Field(gt=0)`` reads as "``int``,
312+
decorated with ``Field(gt=0)``."
313+
314+
For beginners, the key rule is simple: **in a type annotation, ``@`` means
315+
"with this metadata."** The full ``Annotated[int, Field(gt=0)]`` syntax
316+
remains available and is entirely equivalent for those who find it clearer.
317+
318+
For experienced developers, the precedence rules follow standard Python
319+
operator precedence (``@`` binds tighter than ``|``), and chaining
320+
``T @ m1 @ m2`` flattens exactly as nested ``Annotated`` does.
321+
322+
Documentation and teaching materials should introduce the shorthand alongside
323+
``Annotated``, not as a replacement. The longhand form is still preferred in
324+
contexts where multiple metadata items are passed as a group and chaining
325+
would be unwieldy.
326+
327+
Backwards Compatibility
328+
=======================
329+
324330
Operator Overloading
325331
--------------------
326332

@@ -354,7 +360,8 @@ metaclass.
354360
The private ``typing._AnnotatedAlias`` class is retained as a deprecated
355361
compatibility shim. Code using ``isinstance(x, typing._AnnotatedAlias)``
356362
will continue to work but emit a ``DeprecationWarning``. The shim is
357-
scheduled for removal in Python 3.18.
363+
scheduled for removal in Python 3.21 (following the standard 5-year deprecation
364+
policy outlined in :pep:`387`).
358365

359366
Code that should be updated:
360367

0 commit comments

Comments
 (0)