PEP 649: typo fixes and minor clarifications (#3113)
* PEP 649: typo fixes and minor clarifications * review comments
This commit is contained in:
parent
468f3ed4eb
commit
076f44117a
79
pep-0649.rst
79
pep-0649.rst
|
@ -55,7 +55,7 @@ annotations, the Python compiler will write the expressions
|
||||||
computing the annotations into its own function. When run,
|
computing the annotations into its own function. When run,
|
||||||
the function will return the annotations dict. The Python
|
the function will return the annotations dict. The Python
|
||||||
compiler then stores a reference to this function in
|
compiler then stores a reference to this function in
|
||||||
``__annotate`` on the object.
|
``__annotate__`` on the object.
|
||||||
|
|
||||||
Furthermore, ``__annotations__`` is redefined to be a
|
Furthermore, ``__annotations__`` is redefined to be a
|
||||||
"data descriptor" which calls this annotation function once
|
"data descriptor" which calls this annotation function once
|
||||||
|
@ -362,7 +362,9 @@ traditionally better served with stock semantics. This use case is
|
||||||
largely incompatible with :pep:`563`, particularly with the
|
largely incompatible with :pep:`563`, particularly with the
|
||||||
``if typing.TYPE_CHECKING`` idiom.
|
``if typing.TYPE_CHECKING`` idiom.
|
||||||
|
|
||||||
Under this PEP, runtime annotation users will use ``VALUE`` format.
|
Under this PEP, runtime annotation users will most likely prefer ``VALUE``
|
||||||
|
format, though some (e.g. if they evaluate annotations eagerly in a decorator
|
||||||
|
and want to support forward references) may also use ``FORWARDREF`` format.
|
||||||
|
|
||||||
|
|
||||||
Wrappers
|
Wrappers
|
||||||
|
@ -425,7 +427,7 @@ particularly inconvenient for decorators that wrap existing
|
||||||
functions and classes, as these decorators often use closures.
|
functions and classes, as these decorators often use closures.
|
||||||
|
|
||||||
Second, in order for ``eval`` to correctly look up globals in a
|
Second, in order for ``eval`` to correctly look up globals in a
|
||||||
stringized annotation, you must first obtaining a reference
|
stringized annotation, you must first obtain a reference
|
||||||
to the correct module.
|
to the correct module.
|
||||||
But class objects don't retain a reference to their globals.
|
But class objects don't retain a reference to their globals.
|
||||||
:pep:`563` suggests looking up a class's module by name in
|
:pep:`563` suggests looking up a class's module by name in
|
||||||
|
@ -437,11 +439,11 @@ difficult to determine the correct globals and locals dicts to
|
||||||
give to ``eval`` to properly evaluate a stringized annotation.
|
give to ``eval`` to properly evaluate a stringized annotation.
|
||||||
Even worse, in some situations it may simply be infeasible.
|
Even worse, in some situations it may simply be infeasible.
|
||||||
|
|
||||||
For example, some libraries (e.g. TypedDict, dataclass) wrap a user
|
For example, some libraries (e.g. ``typing.TypedDict``, :mod:`dataclasses`)
|
||||||
class, then merge all the annotations from all that class's base
|
wrap a user class, then merge all the annotations from all that
|
||||||
classes together into one cumulative annotations dict. If those
|
class's base classes together into one cumulative annotations dict.
|
||||||
annotations were stringized, calling ``eval`` on them later may
|
If those annotations were stringized, calling ``eval`` on them later
|
||||||
not work properly, because the globals dictionary used for the
|
may not work properly, because the globals dictionary used for the
|
||||||
``eval`` will be the module where the *user class* was defined,
|
``eval`` will be the module where the *user class* was defined,
|
||||||
which may not be the same module where the *annotation* was
|
which may not be the same module where the *annotation* was
|
||||||
defined. However, if the annotations were stringized because
|
defined. However, if the annotations were stringized because
|
||||||
|
@ -464,7 +466,7 @@ of that string will fail. This is a problem for libraries with
|
||||||
that need to examine the annotation, because they can't reliably
|
that need to examine the annotation, because they can't reliably
|
||||||
convert these stringized annotations into real values.
|
convert these stringized annotations into real values.
|
||||||
|
|
||||||
* Some libraries (e.g. ``dataclass``) solved this by foregoing real
|
* Some libraries (e.g. :mod:`dataclasses`) solved this by foregoing real
|
||||||
values and performing lexical analysis of the stringized annotation,
|
values and performing lexical analysis of the stringized annotation,
|
||||||
which requires a lot of work to get right.
|
which requires a lot of work to get right.
|
||||||
|
|
||||||
|
@ -476,7 +478,7 @@ Also, ``eval()`` is slow, and it isn't always available; it's
|
||||||
sometimes removed for space reasons on certain platforms.
|
sometimes removed for space reasons on certain platforms.
|
||||||
``eval()`` on MicroPython doesn't support the ``locals``
|
``eval()`` on MicroPython doesn't support the ``locals``
|
||||||
argument, which makes converting stringized annotations
|
argument, which makes converting stringized annotations
|
||||||
into real values at runtime even harder..
|
into real values at runtime even harder.
|
||||||
|
|
||||||
Finally, :pep:`563` requires Python implementations to
|
Finally, :pep:`563` requires Python implementations to
|
||||||
stringize their annotations. This is surprising behavior—unprecedented
|
stringize their annotations. This is surprising behavior—unprecedented
|
||||||
|
@ -530,7 +532,7 @@ __annotate__ and __annotations__
|
||||||
================================
|
================================
|
||||||
|
|
||||||
Python supports annotations on three different types:
|
Python supports annotations on three different types:
|
||||||
function, classes, and modules. This PEP modifies
|
functions, classes, and modules. This PEP modifies
|
||||||
the semantics on all three of these types in a similar
|
the semantics on all three of these types in a similar
|
||||||
way.
|
way.
|
||||||
|
|
||||||
|
@ -554,7 +556,7 @@ code should prefer to interact with it only via the
|
||||||
|
|
||||||
The callable stored in ``__annotate__`` must accept a
|
The callable stored in ``__annotate__`` must accept a
|
||||||
single required positional argument called ``format``,
|
single required positional argument called ``format``,
|
||||||
which will always be a ``int``. It must either return
|
which will always be an ``int``. It must either return
|
||||||
a dict (or subclass of dict) or raise
|
a dict (or subclass of dict) or raise
|
||||||
``NotImplementedError()``.
|
``NotImplementedError()``.
|
||||||
|
|
||||||
|
@ -599,9 +601,9 @@ Language Reference:
|
||||||
may raise ``NameError``; it must not raise ``NameError`` when called
|
may raise ``NameError``; it must not raise ``NameError`` when called
|
||||||
requesting any other format.
|
requesting any other format.
|
||||||
|
|
||||||
If an object doesn't have any annotations, ``__annotate__``
|
If an object doesn't have any annotations, ``__annotate__`` should
|
||||||
should preferably be deleted or set to ``None``, rather than set to
|
preferably be set to ``None`` (it can't be deleted), rather than set to a
|
||||||
a function that returns an empty dict.
|
function that returns an empty dict.
|
||||||
|
|
||||||
When the Python compiler compiles an object with
|
When the Python compiler compiles an object with
|
||||||
annotations, it simultaneously compiles the appropriate
|
annotations, it simultaneously compiles the appropriate
|
||||||
|
@ -619,7 +621,7 @@ the appropriate namespaces:
|
||||||
* For methods on classes, and for classes, the locals dictionary
|
* For methods on classes, and for classes, the locals dictionary
|
||||||
will be the class dictionary.
|
will be the class dictionary.
|
||||||
* If the annotations refer to free variables, the closure will
|
* If the annotations refer to free variables, the closure will
|
||||||
be the appropriate tuple containing free variables.
|
be the appropriate closure tuple containing cells for free variables.
|
||||||
|
|
||||||
Second, this PEP requires that the existing
|
Second, this PEP requires that the existing
|
||||||
``__annotations__`` must be a "data descriptor",
|
``__annotations__`` must be a "data descriptor",
|
||||||
|
@ -644,7 +646,7 @@ an object of any of these three types:
|
||||||
* When ``o.__annotations__`` is evaluated, and the internal storage
|
* When ``o.__annotations__`` is evaluated, and the internal storage
|
||||||
for ``o.__annotations__`` is unset, and ``o.__annotate__`` is set
|
for ``o.__annotations__`` is unset, and ``o.__annotate__`` is set
|
||||||
to a callable, the getter for ``o.__annotations__`` calls
|
to a callable, the getter for ``o.__annotations__`` calls
|
||||||
``o.__annotate__(1)``, then caches the result in its intenral
|
``o.__annotate__(1)``, then caches the result in its internal
|
||||||
storage and returns the result.
|
storage and returns the result.
|
||||||
|
|
||||||
- To explicitly clarify one question that has come up multiple times:
|
- To explicitly clarify one question that has come up multiple times:
|
||||||
|
@ -695,7 +697,7 @@ makes some modifications to the annotations before it returns them.
|
||||||
This PEP adds a new keyword-only parameter to these two functions,
|
This PEP adds a new keyword-only parameter to these two functions,
|
||||||
``format``. ``format`` specifies what format the values in the
|
``format``. ``format`` specifies what format the values in the
|
||||||
annotations dict should be returned in.
|
annotations dict should be returned in.
|
||||||
``format`` accepts following values, defined as attributes on the
|
``format`` accepts the following values, defined as attributes on the
|
||||||
``inspect`` module::
|
``inspect`` module::
|
||||||
|
|
||||||
VALUE = 1
|
VALUE = 1
|
||||||
|
@ -713,15 +715,14 @@ the minimum and maximum supported ``format`` values::
|
||||||
FORMAT_MAX = SOURCE
|
FORMAT_MAX = SOURCE
|
||||||
|
|
||||||
|
|
||||||
Also, when either ``__annotations__`` or ``__annotate__``
|
Also, when either ``__annotations__`` or ``__annotate__`` is updated on an
|
||||||
is updated on an object, the other of those two attributes
|
object, the other of those two attributes is now out-of-date and should also
|
||||||
is now out-of-date and should also either be updated or
|
either be updated or deleted (set to ``None``, in the case of ``__annotate__``
|
||||||
deleted. In general, the semantics established in the
|
which cannot be deleted). In general, the semantics established in the previous
|
||||||
previous section ensure that this happens automatically.
|
section ensure that this happens automatically. However, there's one case which
|
||||||
However, there's one case which for all practical
|
for all practical purposes can't be handled automatically: when the dict cached
|
||||||
purposes can't be handled automatically: when the dict cached
|
by ``o.__annotations__`` is itself modified, or when mutable values inside that
|
||||||
by ``o.__annotations__`` is itself modified, or when mutable
|
dict are modified.
|
||||||
values inside that dict are modified.
|
|
||||||
|
|
||||||
Since this can't be handled in code, it must be handled in
|
Since this can't be handled in code, it must be handled in
|
||||||
documentation. This PEP proposes amending the documentation
|
documentation. This PEP proposes amending the documentation
|
||||||
|
@ -735,9 +736,8 @@ for ``inspect.get_annotations`` (and similarly for
|
||||||
modifying the ``__annotations__`` dict directly, consider replacing
|
modifying the ``__annotations__`` dict directly, consider replacing
|
||||||
that object's ``__annotate__`` method with a function computing
|
that object's ``__annotate__`` method with a function computing
|
||||||
the annotations dict with your desired values. Failing that, it's
|
the annotations dict with your desired values. Failing that, it's
|
||||||
best to overwrite the object's ``__annotate__`` method with ``None``,
|
best to overwrite the object's ``__annotate__`` method with ``None``
|
||||||
or delete ``__annotate__`` from the object, to prevent
|
to prevent ``inspect.get_annotations`` from generating stale results
|
||||||
``inspect.get_annotations`` from generating stale results
|
|
||||||
for ``SOURCE`` and ``FORWARDREF`` formats.
|
for ``SOURCE`` and ``FORWARDREF`` formats.
|
||||||
|
|
||||||
|
|
||||||
|
@ -762,8 +762,7 @@ dict is replaced with what's called a "fake globals" dict.
|
||||||
A "fake globals" dict is a dict with one important difference:
|
A "fake globals" dict is a dict with one important difference:
|
||||||
every time you "get" a key from it that isn't mapped,
|
every time you "get" a key from it that isn't mapped,
|
||||||
it creates, caches, and returns a new value for that key
|
it creates, caches, and returns a new value for that key
|
||||||
(as per the ``__missing__`` callback for a
|
(as per the ``__missing__`` callback for a dictionary).
|
||||||
``collections.defaultdict``).
|
|
||||||
That value is a an instance of a novel type referred to
|
That value is a an instance of a novel type referred to
|
||||||
as a "stringizer".
|
as a "stringizer".
|
||||||
|
|
||||||
|
@ -818,13 +817,13 @@ supports ``FORWARDREF`` and ``SOURCE`` formats. Initially,
|
||||||
``__annotate__`` method requesting the desired format.
|
``__annotate__`` method requesting the desired format.
|
||||||
If that raises ``NotImplementedError``, ``inspect.get_annotations``
|
If that raises ``NotImplementedError``, ``inspect.get_annotations``
|
||||||
will construct a "fake globals" environment, then call
|
will construct a "fake globals" environment, then call
|
||||||
the object's ``__annotate__`` method
|
the object's ``__annotate__`` method.
|
||||||
|
|
||||||
* ``inspect.get_annotations`` produces ``SOURCE`` format
|
* ``inspect.get_annotations`` produces ``SOURCE`` format
|
||||||
by creating a new empty "fake globals" dict, binding it
|
by creating a new empty "fake globals" dict, binding it
|
||||||
to the object's ``__annotate__`` method, calling that
|
to the object's ``__annotate__`` method, calling that
|
||||||
requesting ``VALUE`` format, and
|
requesting ``VALUE`` format, and then extracting the string
|
||||||
then extracting the "value" from each ``FowardRef`` object
|
"value" from each ``ForwardRef`` object
|
||||||
in the resulting dict.
|
in the resulting dict.
|
||||||
|
|
||||||
* ``inspect.get_annotations`` produces ``FORWARDREF`` format
|
* ``inspect.get_annotations`` produces ``FORWARDREF`` format
|
||||||
|
@ -845,7 +844,7 @@ methods, this approach is a reliable, practical solution.
|
||||||
|
|
||||||
However, it's not reasonable to attempt this technique with
|
However, it's not reasonable to attempt this technique with
|
||||||
just any ``__annotate__`` method. This PEP assumes that
|
just any ``__annotate__`` method. This PEP assumes that
|
||||||
third-party libraries will implement their own ``__annotate__``
|
third-party libraries may implement their own ``__annotate__``
|
||||||
methods, and those functions would almost certainly work
|
methods, and those functions would almost certainly work
|
||||||
incorrectly when run in this "fake globals" environment.
|
incorrectly when run in this "fake globals" environment.
|
||||||
For that reason, this PEP allocates a flag on code objects,
|
For that reason, this PEP allocates a flag on code objects,
|
||||||
|
@ -928,7 +927,7 @@ to implement their own ``__annotate__`` methods,
|
||||||
so that downstream users of
|
so that downstream users of
|
||||||
those objects can take full advantage of annotations.
|
those objects can take full advantage of annotations.
|
||||||
In particular, wrappers will likely need to transform
|
In particular, wrappers will likely need to transform
|
||||||
the annotation dicts produced by the wrapped object--adding,
|
the annotation dicts produced by the wrapped object: adding,
|
||||||
removing, or modifying the dictionary in some way.
|
removing, or modifying the dictionary in some way.
|
||||||
|
|
||||||
Most of the time, third-party code will implement
|
Most of the time, third-party code will implement
|
||||||
|
@ -1117,7 +1116,7 @@ When annotations are both defined and referenced, code using
|
||||||
this PEP should be much faster than :pep:`563`, and be as fast
|
this PEP should be much faster than :pep:`563`, and be as fast
|
||||||
or faster than stock. :pep:`563` semantics requires invoking
|
or faster than stock. :pep:`563` semantics requires invoking
|
||||||
``eval()`` for every value inside an annotations dict which is
|
``eval()`` for every value inside an annotations dict which is
|
||||||
enormously slow. And the implementation of PEP generates measurably
|
enormously slow. And the implementation of this PEP generates measurably
|
||||||
more efficient bytecode for class and module annotations than stock
|
more efficient bytecode for class and module annotations than stock
|
||||||
semantics; for function annotations, this PEP and stock semantics
|
semantics; for function annotations, this PEP and stock semantics
|
||||||
should be about the same speed.
|
should be about the same speed.
|
||||||
|
@ -1193,7 +1192,7 @@ Python version the code runs under.
|
||||||
|
|
||||||
|
|
||||||
Since delaying the evaluation of annotations until they are
|
Since delaying the evaluation of annotations until they are
|
||||||
evaluated changes the semantics of the language, it's observable
|
introspected changes the semantics of the language, it's observable
|
||||||
from within the language. Therefore it's *possible* to write code
|
from within the language. Therefore it's *possible* to write code
|
||||||
that behaves differently based on whether annotations are
|
that behaves differently based on whether annotations are
|
||||||
evaluated at binding time or at access time, e.g.
|
evaluated at binding time or at access time, e.g.
|
||||||
|
@ -1312,7 +1311,7 @@ dicts; however, users should consider switching to
|
||||||
|
|
||||||
Similarly, :pep:`563` permitted use of class decorators on
|
Similarly, :pep:`563` permitted use of class decorators on
|
||||||
annotated classes in a way that hadn't previously been possible.
|
annotated classes in a way that hadn't previously been possible.
|
||||||
Some class decorators (e.g. ``dataclasses``) examine the annotations
|
Some class decorators (e.g. :mod:`dataclasses`) examine the annotations
|
||||||
on the class. Because class decorators using the ``@`` decorator
|
on the class. Because class decorators using the ``@`` decorator
|
||||||
syntax are run before the class name is bound, they can cause
|
syntax are run before the class name is bound, they can cause
|
||||||
unsolvable circular-definition problems. If you annotate attributes
|
unsolvable circular-definition problems. If you annotate attributes
|
||||||
|
|
Loading…
Reference in New Issue