gh-119180: Documentation for PEP 649 and 749 (#122235)

Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com>
Co-authored-by: Carol Willing <carolcode@willingconsulting.com>
This commit is contained in:
Jelle Zijlstra 2024-09-11 07:49:59 -07:00 committed by GitHub
parent 6e23c89fcd
commit 5436d8b9c3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 680 additions and 104 deletions

View file

@ -1329,13 +1329,7 @@ following the parameter name. Any parameter may have an annotation, even those
``*identifier`` or ``**identifier``. Functions may have "return" annotation of
the form "``-> expression``" after the parameter list. These annotations can be
any valid Python expression. The presence of annotations does not change the
semantics of a function. The annotation values are available as values of
a dictionary keyed by the parameters' names in the :attr:`__annotations__`
attribute of the function object. If the ``annotations`` import from
:mod:`__future__` is used, annotations are preserved as strings at runtime which
enables postponed evaluation. Otherwise, they are evaluated when the function
definition is executed. In this case annotations may be evaluated in
a different order than they appear in the source code.
semantics of a function. See :ref:`annotations` for more information on annotations.
.. index:: pair: lambda; expression
@ -1852,6 +1846,44 @@ Here, ``annotation-def`` (not a real keyword) indicates an
:ref:`annotation scope <annotation-scopes>`. The capitalized names
like ``TYPE_PARAMS_OF_ListOrSet`` are not actually bound at runtime.
.. _annotations:
Annotations
===========
.. versionchanged:: 3.14
Annotations are now lazily evaluated by default.
Variables and function parameters may carry :term:`annotations <annotation>`,
created by adding a colon after the name, followed by an expression::
x: annotation = 1
def f(param: annotation): ...
Functions may also carry a return annotation following an arrow::
def f() -> annotation: ...
Annotations are conventionally used for :term:`type hints <type hint>`, but this
is not enforced by the language, and in general annotations may contain arbitrary
expressions. The presence of annotations does not change the runtime semantics of
the code, except if some mechanism is used that introspects and uses the annotations
(such as :mod:`dataclasses` or :func:`functools.singledispatch`).
By default, annotations are lazily evaluated in a :ref:`annotation scope <annotation-scopes>`.
This means that they are not evaluated when the code containing the annotation is evaluated.
Instead, the interpreter saves information that can be used to evaluate the annotation later
if requested. The :mod:`annotationlib` module provides tools for evaluating annotations.
If the :ref:`future statement <future>` ``from __future__ import annotations`` is present,
all annotations are instead stored as strings::
>>> from __future__ import annotations
>>> def f(param: annotation): ...
>>> f.__annotations__
{'param': 'annotation'}
.. rubric:: Footnotes
.. [#] The exception is propagated to the invocation stack unless

View file

@ -581,6 +581,7 @@ Special writable attributes
single: __defaults__ (function attribute)
single: __code__ (function attribute)
single: __annotations__ (function attribute)
single: __annotate__ (function attribute)
single: __kwdefaults__ (function attribute)
single: __type_params__ (function attribute)
@ -628,7 +629,17 @@ Most of these attributes check the type of the assigned value:
:term:`parameters <parameter>`.
The keys of the dictionary are the parameter names,
and ``'return'`` for the return annotation, if provided.
See also: :ref:`annotations-howto`.
See also: :attr:`object.__annotations__`.
.. versionchanged:: 3.14
Annotations are now :ref:`lazily evaluated <lazy-evaluation>`.
See :pep:`649`.
* - .. attribute:: function.__annotate__
- The :term:`annotate function` for this function, or ``None``
if the function has no annotations. See :attr:`object.__annotate__`.
.. versionadded:: 3.14
* - .. attribute:: function.__kwdefaults__
- A :class:`dictionary <dict>` containing defaults for keyword-only
@ -881,6 +892,7 @@ Attribute assignment updates the module's namespace dictionary, e.g.,
single: __doc__ (module attribute)
single: __file__ (module attribute)
single: __annotations__ (module attribute)
single: __annotate__ (module attribute)
pair: module; namespace
Predefined (writable) attributes:
@ -901,11 +913,21 @@ Predefined (writable) attributes:
loaded dynamically from a shared library, it's the pathname of the shared
library file.
:attr:`__annotations__`
:attr:`~object.__annotations__`
A dictionary containing
:term:`variable annotations <variable annotation>` collected during
module body execution. For best practices on working
with :attr:`__annotations__`, please see :ref:`annotations-howto`.
with :attr:`!__annotations__`, see :mod:`annotationlib`.
.. versionchanged:: 3.14
Annotations are now :ref:`lazily evaluated <lazy-evaluation>`.
See :pep:`649`.
:attr:`~object.__annotate__`
The :term:`annotate function` for this module, or ``None``
if the module has no annotations. See :attr:`object.__annotate__`.
.. versionadded:: 3.14
.. index:: single: __dict__ (module attribute)
@ -969,6 +991,7 @@ A class object can be called (see above) to yield a class instance (see below).
single: __bases__ (class attribute)
single: __doc__ (class attribute)
single: __annotations__ (class attribute)
single: __annotate__ (class attribute)
single: __type_params__ (class attribute)
single: __static_attributes__ (class attribute)
single: __firstlineno__ (class attribute)
@ -991,12 +1014,36 @@ Special attributes:
:attr:`__doc__`
The class's documentation string, or ``None`` if undefined.
:attr:`__annotations__`
:attr:`~object.__annotations__`
A dictionary containing
:term:`variable annotations <variable annotation>`
collected during class body execution. For best practices on
working with :attr:`__annotations__`, please see
:ref:`annotations-howto`.
working with :attr:`~object.__annotations__`, please see
:mod:`annotationlib`.
.. warning::
Accessing the :attr:`~object.__annotations__` attribute of a class
object directly may yield incorrect results in the presence of
metaclasses. Use :func:`annotationlib.get_annotations` to
retrieve class annotations safely.
.. versionchanged:: 3.14
Annotations are now :ref:`lazily evaluated <lazy-evaluation>`.
See :pep:`649`.
:attr:`~object.__annotate__`
The :term:`annotate function` for this class, or ``None``
if the class has no annotations. See :attr:`object.__annotate__`.
.. warning::
Accessing the :attr:`~object.__annotate__` attribute of a class
object directly may yield incorrect results in the presence of
metaclasses. Use :func:`annotationlib.get_annotate_function` to
retrieve the annotate function safely.
.. versionadded:: 3.14
:attr:`__type_params__`
A tuple containing the :ref:`type parameters <type-params>` of
@ -3253,6 +3300,51 @@ implement the protocol in Python.
:class:`collections.abc.Buffer`
ABC for buffer types.
Annotations
-----------
Functions, classes, and modules may contain :term:`annotations <annotation>`,
which are a way to associate information (usually :term:`type hints <type hint>`)
with a symbol.
.. attribute:: object.__annotations__
This attribute contains the annotations for an object. It is
:ref:`lazily evaluated <lazy-evaluation>`, so accessing the attribute may
execute arbitrary code and raise exceptions. If evaluation is successful, the
attribute is set to a dictionary mapping from variable names to annotations.
.. versionchanged:: 3.14
Annotations are now lazily evaluated.
.. method:: object.__annotate__(format)
An :term:`annotate function`.
Returns a new dictionary object mapping attribute/parameter names to their annotation values.
Takes a format parameter specifying the format in which annotations values should be provided.
It must be a member of the :class:`annotationlib.Format` enum, or an integer with
a value corresponding to a member of the enum.
If an annotate function doesn't support the requested format, it must raise
:exc:`NotImplementedError`. Annotate functions must always support
:attr:`~annotationlib.Format.VALUE` format; they must not raise
:exc:`NotImplementedError()` when called with this format.
When called with :attr:`~annotationlib.Format.VALUE` format, an annotate function may raise
:exc:`NameError`; it must not raise :exc:`!NameError` when called requesting any other format.
If an object does not have any annotations, :attr:`~object.__annotate__` should preferably be set
to ``None`` (it cant be deleted), rather than set to a function that returns an empty dict.
.. versionadded:: 3.14
.. seealso::
:pep:`649` --- Deferred evaluation of annotation using descriptors
Introduces lazy evaluation of annotations and the ``__annotate__`` function.
.. _special-lookup:
Special method lookup

View file

@ -190,14 +190,15 @@ However, the following will succeed::
Annotation scopes
-----------------
:ref:`Type parameter lists <type-params>` and :keyword:`type` statements
:term:`Annotations <annotation>`, :ref:`type parameter lists <type-params>`
and :keyword:`type` statements
introduce *annotation scopes*, which behave mostly like function scopes,
but with some exceptions discussed below. :term:`Annotations <annotation>`
currently do not use annotation scopes, but they are expected to use
annotation scopes in Python 3.13 when :pep:`649` is implemented.
but with some exceptions discussed below.
Annotation scopes are used in the following contexts:
* :term:`Function annotations <function annotation>`.
* :term:`Variable annotations <variable annotation>`.
* Type parameter lists for :ref:`generic type aliases <generic-type-aliases>`.
* Type parameter lists for :ref:`generic functions <generic-functions>`.
A generic function's annotations are
@ -236,17 +237,23 @@ Annotation scopes differ from function scopes in the following ways:
Annotation scopes are also used for type parameter defaults, as
introduced by :pep:`696`.
.. versionchanged:: 3.14
Annotation scopes are now also used for annotations, as specified in
:pep:`649` and :pep:`749`.
.. _lazy-evaluation:
Lazy evaluation
---------------
The values of type aliases created through the :keyword:`type` statement are
*lazily evaluated*. The same applies to the bounds, constraints, and default values of type
Most annotation scopes are *lazily evaluated*. This includes annotations,
the values of type aliases created through the :keyword:`type` statement, and
the bounds, constraints, and default values of type
variables created through the :ref:`type parameter syntax <type-params>`.
This means that they are not evaluated when the type alias or type variable is
created. Instead, they are only evaluated when doing so is necessary to resolve
an attribute access.
created, or when the object carrying annotations is created. Instead, they
are only evaluated when necessary, for example when the ``__value__``
attribute on a type alias is accessed.
Example:

View file

@ -336,23 +336,21 @@ The difference from normal :ref:`assignment` is that only a single target is all
The assignment target is considered "simple" if it consists of a single
name that is not enclosed in parentheses.
For simple assignment targets, if in class or module scope,
the annotations are evaluated and stored in a special class or module
attribute :attr:`__annotations__`
that is a dictionary mapping from variable names (mangled if private) to
evaluated annotations. This attribute is writable and is automatically
created at the start of class or module body execution, if annotations
are found statically.
the annotations are gathered in a lazily evaluated
:ref:`annotation scope <annotation-scopes>`. The annotations can be
evaluated using the :attr:`~object.__annotations__` attribute of a
class or module, or using the facilities in the :mod:`annotationlib`
module.
If the assignment target is not simple (an attribute, subscript node, or
parenthesized name), the annotation is evaluated if
in class or module scope, but not stored.
parenthesized name), the annotation is never evaluated.
If a name is annotated in a function scope, then this name is local for
that scope. Annotations are never evaluated and stored in function scopes.
If the right hand side is present, an annotated
assignment performs the actual assignment before evaluating annotations
(where applicable). If the right hand side is not present for an expression
assignment performs the actual assignment as if there was no annotation
present. If the right hand side is not present for an expression
target, then the interpreter evaluates the target except for the last
:meth:`~object.__setitem__` or :meth:`~object.__setattr__` call.
@ -373,6 +371,10 @@ target, then the interpreter evaluates the target except for the last
regular assignments. Previously, some expressions (like un-parenthesized
tuple expressions) caused a syntax error.
.. versionchanged:: 3.14
Annotations are now lazily evaluated in a separate :ref:`annotation scope <annotation-scopes>`.
If the assignment target is not simple, annotations are never evaluated.
.. _assert:
@ -975,8 +977,8 @@ block textually preceding that :keyword:`!global` statement.
Names listed in a :keyword:`global` statement must not be defined as formal
parameters, or as targets in :keyword:`with` statements or :keyword:`except` clauses, or in a :keyword:`for` target list, :keyword:`class`
definition, function definition, :keyword:`import` statement, or variable
annotation.
definition, function definition, :keyword:`import` statement, or
:term:`variable annotations <variable annotation>`.
.. impl-detail::