Skip to content

Commit 7ee882a

Browse files
committed
Overhaul docs for typing.Annotated
1 parent fbdee00 commit 7ee882a

File tree

2 files changed

+78
-54
lines changed

2 files changed

+78
-54
lines changed

Doc/library/typing.rst

Lines changed: 70 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1194,40 +1194,36 @@ These can be used as types in annotations using ``[]``, each having a unique syn
11941194

11951195
.. data:: Annotated
11961196

1197-
A type, introduced in :pep:`593` (``Flexible function and variable
1198-
annotations``), to decorate existing types with context-specific metadata
1199-
(possibly multiple pieces of it, as ``Annotated`` is variadic).
1200-
Specifically, a type ``T`` can be annotated with metadata ``x`` via the
1201-
typehint ``Annotated[T, x]``. This metadata can be used for either static
1202-
analysis or at runtime: at runtime, it is stored in a :attr:`__metadata__`
1203-
attribute. If a library (or tool) encounters a typehint
1204-
``Annotated[T, x]`` and has no special logic for metadata ``x``, it
1205-
should ignore it and simply treat the type as ``T``. Unlike the
1206-
``no_type_check`` functionality that currently exists in the ``typing``
1207-
module which completely disables typechecking annotations on a function
1208-
or a class, the ``Annotated`` type allows for both static typechecking
1209-
of ``T`` (which can safely ignore ``x``)
1210-
together with runtime access to ``x`` within a specific application.
1211-
1212-
Ultimately, the responsibility of how to interpret the annotations (if
1213-
at all) is the responsibility of the tool or library encountering the
1214-
``Annotated`` type. A tool or library encountering an ``Annotated`` type
1215-
can scan through the annotations to determine if they are of interest
1216-
(e.g., using ``isinstance()``).
1217-
1218-
When a tool or a library does not support annotations or encounters an
1219-
unknown annotation it should just ignore it and treat annotated type as
1220-
the underlying type.
1221-
1222-
It's up to the tool consuming the annotations to decide whether the
1223-
client is allowed to have several annotations on one type and how to
1224-
merge those annotations.
1225-
1226-
Since the ``Annotated`` type allows you to put several annotations of
1227-
the same (or different) type(s) on any node, the tools or libraries
1228-
consuming those annotations are in charge of dealing with potential
1229-
duplicates. For example, if you are doing value range analysis you might
1230-
allow this:
1197+
Special typing form to add context-specific metadata to an annotation.
1198+
1199+
Add metadata ``x`` to a given type ``T`` by using the annotation
1200+
``Annotated[T, x]``. Metadata added using ``Annotated`` can be used by
1201+
static analysis tools or at runtime. At runtime, the metadata is stored
1202+
in a :attr:`!__metadata__` attribute.
1203+
1204+
If a library or tool encounters an annotation ``Annotated[T, x]`` and has
1205+
no special logic for the metadata, it should ignore the metadata and simply
1206+
treat the annotation as ``T``. As such, ``Annotated`` can be useful for code
1207+
that wants to use annotations for purposes outside Python's static typing
1208+
system.
1209+
1210+
Using ``Annotated[T, x]`` as an annotation still allows for static
1211+
typechecking of ``T``, as type checkers will simply ignore the metadata ``x``.
1212+
In this way, ``Annotated`` differs from the
1213+
:func:`@no_type_check <no_type_check>` decorator, which can also be used for
1214+
adding annotations outside the scope of the typing system, but
1215+
completely disables typechecking for a function or class.
1216+
1217+
The responsibility of how to interpret the metadata
1218+
lies with the the tool or library encountering an
1219+
``Annotated`` annotation. A tool or library encountering an ``Annotated`` type
1220+
can scan through the metadata elements to determine if they are of interest
1221+
(e.g., using :func:`isinstance`).
1222+
1223+
.. describe:: Annotated[<type>, <metadata>]
1224+
1225+
Here is an example of how you might use ``Annotated`` to add metadata to
1226+
type annotations if you were doing range analysis:
12311227

12321228
.. testcode::
12331229

@@ -1239,14 +1235,11 @@ These can be used as types in annotations using ``[]``, each having a unique syn
12391235
T1 = Annotated[int, ValueRange(-10, 5)]
12401236
T2 = Annotated[T1, ValueRange(-20, 3)]
12411237

1242-
Passing ``include_extras=True`` to :func:`get_type_hints` lets one
1243-
access the extra annotations at runtime.
1244-
1245-
The details of the syntax:
1238+
Details of the syntax:
12461239

12471240
* The first argument to ``Annotated`` must be a valid type
12481241

1249-
* Multiple type annotations are supported (``Annotated`` supports variadic
1242+
* Multiple metadata elements can be supplied (``Annotated`` supports variadic
12501243
arguments)::
12511244

12521245
@dataclass
@@ -1255,24 +1248,28 @@ These can be used as types in annotations using ``[]``, each having a unique syn
12551248

12561249
Annotated[int, ValueRange(3, 10), ctype("char")]
12571250

1258-
* ``Annotated`` must be called with at least two arguments (
1251+
It is up to the tool consuming the annotations to decide whether the
1252+
client is allowed to add multiple metadata elements to one annotation and how to
1253+
merge those annotations.
1254+
1255+
* ``Annotated`` must be subscripted with at least two arguments (
12591256
``Annotated[int]`` is not valid)
12601257

1261-
* The order of the annotations is preserved and matters for equality
1258+
* The order of the metadata elements is preserved and matters for equality
12621259
checks::
12631260

12641261
assert Annotated[int, ValueRange(3, 10), ctype("char")] != Annotated[
12651262
int, ctype("char"), ValueRange(3, 10)
12661263
]
12671264

1268-
* Nested ``Annotated`` types are flattened, with metadata ordered
1269-
starting with the innermost annotation::
1265+
* Nested ``Annotated`` types are flattened. The order of the metadata elements
1266+
starts with the innermost annotation::
12701267

12711268
assert Annotated[Annotated[int, ValueRange(3, 10)], ctype("char")] == Annotated[
12721269
int, ValueRange(3, 10), ctype("char")
12731270
]
12741271

1275-
* Duplicated annotations are not removed::
1272+
* Duplicated metadata elements are not removed::
12761273

12771274
assert Annotated[int, ValueRange(3, 10)] != Annotated[
12781275
int, ValueRange(3, 10), ValueRange(3, 10)
@@ -1292,12 +1289,32 @@ These can be used as types in annotations using ``[]``, each having a unique syn
12921289
# ``Annotated[list[tuple[int, int]], MaxLen(10)]``:
12931290
type V = Vec[int]
12941291

1295-
.. attribute:: __metadata__
1292+
* ``Annotated`` cannot be used with an unpacked :class:`TypeVarTuple`::
1293+
1294+
type Variadic[*Ts] = Annotated[*Ts, Ann1] # NOT valid
1295+
1296+
This would be equivalent to::
12961297

1297-
At runtime, the metadata associated with an ``Annotated`` type can be
1298-
retrieved via the ``__metadata__`` attribute.
1298+
Annotated[T1, T2, T3, ..., Ann1]
12991299

1300-
For example:
1300+
where ``T1``, ``T2``, etc. are :class:`TypeVars <TypeVar>`. This would be
1301+
invalid: only one type should be passed to Annotated.
1302+
1303+
* By default, :func:`get_type_hints` strips the metadata from annotations.
1304+
Pass ``include_extras=True`` to have the metadata preserved:
1305+
1306+
.. doctest::
1307+
1308+
>>> from typing import Annotated, get_type_hints
1309+
>>> def func(x: Annotated[int, "metadata"]) -> None: pass
1310+
...
1311+
>>> get_type_hints(func)
1312+
{'x': <class 'int'>, 'return': <class 'NoneType'>}
1313+
>>> get_type_hints(func, include_extras=True)
1314+
{'x': typing.Annotated[int, 'metadata'], 'return': <class 'NoneType'>}
1315+
1316+
* At runtime, the metadata associated with an ``Annotated`` type can be
1317+
retrieved via the :attr:`!__metadata__` attribute:
13011318

13021319
.. doctest::
13031320

@@ -1308,6 +1325,11 @@ These can be used as types in annotations using ``[]``, each having a unique syn
13081325
>>> X.__metadata__
13091326
('very', 'important', 'metadata')
13101327

1328+
.. seealso::
1329+
1330+
:pep:`593` - Flexible function and variable annotations
1331+
The PEP introducing ``Annotated`` to the standard library.
1332+
13111333
.. versionadded:: 3.9
13121334

13131335

Lib/typing.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2015,7 +2015,7 @@ class Annotated:
20152015
20162016
assert Annotated[int, '$'].__metadata__ == ('$',)
20172017
2018-
- Nested Annotated are flattened::
2018+
- Nested Annotated types are flattened::
20192019
20202020
assert Annotated[Annotated[T, Ann1, Ann2], Ann3] == Annotated[T, Ann1, Ann2, Ann3]
20212021
@@ -2026,15 +2026,17 @@ class Annotated:
20262026
20272027
- Annotated can be used as a generic type alias::
20282028
2029-
Optimized = Annotated[T, runtime.Optimize()]
2030-
assert Optimized[int] == Annotated[int, runtime.Optimize()]
2029+
type Optimized[T] = Annotated[T, runtime.Optimize()]
2030+
# type checker will treat Optimized[int]
2031+
# as equivalent to Annotated[int, runtime.Optimize()]
20312032
2032-
OptimizedList = Annotated[List[T], runtime.Optimize()]
2033-
assert OptimizedList[int] == Annotated[List[int], runtime.Optimize()]
2033+
type OptimizedList[T] = Annotated[list[T], runtime.Optimize()]
2034+
# type checker will treat OptimizedList[int]
2035+
# as equivalent to Annotated[list[int], runtime.Optimize()]
20342036
20352037
- Annotated cannot be used with an unpacked TypeVarTuple::
20362038
2037-
Annotated[*Ts, Ann1] # NOT valid
2039+
type Variadic[*Ts] = Annotated[*Ts, Ann1] # NOT valid
20382040
20392041
This would be equivalent to::
20402042

0 commit comments

Comments
 (0)