@@ -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
3232modifier 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.
3633Elevating metadata to first-class syntax recognizes that modern libraries
3734increasingly 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
5450constraints, 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
6460of 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
326322issue where ``"Foo" | int `` incorrectly produced ``ForwardRef('Foo | int') ``
327323under ``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-
342325Rationale
343326=========
344327
345- Baking ``Annotated `` into native syntax removes ``typing `` import overhead,
328+ Integrating ``Annotated `` into native syntax removes ``typing `` import overhead,
346329directly 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
350334analysis. We have prototyped an automated conversion rule in Ruff, enabling
351335automated codebase migrations, and CPython prototype testing confirms that
352336libraries like ``typer `` and ``pydantic `` work natively out of the box.
0 commit comments