PEP 749: Updates from discussion (#3823)

* Reject "make it a no-op forever"

* annotationlib
This commit is contained in:
Jelle Zijlstra 2024-06-06 05:03:41 -07:00 committed by GitHub
parent 8a3d3f0f4a
commit 736c72935b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 27 additions and 15 deletions

View File

@ -20,7 +20,7 @@ specification:
* ``from __future__ import annotations`` (:pep:`563`) will continue to exist with
its current behavior at least until Python 3.13 reaches its end-of-life. Subsequently,
it will be deprecated and eventually removed.
* A new standard library module, ``annotations``, is added to provide tooling for
* A new standard library module, ``annotationlib``, is added to provide tooling for
annotations. It will include the ``get_annotations()`` function, an enum for annotation
formats, a ``ForwardRef`` class, and a helper function for calling ``__annotate__`` functions.
* Annotations in the REPL are lazily evaluated, just like other module-level annotations.
@ -118,8 +118,16 @@ indefinitely. However, this would permanently bifurcate the behavior of the Pyth
language. This is undesirable; the language should have only a single set of semantics,
not two permanently different modes.
New ``annotations`` module
==========================
*Make the future import a no-op in the future*: Instead of eventually making
``from __future__ import annotations`` a ``SyntaxError``, we could make it do nothing
instead at some point after Python 3.13 reaches its end-of-life. This still has some
of the same issues outlined above around making it a no-op now, although the ecosystem
would have had much longer to adapt. It is better to have users explicitly remove
the future import from their code in the future once they have confirmed they do not
rely on stringized annotations.
New ``annotationlib`` module
============================
:pep:`649` proposes to add tooling related to annotations to the :py:mod:`inspect`
module. However, that module is rather large, has direct or indirect dependencies
@ -148,14 +156,14 @@ and `pyanalyze <https://github.com/quora/pyanalyze/blob/9e401724f9d035cf138b7261
use the ``__forward_arg__`` attribute.
We replace the existing but poorly specified :py:class:`!typing.ForwardRef` with a new class,
``annotations.ForwardRef``. It is designed to be mostly compatible with existing uses
``annotationlib.ForwardRef``. It is designed to be mostly compatible with existing uses
of the :py:class:`!typing.ForwardRef` class, but without the behaviors specific to the
:py:mod:`!typing` module. For compatibility with existing users, we keep the private
``_evaluate`` method, but mark it as deprecated. It delegates to a new public function in
the :py:mod:`!typing` module, ``typing.evaluate_forward_ref``, that is designed to
evaluate forward references in a way that is specific to type hints.
We add a function ``annotations.call_annotate_function`` as a helper for calling
We add a function ``annotationlib.call_annotate_function`` as a helper for calling
``__annotate__`` functions. This is a useful building block when implementing functionality
that needs to partially evaluate annotations while a class is being constructed.
For example, the implementation of :py:class:`typing.NamedTuple` needs to retrieve
@ -165,7 +173,7 @@ can be constructed, because the annotations determine what fields exist on the n
Specification
-------------
A new module, ``annotations``, is added to the standard library. Its aim is to
A new module, ``annotationlib``, is added to the standard library. Its aim is to
provide tooling for introspecting and wrapping annotations.
The exact contents of the module are not yet specified. We will add support for
@ -217,7 +225,7 @@ types and evaluates additional forward references within these types.
Contrary to :pep:`649`, the annotation formats (``VALUE``, ``FORWARDREF``, and ``SOURCE``)
will not be added as global members of the :py:mod:`inspect` module. The only recommended
way to refer to these constants will be as ``annotations.Format.VALUE``.
way to refer to these constants will be as ``annotationlib.Format.VALUE``.
Open issues
-----------
@ -225,7 +233,9 @@ Open issues
What should this module be called? Some ideas:
- ``annotations``: The most obvious name, but it may cause confusion with the existing
``from __future__ import annotations``. There is a PyPI package :pypi:`annotations`,
``from __future__ import annotations``, because users may have both ``import annotations``
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`,
but it had only a single release in 2015 and looks abandoned.
- ``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
@ -236,6 +246,8 @@ What should this module be called? Some ideas:
no other public standard library module has an underscore in its name.
- ``annotationslib``: Analogous to :py:mod:`tomllib`, :py:mod:`pathlib`, and :py:mod:`importlib`.
There is no PyPI package with this name.
- ``annotationlib``: Similar to the above, but one character shorter and subjectively reads
better. Also not taken on PyPI.
Rejected alternatives
---------------------
@ -342,7 +354,7 @@ object.
Classes and functions defined within the REPL will also work like any other classes,
so evaluation of their annotations will be deferred. It is possible to access the
``__annotations__`` and ``__annotate__`` attributes or use the ``annotations`` module
``__annotations__`` and ``__annotate__`` attributes or use the ``annotationlib`` module
to introspect the annotations.
Wrappers that provide ``__annotations__``
@ -402,7 +414,7 @@ that custom ``__annotate__`` functions may not work well with the
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
``annotations.call_annotate_function`` plus some transformation of the
``annotationlib.call_annotate_function`` plus some transformation of the
result.
In addition, the proposed mechanism couples the implementation with
@ -484,9 +496,9 @@ these callables return a single value, not a dictionary of annotations.
These attributes are read-only.
Usually, users would use these attributes in combinations with
``annotations.call_evaluate_function``. For example, to get a ``TypeVar``'s bound
``annotationlib.call_evaluate_function``. For example, to get a ``TypeVar``'s bound
in SOURCE format, one could write
``annotations.call_evaluate_function(T.evaluate_bound, annotations.Format.SOURCE)``.
``annotationlib.call_evaluate_function(T.evaluate_bound, annotations.Format.SOURCE)``.
Miscellaneous implementation details
====================================
@ -564,7 +576,7 @@ that perform introspection, and it is important that we make it as easy as possi
libraries to support the new semantics in a straightforward, user-friendly way.
We will update those parts of the standard library that are affected by this problem,
and we propose to add commonly useful functionality to the new ``annotations`` module,
and we propose to add commonly useful functionality to the new ``annotationlib`` module,
so third-party tools can use the same set of tools.
@ -583,7 +595,7 @@ quotes around annotations that require forward references, a major source of con
for users.
For advanced users who need to introspect annotations, the story becomes more complex.
The documentation of the new ``annotations`` module will serve as a reference for users
The documentation of the new ``annotationlib`` module will serve as a reference for users
who need to interact programmatically with annotations.
@ -646,7 +658,7 @@ Accessing ``.type`` might throw an error:
^^^^^^^^^
NameError: name 'undefined' is not defined
But users could use ``annotations.call_evaluate_function`` to get the type in other formats:
But users could use ``annotationlib.call_evaluate_function`` to get the type in other formats:
.. code:: pycon