Skip to content

Commit a9aacbe

Browse files
committed
More changes
1 parent 44afe0d commit a9aacbe

File tree

1 file changed

+57
-41
lines changed

1 file changed

+57
-41
lines changed

peps/pep-0749.rst

Lines changed: 57 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -198,10 +198,11 @@ The module will contain the following functionality:
198198
module, or class. This will replace :py:func:`inspect.get_annotations`. The latter
199199
will delegate to the new function. It may eventually be deprecated, but to
200200
minimize disruption, we do not propose an immediate deprecation.
201-
* ``get_annotate_function()``: A function that returns the ``__annotate__`` function
202-
of an object, if it has one, or ``None`` if it does not. This is usually equivalent
203-
to accessing the ``.__annotate__`` attribute, except in the presence of metaclasses
204-
(see :ref:`below <pep749-metaclasses>`).
201+
* ``get_annotate_from_class_namespace(namespace: Mapping[str, Any])``: A function that
202+
returns the ``__annotate__`` function from a class namespace dictionary, or ``None``
203+
if there is none. This is useful in metaclasses during class construction. It is
204+
a separate function to avoid exposing implementation details about the internal storage
205+
for the ``__annotate__`` function (see :ref:`below <pep749-metaclasses>`).
205206
* ``Format``: an enum that contains the possible formats of annotations. This will
206207
replace the ``VALUE``, ``FORWARDREF``, and ``SOURCE`` formats in :pep:`649`.
207208
PEP 649 proposed to make these values global members of the :py:mod:`inspect`
@@ -238,7 +239,7 @@ The module will contain the following functionality:
238239
This is useful for
239240
implementing the ``SOURCE`` format in cases where the original source is not available,
240241
such as in the functional syntax for :py:class:`typing.TypedDict`.
241-
* ``value_to_string(value: object) -> str``: a function that converts a single value to a
242+
* ``type_repr(value: object) -> str``: a function that converts a single value to a
242243
string representation. This is used by ``annotations_to_string``.
243244
It uses ``repr()`` for most values, but for types it returns the fully qualified name.
244245
It is also useful as a helper for the ``repr()`` of a number of objects in the
@@ -501,45 +502,64 @@ attribute lookup is used, this approach breaks down in the presence of
501502
metaclasses, because entries in the metaclass's own class dictionary can render
502503
the descriptors invisible.
503504

504-
While we considered several approaches that would allow ``cls.__annotations__``
505-
and ``cls.__annotate__`` to work reliably when ``cls`` is a type with a custom
506-
metaclass, any such approach would expose significant complexity to advanced users.
507-
Instead, we recommend a simpler approach that confines the complexity to the
508-
``annotationlib`` module: in ``annotationlib.get_annotations``, we bypass normal
509-
attribute lookup by using the ``type.__annotations__`` descriptor directly.
505+
We considered several solutions but landed on one where we store the ``__annotate__``
506+
and ``__annotations__`` objects in the class dictionary, but under a different,
507+
internal-only name. This means that the class dictionary entries will not interfere
508+
with the descriptors defined on :py:class:`type`.
509+
510+
This approach means that the ``.__annotate__`` and ``.__annotations__`` objects in class
511+
objects will behave mostly intuitively, but there are a few downsides.
512+
513+
One concerns the interaction with classes defined under ``from __future__ import annotations``.
514+
Those will continue to have the ``__annotations__`` entry in the class dictionary, meaning
515+
that they will continue to display some buggy behavior. For example, if a metaclass is defined
516+
with the ``__future__`` import enabled and has annotations, and a class using that metaclass is
517+
defined without the ``__future__`` import, accessing ``.__annotations__`` on that class will yield
518+
the wrong results. However, this bug already exists in previous versions of Python. It could be
519+
fixed by setting the annotations at a different key in the class dict in this case too, but that
520+
would break users who directly access the class dictionary (e.g., during class construction).
521+
We prefer to keep the behavior under the ``__future__`` import unchanged as much as possible.
522+
523+
Second, in previous versions of Python it was possible to access the ``__annotations__`` attribute
524+
on instances of user-defined classes with annotations. However, this behavior was undocumented
525+
and not supported by :func:`inspect.get_annotations`, and it cannot be preserved under the
526+
:pep:`649` framework without bigger changes, such as a new ``object.__annotations__`` descriptor.
527+
This behavior change should be called out in porting guides.
510528

511529
Specification
512530
-------------
513531

514-
Users should always use ``annotationlib.get_annotations`` to access the
515-
annotations of a class object, and ``annotationlib.get_annotate_function``
516-
to access the ``__annotate__`` function. These functions will return only
517-
the class's own annotations, even when metaclasses are involved.
532+
The ``.__annotate__`` and ``.__annotations__`` attributes on class objects
533+
should reliably return the annotate function and the annotations dictionary,
534+
respectively, even in the presence of custom metaclasses.
518535

519-
The behavior of accessing the ``__annotations__`` and ``__annotate__``
520-
attributes on classes with a metaclass other than ``builtins.type`` is
521-
unspecified. The documentation should warn against direct use of these
522-
attributes and recommend using the ``annotationlib`` module instead.
523-
524-
Similarly, the presence of ``__annotations__`` and ``__annotate__`` keys
525-
in the class dictionary is an implementation detail and should not be relied
526-
upon.
536+
Users should not access the class dictionary directly for accessing annotations
537+
or the annotate function; the data stored in the class dictionary is an implementation
538+
detail and its format may change in the future. If only the class namespace
539+
dictionary is available (e.g., while the class is being constructed),
540+
``annotationlib.get_annotate_function`` may be used to retrieve the annotate function
541+
from the class dictionary.
527542

528543
Rejected alternatives
529544
---------------------
530545

531-
We considered two broad approaches for dealing with the behavior
546+
We considered three broad approaches for dealing with the behavior
532547
of the ``__annotations__`` and ``__annotate__`` entries in classes:
533548

534549
* Ensure that the entry is *always* present in the class dictionary, even if it
535550
is empty or has not yet been evaluated. This means we do not have to rely on
536551
the descriptors defined on :py:class:`type` to fill in the field, and
537552
therefore the metaclass's attributes will not interfere. (Prototype
538553
in `gh-120719 <https://github.com/python/cpython/pull/120719>`__.)
554+
* Warn users against using the ``__annotations__`` and ``__annotate__`` attributes
555+
directly. Instead, users should call function in ``annotationlib`` that
556+
invoke the :class:`type` descriptors directly. (Implemented in
557+
`gh-122074 <https://github.com/python/cpython/pull/122074>`__.)
539558
* Ensure that the entry is *never* present in the class dictionary, or at least
540559
never added by logic in the language core. This means that the descriptors
541560
on :py:class:`type` will always be used, without interference from the metaclass.
542-
(Prototype in `gh-120816 <https://github.com/python/cpython/pull/120816>`__.)
561+
(Initial prototype in `gh-120816 <https://github.com/python/cpython/pull/120816>`__;
562+
later implemented in `gh-132345 <https://github.com/python/cpython/pull/132345>`__.)
543563

544564
Alex Waygood suggested an implementation using the first approach. When a
545565
heap type (such as a class created through the ``class`` statement) is created,
@@ -561,19 +581,8 @@ While this approach would fix the known edge cases with metaclasses, it
561581
introduces significant complexity to all classes, including a new built-in type
562582
(for the annotations descriptor) with unusual behavior.
563583

564-
The alternative approach would be to never set ``__dict__["__annotations__"]``
565-
and use some other storage to store the cached annotations. This behavior
566-
change would have to apply even to classes defined under
567-
``from __future__ import annotations``, because otherwise there could be buggy
568-
behavior if a class is defined without ``from __future__ import annotations``
569-
but its metaclass does have the future enabled. As :pep:`649` previously noted,
570-
removing ``__annotations__`` from class dictionaries also has backwards compatibility
571-
implications: ``cls.__dict__.get("__annotations__")`` is a common idiom to
572-
retrieve annotations.
573-
574-
This approach would also mean that accessing ``.__annotations__`` on an instance
575-
of an annotated class no longer works. While this behavior is not documented,
576-
it is a long-standing feature of Python and is relied upon by some users.
584+
The second approach is simple to implement, but has the downside that direct
585+
access to ``cls.__annotations__`` remains prone to erratic behavior.
577586

578587
Adding the ``VALUE_WITH_FAKE_GLOBALS`` format
579588
=============================================
@@ -611,10 +620,17 @@ the ``VALUE_WITH_FAKE_GLOBALS`` format is requested, so the standard
611620
library will not call the manually written annotate function with
612621
"fake globals", which could have unpredictable results.
613622

623+
The names of annotation formats indicate what kind of objects an
624+
``__annotate__`` function should return: with the ``STRING`` format, it
625+
should return strings; with the ``FORWARDREF`` format, it should return
626+
forward references; and with the ``VALUE`` format, it should return values.
627+
The name ``VALUE_WITH_FAKE_GLOBALS`` indicates that the function should
628+
still return values, but is being executed in an unusual "fake globals" environment.
629+
614630
Specification
615631
-------------
616632

617-
An additional format, ``FAKE_GLOBALS_VALUE``, is added to the ``Format`` enum in the
633+
An additional format, ``VALUE_WITH_FAKE_GLOBALS``, is added to the ``Format`` enum in the
618634
``annotationlib`` module, with value equal to 2. (As a result, the values of the
619635
other formats will shift relative to PEP 649: ``FORWARDREF`` will be 3 and ``SOURCE``
620636
will be 4.)
@@ -625,10 +641,10 @@ they would return for the ``VALUE`` format. The standard library will pass
625641
this format to the ``__annotate__`` function when it is called in a "fake globals"
626642
environment, as used to implement the ``FORWARDREF`` and ``SOURCE`` formats.
627643
All public functions in the ``annotationlib`` module that accept a format
628-
argument will raise :py:exc:`NotImplementedError` if the format is ``FAKE_GLOBALS_VALUE``.
644+
argument will raise :py:exc:`NotImplementedError` if the format is ``VALUE_WITH_FAKE_GLOBALS``.
629645

630646
Third-party code that implements ``__annotate__`` functions should raise
631-
:py:exc:`NotImplementedError` if the ``FAKE_GLOBALS_VALUE`` format is passed
647+
:py:exc:`NotImplementedError` if the ``VALUE_WITH_FAKE_GLOBALS`` format is passed
632648
and the function is not prepared to be run in a "fake globals" environment.
633649
This should be mentioned in the data model documentation for ``__annotate__``.
634650

0 commit comments

Comments
 (0)