Skip to content

Commit 1f97d9b

Browse files
PEP 835: Trim redundancy and tighten narrative
1 parent 9dea6b3 commit 1f97d9b

1 file changed

Lines changed: 14 additions & 30 deletions

File tree

peps/pep-0835.rst

Lines changed: 14 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -30,37 +30,33 @@ Historically, Python has treated type metadata as an afterthought.
3030
``Annotated[T, M]`` models metadata as a generic container, similar to how
3131
``Union[X, Y]`` once modeled unions. But metadata is not a container; it is a
3232
modifier applied to a type.
33-
34-
Just as :pep:`604` evolved unions from the ``Union`` container to the ``|``
35-
operator, the ``@`` syntax evolves metadata into a native language operator.
3633
Elevating metadata to first-class syntax recognizes that modern libraries
3734
increasingly use constraints on types (e.g., bounds, shapes, patterns).
3835

39-
Previously, applying these constraints forced a frustrating compromise between
40-
type safety and readability. Before :pep:`593` (``Annotated``), frameworks like
41-
Pydantic and FastAPI embedded metadata directly into default values. This was
42-
concise but broke static analysis:
36+
Previously, applying these constraints forced a compromise between type safety
37+
and readability. Before :pep:`593` (``Annotated``), frameworks like Pydantic and
38+
FastAPI embedded metadata directly into default values. This was concise but
39+
broke static analysis:
4340

4441
.. code-block::
4542
:class: bad
4643
4744
class User(BaseModel):
48-
id: int = Field(gt=0)
49-
name: str = Field(min_length=3)
45+
id: int = Field(gt=0, le=1000)
5046
51-
Moving to ``Annotated`` fixed the typing semantics but introduced significant
52-
complexity. To hide the visual noise, libraries introduced alias types (e.g.,
53-
``PositiveInt``). However, the moment users step outside pre-packaged
47+
Moving to ``Annotated`` fixed the typing semantics but introduced deep nesting
48+
and syntactic overhead. To hide the complexity, libraries introduced alias types
49+
(e.g., ``PositiveInt``). However, the moment users step outside pre-packaged
5450
constraints, they are forced back into the complex syntax::
5551

5652
from typing import Annotated
5753
from pydantic import BaseModel, Field, PositiveInt
5854

5955
class User(BaseModel):
60-
age: PositiveInt # Concise but limited
56+
age: PositiveInt # Concise but limited alias
6157
id: Annotated[int, Field(gt=0, le=1000)] # Verbose fallback
6258

63-
The ``@`` shorthand resolves the ergonomic tradeoff, restoring the conciseness
59+
The ``@`` shorthand resolves this structural tradeoff, restoring the readability
6460
of early frameworks while preserving the strict semantics of ``Annotated``:
6561

6662
.. code-block::
@@ -326,27 +322,15 @@ The metadata remains immediately accessible. This also resolves the pre-existing
326322
issue where ``"Foo" | int`` incorrectly produced ``ForwardRef('Foo | int')``
327323
under ``Format.FORWARDREF``.
328324

329-
Importantly, because ``Format.TYPE`` can evaluate structurally when runtime
330-
dunder methods are missing (falling back to ``types.UnionType`` and
331-
``types.AnnotatedType`` for the ``|`` and ``@`` operators), it parses ``None
332-
@Metadata`` without requiring ``NoneType.__matmul__``.
333-
334-
Parsing and Grammar
335-
-------------------
336-
337-
No changes to the Python grammar are required. Because ``@`` is already a valid
338-
operator, it is natively parsed as a binary operation. The shorthand is resolved
339-
during semantic analysis, entirely bypassing the need to patch grammar files or
340-
update the parser.
341-
342325
Rationale
343326
=========
344327

345-
Baking ``Annotated`` into native syntax removes ``typing`` import overhead,
328+
Integrating ``Annotated`` into native syntax removes ``typing`` import overhead,
346329
directly aiding runtime type introspection.
347330

348-
Because ``@`` is already a valid expression operator, type checkers (Mypy,
349-
Pyright) require no parser changes, handling the syntax entirely during semantic
331+
Because ``@`` is already a valid expression operator, no changes to the Python
332+
grammar are required. Type checkers (Mypy, Pyright) and other static tools
333+
require no parser changes, handling the syntax entirely during semantic
350334
analysis. We have prototyped an automated conversion rule in Ruff, enabling
351335
automated codebase migrations, and CPython prototype testing confirms that
352336
libraries like ``typer`` and ``pydantic`` work natively out of the box.

0 commit comments

Comments
 (0)