PEP 749: Rename SOURCE to STRING, go back on __annotations__ setting (#4000)

This commit is contained in:
Jelle Zijlstra 2024-09-26 11:55:45 -07:00 committed by GitHub
parent aced24fc35
commit 55cf374db5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 41 additions and 17 deletions

View File

@ -30,9 +30,11 @@ specification:
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.
* Deleting the ``__annotations__`` attribute directly will also clear ``__annotate__``.
* 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.
* The ``SOURCE`` format is renamed to ``STRING`` to improve clarity and reduce the risk of
user confusion.
Motivation
==========
@ -229,13 +231,13 @@ The module will contain the following functionality:
dictionary. This is intended to be used for evaluating deferred attributes introduced by
:pep:`695` and :pep:`696`; see below for details. *func* may be ``None``
for convenience; if ``None`` is passed, the function also returns ``None``.
* ``annotations_to_source(annotations: dict[str, object]) -> dict[str, str]``: a function that
* ``annotations_to_string(annotations: dict[str, object]) -> dict[str, str]``: a function that
converts each value in an annotations dictionary to a string representation.
This is useful for
implementing the ``SOURCE`` format in cases where the original source is not available,
such as in the functional syntax for :py:class:`typing.TypedDict`.
* ``value_to_source(value: object) -> str``: a function that converts a single value to a
string representation. This is used by ``annotations_to_source``.
* ``value_to_string(value: object) -> str``: a function that converts a single value to a
string representation. This is used by ``annotations_to_string``.
It uses ``repr()`` for most values, but for types it returns the fully qualified name.
It is also useful as a helper for the ``repr()`` of a number of objects in the
:py:mod:`typing` and :py:mod:`collections.abc` modules.
@ -629,29 +631,23 @@ Third-party code that implements ``__annotate__`` functions should raise
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 deleting ``__annotations__``
======================================
:pep:`649` specifies:
Setting ``o.__annotations__`` to a legal value
automatically sets ``o.__annotate__`` to ``None``.
We would prefer to keep ``__annotate__`` unchanged when ``__annotations__``
is written to. Conceptually, ``__annotate__`` provides the ground truth
and ``__annotations__`` is merely a cache, and we shouldn't throw away the
ground truth if the cache is modified.
The motivation for :pep:`649`'s behavior is to keep the two attributes in sync.
However, this is impossible in general; if the ``__annotations__`` dictionary
is modified in place, this will not be reflected in the ``__annotate__`` attribute.
The overall mental model for this area will be simpler if setting ``__annotations__``
has no effect on ``__annotate__``.
However, the PEP does not say what happens if the ``__annotations__`` attribute
is deleted (using ``del``). It seems most consistent that deleting the attribute
will also delete ``__annotate__``.
Specification
-------------
The value of ``__annotate__`` is not changed when ``__annotations__`` is set.
Deleting the ``__annotations__`` attribute on functions, modules, and classes
results in setting ``__annotate__`` to None.
Deferred evaluation of PEP 695 and 696 objects
==============================================
@ -730,6 +726,34 @@ can use the ``ForwardRef.evaluate`` method.
If use cases come up in the future, we could add additional functionality,
such as a new method that re-evaluates the annotation from scratch.
Renaming ``SOURCE`` to ``STRING``
=================================
The ``SOURCE`` format is meant for tools that need to show a human-readable
format that is close to the original source code. However, we cannot retrieve
the original source in ``__annotate__`` functions, and in some cases, we have
``__annotate__`` functions in Python code that do not have access to the original
code. For example, this applies to :py:func:`dataclasses.make_dataclass`
and the call-based syntax for :py:class:`typing.TypedDict`.
This makes the name ``SOURCE`` a bit of a misnomer. The goal of the format
should indeed be to recreate the source, but the name is likely to mislead
users in practice. A more neutral name would emphasize that the format returns
an annotation dictionary with only strings. We suggest ``STRING``.
Specification
-------------
The ``SOURCE`` format is renamed to ``STRING``. To reiterate the changes in this
PEP, the four supported formats are now:
- ``VALUE``: the default format, which evaluates the annotations and returns the
resulting values.
- ``VALUE_WITH_FAKE_GLOBALS``: for internal use; should be handled like ``VALUE``
by annotate functions that support execution with fake globals.
- ``FORWARDREF``: replaces undefined names with ``ForwardRef`` objects.
- ``STRING``: returns strings, attempts to recreate code close to the original source.
Miscellaneous implementation details
====================================