Skip to content

Commit

Permalink
pythongh-119180: Documentation for PEP 649/749
Browse files Browse the repository at this point in the history
  • Loading branch information
JelleZijlstra committed Jul 24, 2024
1 parent e91ef13 commit 20dcb62
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 45 deletions.
14 changes: 9 additions & 5 deletions Doc/glossary.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,23 @@ Glossary
and loaders (in the :mod:`importlib.abc` module). You can create your own
ABCs with the :mod:`abc` module.

annotate function
A function that can be called to retrieve the :term:`annotations <annotation>`
of an object. This function is accessible as the :attr:`~object.__annotate__`
attribute of functions, classes, and modules.

annotation
A label associated with a variable, a class
attribute or a function parameter or return value,
used by convention as a :term:`type hint`.

Annotations of local variables cannot be accessed at runtime, but
annotations of global variables, class attributes, and functions
are stored in the :attr:`__annotations__`
special attribute of modules, classes, and functions,
respectively.
can be retrieved by calling :func:`annotationlib.get_annotations`
on modules, classes, and functions, respectively.

See :term:`variable annotation`, :term:`function annotation`, :pep:`484`
and :pep:`526`, which describe this functionality.
See :term:`variable annotation`, :term:`function annotation`, :pep:`484`,
:pep:`526` and :pep:`649`, which describe this functionality.
Also see :ref:`annotations-howto`
for best practices on working with annotations.

Expand Down
14 changes: 7 additions & 7 deletions Doc/library/__future__.rst
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,10 @@ language using this mechanism:
| generator_stop | 3.5.0b1 | 3.7 | :pep:`479`: |
| | | | *StopIteration handling inside generators* |
+------------------+-------------+--------------+---------------------------------------------+
| annotations | 3.7.0b1 | TBD [1]_ | :pep:`563`: |
| | | | *Postponed evaluation of annotations* |
| annotations | 3.7.0b1 | Never [1]_ | :pep:`563`: |
| | | | *Postponed evaluation of annotations*, |
| | | | :pep:`649`: *Deferred evalutation of |
| | | | annotations using descriptors* |
+------------------+-------------+--------------+---------------------------------------------+

.. XXX Adding a new entry? Remember to update simple_stmts.rst, too.
Expand Down Expand Up @@ -115,11 +117,9 @@ language using this mechanism:

.. [1]
``from __future__ import annotations`` was previously scheduled to
become mandatory in Python 3.10, but the Python Steering Council
twice decided to delay the change
(`announcement for Python 3.10 <https://mail.python.org/archives/list/[email protected]/message/CLVXXPQ2T2LQ5MP2Y53VVQFCXYWQJHKZ/>`__;
`announcement for Python 3.11 <https://mail.python.org/archives/list/[email protected]/message/VIZEBX5EYMSYIJNDBF6DMUMZOCWHARSO/>`__).
No final decision has been made yet. See also :pep:`563` and :pep:`649`.
become mandatory in Python 3.10, but the change was delayed and ultimately
canceled. This feature will eventually be deprecated and removed. See
:pep:`649` and :pep:`749`.
.. seealso::
Expand Down
1 change: 1 addition & 0 deletions Doc/library/python.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ overview:
__future__.rst
gc.rst
inspect.rst
annotationlib.rst
site.rst
44 changes: 37 additions & 7 deletions Doc/reference/compound_stmts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -1852,6 +1846,42 @@ 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.

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
Expand Down
104 changes: 98 additions & 6 deletions Doc/reference/datamodel.rst
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,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)

Expand Down Expand Up @@ -624,7 +625,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
Expand Down Expand Up @@ -884,6 +895,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:
Expand All @@ -904,11 +916,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)

Expand Down Expand Up @@ -972,6 +994,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)
Expand All @@ -994,12 +1017,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
Expand Down Expand Up @@ -3256,6 +3303,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 can’t 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
Expand Down
23 changes: 15 additions & 8 deletions Doc/reference/executionmodel.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:

Expand Down
26 changes: 14 additions & 12 deletions Doc/reference/simple_stmts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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:

Expand Down Expand Up @@ -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::

Expand Down

0 comments on commit 20dcb62

Please sign in to comment.