PEP 749: Add a FAKE_GLOBALS_VALUE format (#3991)
Co-authored-by: Carl Meyer <carl@oddbird.net>
This commit is contained in:
parent
6fe0175e3d
commit
9c9c5c3aee
|
@ -27,7 +27,9 @@ specification:
|
||||||
* We specify the behavior of wrapper objects that provide annotations, such as :py:func:`classmethod`
|
* We specify the behavior of wrapper objects that provide annotations, such as :py:func:`classmethod`
|
||||||
and code that uses :py:func:`functools.wraps`.
|
and code that uses :py:func:`functools.wraps`.
|
||||||
* There will not be a code flag for marking ``__annotate__`` functions
|
* There will not be a code flag for marking ``__annotate__`` functions
|
||||||
that can be run in a "fake globals" environment.
|
that can be run in a "fake globals" environment. Instead, we add a fourth format,
|
||||||
|
``VALUE_WITH_FAKE_GLOBALS``, to allow third-party implementors of annotate functions to
|
||||||
|
indicate what formats they support.
|
||||||
* Setting the ``__annotations__`` attribute directly will not affect the ``__annotate__`` attribute.
|
* Setting the ``__annotations__`` attribute directly will not affect the ``__annotate__`` attribute.
|
||||||
* We add functionality to allow evaluating type alias values and type parameter bounds and defaults
|
* We add functionality to allow evaluating type alias values and type parameter bounds and defaults
|
||||||
(which were added by :pep:`695` and :pep:`696`) using PEP 649-like semantics.
|
(which were added by :pep:`695` and :pep:`696`) using PEP 649-like semantics.
|
||||||
|
@ -74,6 +76,11 @@ We suggest the following deprecation plan:
|
||||||
|
|
||||||
- In Python 3.14, ``from __future__ import annotations`` will continue to work as it
|
- In Python 3.14, ``from __future__ import annotations`` will continue to work as it
|
||||||
did before, converting annotations into strings.
|
did before, converting annotations into strings.
|
||||||
|
|
||||||
|
- If the future import is active, the ``__annotate__`` function of objects with
|
||||||
|
annotations will return the annotations as strings when called with the ``VALUE``
|
||||||
|
format, reflecting the behavior of ``__annotations__``.
|
||||||
|
|
||||||
- Sometime after the last release that did not support :pep:`649` semantics (expected to be 3.13)
|
- Sometime after the last release that did not support :pep:`649` semantics (expected to be 3.13)
|
||||||
reaches its end-of-life, ``from __future__ import annotations`` is deprecated. Compiling
|
reaches its end-of-life, ``from __future__ import annotations`` is deprecated. Compiling
|
||||||
any code that uses the future import will emit a :py:exc:`DeprecationWarning`. This will
|
any code that uses the future import will emit a :py:exc:`DeprecationWarning`. This will
|
||||||
|
@ -194,7 +201,8 @@ The module will contain the following functionality:
|
||||||
* ``Format``: an enum that contains the possible formats of annotations. This will
|
* ``Format``: an enum that contains the possible formats of annotations. This will
|
||||||
replace the ``VALUE``, ``FORWARDREF``, and ``SOURCE`` formats in :pep:`649`.
|
replace the ``VALUE``, ``FORWARDREF``, and ``SOURCE`` formats in :pep:`649`.
|
||||||
PEP 649 proposed to make these values global members of the :py:mod:`inspect`
|
PEP 649 proposed to make these values global members of the :py:mod:`inspect`
|
||||||
module; we prefer to place them within an enum.
|
module; we prefer to place them within an enum. We propose to add a fourth format,
|
||||||
|
``VALUE_WITH_FAKE_GLOBALS`` (see below).
|
||||||
* ``ForwardRef``: a class representing a forward reference; it may be returned by
|
* ``ForwardRef``: a class representing a forward reference; it may be returned by
|
||||||
``get_annotations()`` when the format is ``FORWARDREF``. The existing class
|
``get_annotations()`` when the format is ``FORWARDREF``. The existing class
|
||||||
:py:class:`typing.ForwardRef` will become an alias of this class. Its members include:
|
:py:class:`typing.ForwardRef` will become an alias of this class. Its members include:
|
||||||
|
@ -251,6 +259,9 @@ What should this module be called? Some ideas:
|
||||||
and ``from __future__ import annotations`` in the same module. The use of a common word
|
and ``from __future__ import annotations`` in the same module. The use of a common word
|
||||||
as the name will make the module harder to search for. There is a PyPI package :pypi:`annotations`,
|
as the name will make the module harder to search for. There is a PyPI package :pypi:`annotations`,
|
||||||
but it had only a single release in 2015 and looks abandoned.
|
but it had only a single release in 2015 and looks abandoned.
|
||||||
|
- ``annotation`` (in the singular): Similar, but does not cause confusion with the future
|
||||||
|
import. There is an abandoned PyPI package :pypi:`annotation`, but it apparently never
|
||||||
|
released any artifacts.
|
||||||
- ``annotools``: Analogous to :py:mod:`itertools` and :py:mod:`functools`, but "anno" is a less
|
- ``annotools``: Analogous to :py:mod:`itertools` and :py:mod:`functools`, but "anno" is a less
|
||||||
obvious abbreviation than "iter" or "func". As of this writing, there
|
obvious abbreviation than "iter" or "func". As of this writing, there
|
||||||
is no PyPI package with this name.
|
is no PyPI package with this name.
|
||||||
|
@ -561,8 +572,8 @@ This approach would also mean that accessing ``.__annotations__`` on an instance
|
||||||
of an annotated class no longer works. While this behavior is not documented,
|
of an annotated class no longer works. While this behavior is not documented,
|
||||||
it is a long-standing feature of Python and is relied upon by some users.
|
it is a long-standing feature of Python and is relied upon by some users.
|
||||||
|
|
||||||
Remove code flag for marking ``__annotate__`` functions
|
Adding the ``VALUE_WITH_FAKE_GLOBALS`` format
|
||||||
=======================================================
|
=============================================
|
||||||
|
|
||||||
:pep:`649` specifies:
|
:pep:`649` specifies:
|
||||||
|
|
||||||
|
@ -577,33 +588,46 @@ Remove code flag for marking ``__annotate__`` functions
|
||||||
it's expected that only ``__annotate__`` methods generated
|
it's expected that only ``__annotate__`` methods generated
|
||||||
by the Python compiler will set it.
|
by the Python compiler will set it.
|
||||||
|
|
||||||
We have not found a need for this mechanism during our work to
|
However, this mechanism couples the implementation with
|
||||||
add :pep:`649` support to the standard library. While it is true
|
|
||||||
that custom ``__annotate__`` functions may not work well with the
|
|
||||||
"fake globals" environment, this technique is used only when the
|
|
||||||
``__annotate__`` function raises :py:exc:`NotImplementedError` to
|
|
||||||
signal that it does not support the requested format. However,
|
|
||||||
manually implemented ``__annotate__`` functions are likely to support
|
|
||||||
all three annotation formats; often, they will consist of a call to
|
|
||||||
``annotationlib.call_annotate_function`` plus some transformation of the
|
|
||||||
result.
|
|
||||||
|
|
||||||
In addition, the proposed mechanism couples the implementation with
|
|
||||||
low-level details of the code object. The code object flags are
|
low-level details of the code object. The code object flags are
|
||||||
CPython-specific and the documentation :py:ref:`explicitly warns <inspect-module-co-flags>`
|
CPython-specific and the documentation :py:ref:`explicitly warns <inspect-module-co-flags>`
|
||||||
against relying on the values.
|
against relying on the values.
|
||||||
|
|
||||||
|
Larry Hastings suggested an alternative approach that does not
|
||||||
|
rely on code flags: a fourth format, ``VALUE_WITH_FAKE_GLOBALS``.
|
||||||
|
Compiler-generated annotate functions would support only the
|
||||||
|
``VALUE`` and ``VALUE_WITH_FAKE_GLOBALS`` formats, both of which are
|
||||||
|
implemented identically. The standard library would use the
|
||||||
|
``VALUE_WITH_FAKE_GLOBALS`` format when invoking an annotate function
|
||||||
|
in one of the special "fake globals" environments.
|
||||||
|
|
||||||
|
This approach is useful as a forward-compatible mechanism for
|
||||||
|
adding new annotation formats in the future. Users who manually
|
||||||
|
write annotate functions should raise ``NotImplementedError`` if
|
||||||
|
the ``VALUE_WITH_FAKE_GLOBALS`` format is requested, so the standard
|
||||||
|
library will not call the manually written annotate function with
|
||||||
|
"fake globals", which could have unpredictable results.
|
||||||
|
|
||||||
Specification
|
Specification
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
The standard library will use the "fake globals" technique on any
|
An additional format, ``FAKE_GLOBALS_VALUE``, is added to the ``Format`` enum in the
|
||||||
``__annotate__`` function that raises :py:exc:`NotImplementedError`
|
``annotationlib`` module, with value equal to 2. (As a result, the values of the
|
||||||
when the requested format is not supported.
|
other formats will shift relative to PEP 649: ``FORWARDREF`` will be 3 and ``SOURCE``
|
||||||
|
will be 4.)
|
||||||
|
|
||||||
Third-party code that implements ``__annotate__`` functions should either
|
Compiler-generated
|
||||||
support all three annotation formats, or be prepared to handle the
|
annotate functions will support this format and return the same value as
|
||||||
"fake globals" environment. This should be mentioned in the data model
|
they would return for the ``VALUE`` format. The standard library will pass
|
||||||
documentation for ``__annotate__``.
|
this format to the ``__annotate__`` function when it is called in a "fake globals"
|
||||||
|
environment, as used to implement the ``FORWARDREF`` and ``SOURCE`` formats.
|
||||||
|
All public functions in the ``annotationlib`` module that accept a format
|
||||||
|
argument will raise :py:exc:`NotImplementedError` if the format is ``FAKE_GLOBALS_VALUE``.
|
||||||
|
|
||||||
|
Third-party code that implements ``__annotate__`` functions should raise
|
||||||
|
:py:exc:`NotImplementedError` if the ``FAKE_GLOBALS_VALUE`` format is passed
|
||||||
|
and the function is not prepared to be run in a "fake globals" environment.
|
||||||
|
This should be mentioned in the data model documentation for ``__annotate__``.
|
||||||
|
|
||||||
Effect of setting ``__annotations__``
|
Effect of setting ``__annotations__``
|
||||||
=====================================
|
=====================================
|
||||||
|
@ -754,7 +778,7 @@ Signature of ``__annotate__`` functions
|
||||||
``__annotate__(format: int) -> dict``
|
``__annotate__(format: int) -> dict``
|
||||||
|
|
||||||
However, using ``format`` as a parameter name could lead to collisions
|
However, using ``format`` as a parameter name could lead to collisions
|
||||||
if an annotation uses a class named ``format``. The parameter should be
|
if an annotation uses a symbol named ``format``. The parameter should be
|
||||||
positional-only and have a name that cannot be a legal identifier in order
|
positional-only and have a name that cannot be a legal identifier in order
|
||||||
to avoid this problem.
|
to avoid this problem.
|
||||||
|
|
||||||
|
@ -846,7 +870,8 @@ initial decisions, but the overall design is still his.
|
||||||
|
|
||||||
I thank Carl Meyer and Alex Waygood for feedback on early drafts of this PEP. Alex Waygood,
|
I thank Carl Meyer and Alex Waygood for feedback on early drafts of this PEP. Alex Waygood,
|
||||||
Alyssa Coghlan, and David Ellis provided insightful feedback and suggestions on the
|
Alyssa Coghlan, and David Ellis provided insightful feedback and suggestions on the
|
||||||
interaction between metaclasses and ``__annotations__``.
|
interaction between metaclasses and ``__annotations__``. Larry Hastings also provided useful
|
||||||
|
feedback on this PEP.
|
||||||
|
|
||||||
Appendix
|
Appendix
|
||||||
========
|
========
|
||||||
|
|
Loading…
Reference in New Issue