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,
|
||||
the function will return the annotations dict. The Python
|
||||
compiler then stores a reference to this function in
|
||||
``__annotate`` on the object.
|
||||
``__annotate__`` on the object.
|
||||
|
||||
Furthermore, ``__annotations__`` is redefined to be a
|
||||
"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
|
||||
``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
|
||||
|
@ -425,7 +427,7 @@ particularly inconvenient for decorators that wrap existing
|
|||
functions and classes, as these decorators often use closures.
|
||||
|
||||
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.
|
||||
But class objects don't retain a reference to their globals.
|
||||
: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.
|
||||
Even worse, in some situations it may simply be infeasible.
|
||||
|
||||
For example, some libraries (e.g. TypedDict, dataclass) wrap a user
|
||||
class, then merge all the annotations from all that class's base
|
||||
classes together into one cumulative annotations dict. If those
|
||||
annotations were stringized, calling ``eval`` on them later may
|
||||
not work properly, because the globals dictionary used for the
|
||||
For example, some libraries (e.g. ``typing.TypedDict``, :mod:`dataclasses`)
|
||||
wrap a user class, then merge all the annotations from all that
|
||||
class's base classes together into one cumulative annotations dict.
|
||||
If those annotations were stringized, calling ``eval`` on them later
|
||||
may not work properly, because the globals dictionary used for the
|
||||
``eval`` will be the module where the *user class* was defined,
|
||||
which may not be the same module where the *annotation* was
|
||||
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
|
||||
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,
|
||||
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.
|
||||
``eval()`` on MicroPython doesn't support the ``locals``
|
||||
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
|
||||
stringize their annotations. This is surprising behavior—unprecedented
|
||||
|
@ -530,7 +532,7 @@ __annotate__ and __annotations__
|
|||
================================
|
||||
|
||||
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
|
||||
way.
|
||||
|
||||
|
@ -554,7 +556,7 @@ code should prefer to interact with it only via the
|
|||
|
||||
The callable stored in ``__annotate__`` must accept a
|
||||
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
|
||||
``NotImplementedError()``.
|
||||
|
||||
|
@ -599,9 +601,9 @@ Language Reference:
|
|||
may raise ``NameError``; it must not raise ``NameError`` when called
|
||||
requesting any other format.
|
||||
|
||||
If an object doesn't have any annotations, ``__annotate__``
|
||||
should preferably be deleted or set to ``None``, rather than set to
|
||||
a function that returns an empty dict.
|
||||
If an object doesn't have any annotations, ``__annotate__`` should
|
||||
preferably be set to ``None`` (it can't be deleted), rather than set to a
|
||||
function that returns an empty dict.
|
||||
|
||||
When the Python compiler compiles an object with
|
||||
annotations, it simultaneously compiles the appropriate
|
||||
|
@ -619,7 +621,7 @@ the appropriate namespaces:
|
|||
* For methods on classes, and for classes, the locals dictionary
|
||||
will be the class dictionary.
|
||||
* 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
|
||||
``__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
|
||||
for ``o.__annotations__`` is unset, and ``o.__annotate__`` is set
|
||||
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.
|
||||
|
||||
- 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,
|
||||
``format``. ``format`` specifies what format the values in the
|
||||
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::
|
||||
|
||||
VALUE = 1
|
||||
|
@ -713,15 +715,14 @@ the minimum and maximum supported ``format`` values::
|
|||
FORMAT_MAX = SOURCE
|
||||
|
||||
|
||||
Also, when either ``__annotations__`` or ``__annotate__``
|
||||
is updated on an object, the other of those two attributes
|
||||
is now out-of-date and should also either be updated or
|
||||
deleted. In general, the semantics established in the
|
||||
previous section ensure that this happens automatically.
|
||||
However, there's one case which for all practical
|
||||
purposes can't be handled automatically: when the dict cached
|
||||
by ``o.__annotations__`` is itself modified, or when mutable
|
||||
values inside that dict are modified.
|
||||
Also, when either ``__annotations__`` or ``__annotate__`` is updated on an
|
||||
object, the other of those two attributes is now out-of-date and should also
|
||||
either be updated or deleted (set to ``None``, in the case of ``__annotate__``
|
||||
which cannot be deleted). In general, the semantics established in the previous
|
||||
section ensure that this happens automatically. However, there's one case which
|
||||
for all practical purposes can't be handled automatically: when the dict cached
|
||||
by ``o.__annotations__`` is itself modified, or when mutable values inside that
|
||||
dict are modified.
|
||||
|
||||
Since this can't be handled in code, it must be handled in
|
||||
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
|
||||
that object's ``__annotate__`` method with a function computing
|
||||
the annotations dict with your desired values. Failing that, it's
|
||||
best to overwrite the object's ``__annotate__`` method with ``None``,
|
||||
or delete ``__annotate__`` from the object, to prevent
|
||||
``inspect.get_annotations`` from generating stale results
|
||||
best to overwrite the object's ``__annotate__`` method with ``None``
|
||||
to prevent ``inspect.get_annotations`` from generating stale results
|
||||
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:
|
||||
every time you "get" a key from it that isn't mapped,
|
||||
it creates, caches, and returns a new value for that key
|
||||
(as per the ``__missing__`` callback for a
|
||||
``collections.defaultdict``).
|
||||
(as per the ``__missing__`` callback for a dictionary).
|
||||
That value is a an instance of a novel type referred to
|
||||
as a "stringizer".
|
||||
|
||||
|
@ -818,13 +817,13 @@ supports ``FORWARDREF`` and ``SOURCE`` formats. Initially,
|
|||
``__annotate__`` method requesting the desired format.
|
||||
If that raises ``NotImplementedError``, ``inspect.get_annotations``
|
||||
will construct a "fake globals" environment, then call
|
||||
the object's ``__annotate__`` method
|
||||
the object's ``__annotate__`` method.
|
||||
|
||||
* ``inspect.get_annotations`` produces ``SOURCE`` format
|
||||
by creating a new empty "fake globals" dict, binding it
|
||||
to the object's ``__annotate__`` method, calling that
|
||||
requesting ``VALUE`` format, and
|
||||
then extracting the "value" from each ``FowardRef`` object
|
||||
requesting ``VALUE`` format, and then extracting the string
|
||||
"value" from each ``ForwardRef`` object
|
||||
in the resulting dict.
|
||||
|
||||
* ``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
|
||||
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
|
||||
incorrectly when run in this "fake globals" environment.
|
||||
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
|
||||
those objects can take full advantage of annotations.
|
||||
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.
|
||||
|
||||
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
|
||||
or faster than stock. :pep:`563` semantics requires invoking
|
||||
``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
|
||||
semantics; for function annotations, this PEP and stock semantics
|
||||
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
|
||||
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
|
||||
that behaves differently based on whether annotations are
|
||||
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
|
||||
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
|
||||
syntax are run before the class name is bound, they can cause
|
||||
unsolvable circular-definition problems. If you annotate attributes
|
||||
|
|
Loading…
Reference in New Issue