Skip to content

Commit ff6bee3

Browse files
JelleZijlstracarljmAlexWaygood
authored
Annotated: propose better terminology (#1769)
Co-authored-by: Carl Meyer <[email protected]> Co-authored-by: Alex Waygood <[email protected]>
1 parent a6eeccf commit ff6bee3

File tree

1 file changed

+61
-33
lines changed

1 file changed

+61
-33
lines changed

docs/spec/qualifiers.rst

Lines changed: 61 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -213,41 +213,52 @@ following should be allowed::
213213
Syntax
214214
^^^^^^
215215

216-
``Annotated`` is parameterized with a type and an arbitrary list of
217-
Python values that represent the annotations. Here are the specific
218-
details of the syntax:
216+
``Annotated`` is parameterized with a *base expression* and at least one
217+
Python value representing associated *metadata*::
219218

220-
* The first argument to ``Annotated`` must be a valid type
219+
from typing import Annotated
221220

222-
* Multiple type annotations are supported (``Annotated`` supports variadic
221+
Annotated[BaseExpr, Metadata1, Metadata2, ...]
222+
223+
Here are the specific details of the syntax:
224+
225+
* The base expression (the first argument to ``Annotated``) must be valid
226+
in the context where it is being used:
227+
228+
* If ``Annotated`` is used in a place where arbitrary
229+
:term:`annotation expressions <annotation expression>` are allowed,
230+
the base expression may be an annotation expression.
231+
* Otherwise, the base expression must be a valid :term:`type expression`.
232+
233+
* Multiple metadata elements are supported (``Annotated`` supports variadic
223234
arguments)::
224235

225236
Annotated[int, ValueRange(3, 10), ctype("char")]
226237

227-
* ``Annotated`` must be called with at least two arguments (
228-
``Annotated[int]`` is not valid)
238+
* There must be at least one metadata element (``Annotated[int]`` is not valid)
229239

230-
* The order of the annotations is preserved and matters for equality
240+
* The order of the metadata is preserved and matters for equality
231241
checks::
232242

233243
Annotated[int, ValueRange(3, 10), ctype("char")] != Annotated[
234244
int, ctype("char"), ValueRange(3, 10)
235245
]
236246

237247
* Nested ``Annotated`` types are flattened, with metadata ordered
238-
starting with the innermost annotation::
248+
starting with the innermost ``Annotated`` expression::
239249

240250
Annotated[Annotated[int, ValueRange(3, 10)], ctype("char")] == Annotated[
241251
int, ValueRange(3, 10), ctype("char")
242252
]
243253

244-
* Duplicated annotations are not removed::
254+
* Duplicated metadata elements are not removed::
245255

246256
Annotated[int, ValueRange(3, 10)] != Annotated[
247257
int, ValueRange(3, 10), ValueRange(3, 10)
248258
]
249259

250-
* ``Annotated`` can be used with nested and generic aliases::
260+
* ``Annotated`` can be used in definition of nested and generic aliases,
261+
but only if it wraps a :term:`type expression`::
251262

252263
T = TypeVar("T")
253264
Vec = Annotated[list[tuple[T, T]], MaxLen(10)]
@@ -272,33 +283,51 @@ details of the syntax:
272283
SmallInt = Annotated[int, ValueRange(0, 100)]
273284
SmallInt(1) # Type error
274285

286+
:pep:`593` and an earlier version of this specification used the term
287+
"annotations" instead of "metadata" for the extra arguments to
288+
``Annotated``. The term "annotations" is deprecated to avoid confusion
289+
with the parameter, return, and variable annotations that are part of
290+
the Python syntax.
291+
292+
Meaning
293+
^^^^^^^
294+
295+
The metadata provided by ``Annotated`` can be used for either static
296+
or runtime analysis. If a library (or tool) encounters an instance of
297+
``Annotated[T, x]`` and has no special logic for metadata element ``x``, it
298+
should ignore it and treat the expression as equivalent to ``T``. Thus, in general,
299+
any :term:`type expression` or :term:`annotation expression` may be
300+
wrapped in ``Annotated`` without changing the meaning of the
301+
wrapped expression. However, type
302+
checkers may additionally choose to recognize particular metadata elements and use
303+
them to implement extensions to the standard type system.
304+
305+
``Annotated`` metadata may apply either to the base expression or to the symbol
306+
being annotated, or even to some other aspect of the program.
275307

276-
Consuming annotations
277-
^^^^^^^^^^^^^^^^^^^^^
308+
Consuming metadata
309+
^^^^^^^^^^^^^^^^^^
278310

279-
Ultimately, the responsibility of how to interpret the annotations (if
311+
Ultimately, deciding how to interpret the metadata (if
280312
at all) is the responsibility of the tool or library encountering the
281313
``Annotated`` type. A tool or library encountering an ``Annotated`` type
282-
can scan through the annotations to determine if they are of interest
314+
can scan through the metadata to determine if they are of interest
283315
(e.g., using ``isinstance()``).
284316

285-
**Unknown annotations:** When a tool or a library does not support
286-
annotations or encounters an unknown annotation it should just ignore it
287-
and treat annotated type as the underlying type. For example, when encountering
288-
an annotation that is not an instance of ``struct2.ctype`` to the annotations
289-
for name (e.g., ``Annotated[str, 'foo', struct2.ctype("<10s")]``), the unpack
290-
method should ignore it.
317+
**Unknown metadata:** When a tool or a library does not support
318+
metadata or encounters an unknown metadata element, it should ignore it
319+
and treat the annotation as the base expression.
291320

292-
**Namespacing annotations:** Namespaces are not needed for annotations since
293-
the class used by the annotations acts as a namespace.
321+
**Namespacing metadata:** Namespaces are not needed for metadata since
322+
the class of the metadata object acts as a namespace.
294323

295-
**Multiple annotations:** It's up to the tool consuming the annotations
296-
to decide whether the client is allowed to have several annotations on
297-
one type and how to merge those annotations.
324+
**Multiple metadata elements:** It's up to the tool consuming the metadata
325+
to decide whether the client is allowed to have several metadata elements on
326+
one annotation and how to merge those elements.
298327

299-
Since the ``Annotated`` type allows you to put several annotations of
300-
the same (or different) type(s) on any node, the tools or libraries
301-
consuming those annotations are in charge of dealing with potential
328+
Since the ``Annotated`` type allows you to put several metadata elements of
329+
the same (or different) type(s) on any annotation, the tools or libraries
330+
consuming the metadata are in charge of dealing with potential
302331
duplicates. For example, if you are doing value range analysis you might
303332
allow this::
304333

@@ -313,12 +342,11 @@ Aliases & Concerns over verbosity
313342
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
314343

315344
Writing ``typing.Annotated`` everywhere can be quite verbose;
316-
fortunately, the ability to alias annotations means that in practice we
345+
fortunately, the ability to alias types means that in practice we
317346
don't expect clients to have to write lots of boilerplate code::
318347

319-
T = TypeVar('T')
320-
Const = Annotated[T, my_annotations.CONST]
348+
type Const[T] = Annotated[T, my_annotations.CONST]
321349

322350
class C:
323-
def const_method(self: Const[List[int]]) -> int:
351+
def const_method(self, x: Const[list[int]]) -> int:
324352
...

0 commit comments

Comments
 (0)