Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 3 commits into from
Sep 26, 2024
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 42 additions & 7 deletions peps/pep-0749.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
==========
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -629,14 +631,18 @@ 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``.

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__``.

We would prefer to keep ``__annotate__`` unchanged when ``__annotations__``
JelleZijlstra marked this conversation as resolved.
Show resolved Hide resolved
is written to. Conceptually, ``__annotate__`` provides the ground truth
and ``__annotations__`` is merely a cache, and we shouldn't throw away the
Expand All @@ -651,7 +657,8 @@ has no effect on ``__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
==============================================
Expand Down Expand Up @@ -730,6 +737,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
====================================

Expand Down