summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/typing.rst132
-rw-r--r--Lib/typing.py14
2 files changed, 85 insertions, 61 deletions
diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst
index 3fa0651..f9b44e1 100644
--- a/Doc/library/typing.rst
+++ b/Doc/library/typing.rst
@@ -1194,40 +1194,36 @@ These can be used as types in annotations using ``[]``, each having a unique syn
.. data:: Annotated
- A type, introduced in :pep:`593` (``Flexible function and variable
- annotations``), to decorate existing types with context-specific metadata
- (possibly multiple pieces of it, as ``Annotated`` is variadic).
- Specifically, a type ``T`` can be annotated with metadata ``x`` via the
- typehint ``Annotated[T, x]``. This metadata can be used for either static
- analysis or at runtime: at runtime, it is stored in a :attr:`__metadata__`
- attribute. If a library (or tool) encounters a typehint
- ``Annotated[T, x]`` and has no special logic for metadata ``x``, it
- should ignore it and simply treat the type as ``T``. Unlike the
- ``no_type_check`` functionality that currently exists in the ``typing``
- module which completely disables typechecking annotations on a function
- or a class, the ``Annotated`` type allows for both static typechecking
- of ``T`` (which can safely ignore ``x``)
- together with runtime access to ``x`` within a specific application.
-
- Ultimately, the responsibility of how to interpret the annotations (if
- at all) is the responsibility of the tool or library encountering the
- ``Annotated`` type. A tool or library encountering an ``Annotated`` type
- can scan through the annotations to determine if they are of interest
- (e.g., using ``isinstance()``).
-
- When a tool or a library does not support annotations or encounters an
- unknown annotation it should just ignore it and treat annotated type as
- the underlying type.
-
- It's up to the tool consuming the annotations to decide whether the
- client is allowed to have several annotations on one type and how to
- merge those annotations.
-
- Since the ``Annotated`` type allows you to put several annotations of
- the same (or different) type(s) on any node, the tools or libraries
- consuming those annotations are in charge of dealing with potential
- duplicates. For example, if you are doing value range analysis you might
- allow this:
+ Special typing form to add context-specific metadata to an annotation.
+
+ Add metadata ``x`` to a given type ``T`` by using the annotation
+ ``Annotated[T, x]``. Metadata added using ``Annotated`` can be used by
+ static analysis tools or at runtime. At runtime, the metadata is stored
+ in a :attr:`!__metadata__` attribute.
+
+ If a library or tool encounters an annotation ``Annotated[T, x]`` and has
+ no special logic for the metadata, it should ignore the metadata and simply
+ treat the annotation as ``T``. As such, ``Annotated`` can be useful for code
+ that wants to use annotations for purposes outside Python's static typing
+ system.
+
+ Using ``Annotated[T, x]`` as an annotation still allows for static
+ typechecking of ``T``, as type checkers will simply ignore the metadata ``x``.
+ In this way, ``Annotated`` differs from the
+ :func:`@no_type_check <no_type_check>` decorator, which can also be used for
+ adding annotations outside the scope of the typing system, but
+ completely disables typechecking for a function or class.
+
+ The responsibility of how to interpret the metadata
+ lies with the the tool or library encountering an
+ ``Annotated`` annotation. A tool or library encountering an ``Annotated`` type
+ can scan through the metadata elements to determine if they are of interest
+ (e.g., using :func:`isinstance`).
+
+ .. describe:: Annotated[<type>, <metadata>]
+
+ Here is an example of how you might use ``Annotated`` to add metadata to
+ type annotations if you were doing range analysis:
.. testcode::
@@ -1239,14 +1235,11 @@ These can be used as types in annotations using ``[]``, each having a unique syn
T1 = Annotated[int, ValueRange(-10, 5)]
T2 = Annotated[T1, ValueRange(-20, 3)]
- Passing ``include_extras=True`` to :func:`get_type_hints` lets one
- access the extra annotations at runtime.
-
- The details of the syntax:
+ Details of the syntax:
* The first argument to ``Annotated`` must be a valid type
- * Multiple type annotations are supported (``Annotated`` supports variadic
+ * Multiple metadata elements can be supplied (``Annotated`` supports variadic
arguments)::
@dataclass
@@ -1255,24 +1248,28 @@ These can be used as types in annotations using ``[]``, each having a unique syn
Annotated[int, ValueRange(3, 10), ctype("char")]
- * ``Annotated`` must be called with at least two arguments (
+ It is up to the tool consuming the annotations to decide whether the
+ client is allowed to add multiple metadata elements to one annotation and how to
+ merge those annotations.
+
+ * ``Annotated`` must be subscripted with at least two arguments (
``Annotated[int]`` is not valid)
- * The order of the annotations is preserved and matters for equality
+ * The order of the metadata elements is preserved and matters for equality
checks::
assert Annotated[int, ValueRange(3, 10), ctype("char")] != Annotated[
int, ctype("char"), ValueRange(3, 10)
]
- * Nested ``Annotated`` types are flattened, with metadata ordered
- starting with the innermost annotation::
+ * Nested ``Annotated`` types are flattened. The order of the metadata elements
+ starts with the innermost annotation::
assert Annotated[Annotated[int, ValueRange(3, 10)], ctype("char")] == Annotated[
int, ValueRange(3, 10), ctype("char")
]
- * Duplicated annotations are not removed::
+ * Duplicated metadata elements are not removed::
assert Annotated[int, ValueRange(3, 10)] != Annotated[
int, ValueRange(3, 10), ValueRange(3, 10)
@@ -1292,21 +1289,46 @@ These can be used as types in annotations using ``[]``, each having a unique syn
# ``Annotated[list[tuple[int, int]], MaxLen(10)]``:
type V = Vec[int]
- .. attribute:: __metadata__
+ * ``Annotated`` cannot be used with an unpacked :class:`TypeVarTuple`::
- At runtime, the metadata associated with an ``Annotated`` type can be
- retrieved via the ``__metadata__`` attribute.
+ type Variadic[*Ts] = Annotated[*Ts, Ann1] # NOT valid
- For example:
+ This would be equivalent to::
- .. doctest::
+ Annotated[T1, T2, T3, ..., Ann1]
+
+ where ``T1``, ``T2``, etc. are :class:`TypeVars <TypeVar>`. This would be
+ invalid: only one type should be passed to Annotated.
+
+ * By default, :func:`get_type_hints` strips the metadata from annotations.
+ Pass ``include_extras=True`` to have the metadata preserved:
+
+ .. doctest::
+
+ >>> from typing import Annotated, get_type_hints
+ >>> def func(x: Annotated[int, "metadata"]) -> None: pass
+ ...
+ >>> get_type_hints(func)
+ {'x': <class 'int'>, 'return': <class 'NoneType'>}
+ >>> get_type_hints(func, include_extras=True)
+ {'x': typing.Annotated[int, 'metadata'], 'return': <class 'NoneType'>}
+
+ * At runtime, the metadata associated with an ``Annotated`` type can be
+ retrieved via the :attr:`!__metadata__` attribute:
+
+ .. doctest::
+
+ >>> from typing import Annotated
+ >>> X = Annotated[int, "very", "important", "metadata"]
+ >>> X
+ typing.Annotated[int, 'very', 'important', 'metadata']
+ >>> X.__metadata__
+ ('very', 'important', 'metadata')
+
+ .. seealso::
- >>> from typing import Annotated
- >>> X = Annotated[int, "very", "important", "metadata"]
- >>> X
- typing.Annotated[int, 'very', 'important', 'metadata']
- >>> X.__metadata__
- ('very', 'important', 'metadata')
+ :pep:`593` - Flexible function and variable annotations
+ The PEP introducing ``Annotated`` to the standard library.
.. versionadded:: 3.9
diff --git a/Lib/typing.py b/Lib/typing.py
index 0ff29fa..44b26af9 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -2021,7 +2021,7 @@ class Annotated:
assert Annotated[int, '$'].__metadata__ == ('$',)
- - Nested Annotated are flattened::
+ - Nested Annotated types are flattened::
assert Annotated[Annotated[T, Ann1, Ann2], Ann3] == Annotated[T, Ann1, Ann2, Ann3]
@@ -2032,15 +2032,17 @@ class Annotated:
- Annotated can be used as a generic type alias::
- Optimized = Annotated[T, runtime.Optimize()]
- assert Optimized[int] == Annotated[int, runtime.Optimize()]
+ type Optimized[T] = Annotated[T, runtime.Optimize()]
+ # type checker will treat Optimized[int]
+ # as equivalent to Annotated[int, runtime.Optimize()]
- OptimizedList = Annotated[List[T], runtime.Optimize()]
- assert OptimizedList[int] == Annotated[List[int], runtime.Optimize()]
+ type OptimizedList[T] = Annotated[list[T], runtime.Optimize()]
+ # type checker will treat OptimizedList[int]
+ # as equivalent to Annotated[list[int], runtime.Optimize()]
- Annotated cannot be used with an unpacked TypeVarTuple::
- Annotated[*Ts, Ann1] # NOT valid
+ type Variadic[*Ts] = Annotated[*Ts, Ann1] # NOT valid
This would be equivalent to::