<div dir="ltr"><div><div><div>Łukasz,<br><br></div>I am hereby accepting your PEP. This will be a great improvement in the experience of users annotating large complex codebases. Congrats on the design and implementation and on your shepherding the PEP through the discussion phase. Also a special thanks to Serhiy for thoroughly reviewing and contributing to the ast-expr-stringification code.<br><br></div>--Guido<br><br></div>PS. I have some editorial quibbles (mostly suggestions to make the exposition clearer in a few places) but they don't affect acceptance of the PEP and I will contact you at a later time with these.<br><div><br></div></div><div class="gmail_extra"><br><div class="gmail_quote">On Tue, Nov 21, 2017 at 4:26 PM, Lukasz Langa <span dir="ltr"><<a href="mailto:lukasz@langa.pl" target="_blank">lukasz@langa.pl</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Based on the feedback I gather in early November,<br>
I'm publishing the third draft for consideration on python-dev.<br>
I hope you like it!<br>
<br>
A nicely formatted rendering is available here:<br>
<a href="https://www.python.org/dev/peps/pep-0563/" rel="noreferrer" target="_blank">https://www.python.org/dev/<wbr>peps/pep-0563/</a><br>
<br>
The full list of changes between this version and the previous draft<br>
can be found here:<br>
<a href="https://github.com/ambv/static-annotations/compare/python-dev1...python-dev2" rel="noreferrer" target="_blank">https://github.com/ambv/<wbr>static-annotations/compare/<wbr>python-dev1...python-dev2</a><br>
<br>
- Ł<br>
<br>
<br>
<br>
PEP: 563<br>
Title: Postponed Evaluation of Annotations<br>
Version: $Revision$<br>
Last-Modified: $Date$<br>
Author: Łukasz Langa <<a href="mailto:lukasz@langa.pl">lukasz@langa.pl</a>><br>
Discussions-To: Python-Dev <<a href="mailto:python-dev@python.org">python-dev@python.org</a>><br>
Status: Draft<br>
Type: Standards Track<br>
Content-Type: text/x-rst<br>
Created: 8-Sep-2017<br>
Python-Version: 3.7<br>
Post-History: 1-Nov-2017, 21-Nov-2017<br>
Resolution:<br>
<br>
<br>
Abstract<br>
========<br>
<br>
PEP 3107 introduced syntax for function annotations, but the semantics<br>
were deliberately left undefined.  PEP 484 introduced a standard meaning<br>
to annotations: type hints.  PEP 526 defined variable annotations,<br>
explicitly tying them with the type hinting use case.<br>
<br>
This PEP proposes changing function annotations and variable annotations<br>
so that they are no longer evaluated at function definition time.<br>
Instead, they are preserved in ``__annotations__`` in string form.<br>
<br>
This change is going to be introduced gradually, starting with a new<br>
``__future__`` import in Python 3.7.<br>
<br>
<br>
Rationale and Goals<br>
===================<br>
<br>
PEP 3107 added support for arbitrary annotations on parts of a function<br>
definition.  Just like default values, annotations are evaluated at<br>
function definition time.  This creates a number of issues for the type<br>
hinting use case:<br>
<br>
* forward references: when a type hint contains names that have not been<br>
  defined yet, that definition needs to be expressed as a string<br>
  literal;<br>
<br>
* type hints are executed at module import time, which is not<br>
  computationally free.<br>
<br>
Postponing the evaluation of annotations solves both problems.<br>
<br>
Non-goals<br>
---------<br>
<br>
Just like in PEP 484 and PEP 526, it should be emphasized that **Python<br>
will remain a dynamically typed language, and the authors have no desire<br>
to ever make type hints mandatory, even by convention.**<br>
<br>
This PEP is meant to solve the problem of forward references in type<br>
annotations.  There are still cases outside of annotations where<br>
forward references will require usage of string literals.  Those are<br>
listed in a later section of this document.<br>
<br>
Annotations without forced evaluation enable opportunities to improve<br>
the syntax of type hints.  This idea will require its own separate PEP<br>
and is not discussed further in this document.<br>
<br>
Non-typing usage of annotations<br>
------------------------------<wbr>-<br>
<br>
While annotations are still available for arbitrary use besides type<br>
checking, it is worth mentioning that the design of this PEP, as well<br>
as its precursors (PEP 484 and PEP 526), is predominantly motivated by<br>
the type hinting use case.<br>
<br>
In Python 3.8 PEP 484 will graduate from provisional status.  Other<br>
enhancements to the Python programming language like PEP 544, PEP 557,<br>
or PEP 560, are already being built on this basis as they depend on<br>
type annotations and the ``typing`` module as defined by PEP 484.<br>
In fact, the reason PEP 484 is staying provisional in Python 3.7 is to<br>
enable rapid evolution for another release cycle that some of the<br>
aforementioned enhancements require.<br>
<br>
With this in mind, uses for annotations incompatible with the<br>
aforementioned PEPs should be considered deprecated.<br>
<br>
<br>
Implementation<br>
==============<br>
<br>
In Python 4.0, function and variable annotations will no longer be<br>
evaluated at definition time.  Instead, a string form will be preserved<br>
in the respective ``__annotations__`` dictionary.  Static type checkers<br>
will see no difference in behavior, whereas tools using annotations at<br>
runtime will have to perform postponed evaluation.<br>
<br>
The string form is obtained from the AST during the compilation step,<br>
which means that the string form might not preserve the exact formatting<br>
of the source.  Note: if an annotation was a string literal already, it<br>
will still be wrapped in a string.<br>
<br>
Annotations need to be syntactically valid Python expressions, also when<br>
passed as literal strings (i.e. ``compile(literal, '', 'eval')``).<br>
Annotations can only use names present in the module scope as postponed<br>
evaluation using local names is not reliable (with the sole exception of<br>
class-level names resolved by ``typing.get_type_hints()``).<br>
<br>
Note that as per PEP 526, local variable annotations are not evaluated<br>
at all since they are not accessible outside of the function's closure.<br>
<br>
Enabling the future behavior in Python 3.7<br>
------------------------------<wbr>------------<br>
<br>
The functionality described above can be enabled starting from Python<br>
3.7 using the following special import::<br>
<br>
    from __future__ import annotations<br>
<br>
A reference implementation of this functionality is available<br>
`on GitHub <<a href="https://github.com/ambv/cpython/tree/string_annotations" rel="noreferrer" target="_blank">https://github.com/ambv/<wbr>cpython/tree/string_<wbr>annotations</a>>`_.<br>
<br>
<br>
Resolving Type Hints at Runtime<br>
==============================<wbr>=<br>
<br>
To resolve an annotation at runtime from its string form to the result<br>
of the enclosed expression, user code needs to evaluate the string.<br>
<br>
For code that uses type hints, the<br>
``typing.get_type_hints(obj, globalns=None, localns=None)`` function<br>
correctly evaluates expressions back from its string form.  Note that<br>
all valid code currently using ``__annotations__`` should already be<br>
doing that since a type annotation can be expressed as a string literal.<br>
<br>
For code which uses annotations for other purposes, a regular<br>
``eval(ann, globals, locals)`` call is enough to resolve the<br>
annotation.<br>
<br>
In both cases it's important to consider how globals and locals affect<br>
the postponed evaluation.  An annotation is no longer evaluated at the<br>
time of definition and, more importantly, *in the same scope* where it<br>
was defined.  Consequently, using local state in annotations is no<br>
longer possible in general.  As for globals, the module where the<br>
annotation was defined is the correct context for postponed evaluation.<br>
<br>
The ``get_type_hints()`` function automatically resolves the correct<br>
value of ``globalns`` for functions and classes.  It also automatically<br>
provides the correct ``localns`` for classes.<br>
<br>
When running ``eval()``,<br>
the value of globals can be gathered in the following way:<br>
<br>
* function objects hold a reference to their respective globals in an<br>
  attribute called ``__globals__``;<br>
<br>
* classes hold the name of the module they were defined in, this can be<br>
  used to retrieve the respective globals::<br>
<br>
    cls_globals = vars(sys.modules[SomeClass.__<wbr>module__])<br>
<br>
  Note that this needs to be repeated for base classes to evaluate all<br>
  ``__annotations__``.<br>
<br>
* modules should use their own ``__dict__``.<br>
<br>
The value of ``localns`` cannot be reliably retrieved for functions<br>
because in all likelihood the stack frame at the time of the call no<br>
longer exists.<br>
<br>
For classes, ``localns`` can be composed by chaining vars of the given<br>
class and its base classes (in the method resolution order).  Since slots<br>
can only be filled after the class was defined, we don't need to consult<br>
them for this purpose.<br>
<br>
Runtime annotation resolution and class decorators<br>
------------------------------<wbr>--------------------<br>
<br>
Metaclasses and class decorators that need to resolve annotations for<br>
the current class will fail for annotations that use the name of the<br>
current class.  Example::<br>
<br>
    def class_decorator(cls):<br>
        annotations = get_type_hints(cls)  # raises NameError on 'C'<br>
        print(f'Annotations for {cls}: {annotations}')<br>
        return cls<br>
<br>
    @class_decorator<br>
    class C:<br>
        singleton: 'C' = None<br>
<br>
This was already true before this PEP.  The class decorator acts on<br>
the class before it's assigned a name in the current definition scope.<br>
<br>
Runtime annotation resolution and ``TYPE_CHECKING``<br>
------------------------------<wbr>---------------------<br>
<br>
Sometimes there's code that must be seen by a type checker but should<br>
not be executed.  For such situations the ``typing`` module defines a<br>
constant, ``TYPE_CHECKING``, that is considered ``True`` during type<br>
checking but ``False`` at runtime.  Example::<br>
<br>
  import typing<br>
<br>
  if typing.TYPE_CHECKING:<br>
      import expensive_mod<br>
<br>
  def a_func(arg: expensive_mod.SomeClass) -> None:<br>
      a_var: expensive_mod.SomeClass = arg<br>
      ...<br>
<br>
This approach is also useful when handling import cycles.<br>
<br>
Trying to resolve annotations of ``a_func`` at runtime using<br>
``typing.get_type_hints()`` will fail since the name ``expensive_mod``<br>
is not defined (``TYPE_CHECKING`` variable being ``False`` at runtime).<br>
This was already true before this PEP.<br>
<br>
<br>
Backwards Compatibility<br>
=======================<br>
<br>
This is a backwards incompatible change.  Applications depending on<br>
arbitrary objects to be directly present in annotations will break<br>
if they are not using ``typing.get_type_hints()`` or ``eval()``.<br>
<br>
Annotations that depend on locals at the time of the function<br>
definition will not be resolvable later.  Example::<br>
<br>
    def generate():<br>
        A = Optional[int]<br>
        class C:<br>
            field: A = 1<br>
            def method(self, arg: A) -> None: ...<br>
        return C<br>
    X = generate()<br>
<br>
Trying to resolve annotations of ``X`` later by using<br>
``get_type_hints(X)`` will fail because ``A`` and its enclosing scope no<br>
longer exists.  Python will make no attempt to disallow such annotations<br>
since they can often still be successfully statically analyzed, which is<br>
the predominant use case for annotations.<br>
<br>
Annotations using nested classes and their respective state are still<br>
valid.  They can use local names or the fully qualified name.  Example::<br>
<br>
    class C:<br>
        field = 'c_field'<br>
        def method(self) -> C.field:  # this is OK<br>
            ...<br>
<br>
        def method(self) -> field:  # this is OK<br>
            ...<br>
<br>
        def method(self) -> C.D:  # this is OK<br>
            ...<br>
<br>
        def method(self) -> D:  # this is OK<br>
            ...<br>
<br>
        class D:<br>
            field2 = 'd_field'<br>
            def method(self) -> C.D.field2:  # this is OK<br>
                ...<br>
<br>
            def method(self) -> D.field2:  # this is OK<br>
                ...<br>
<br>
            def method(self) -> field2:  # this is OK<br>
                ...<br>
<br>
            def method(self) -> field:  # this FAILS, class D doesn't<br>
                ...                     # see C's attributes,  This was<br>
                                        # already true before this PEP.<br>
<br>
In the presence of an annotation that isn't a syntactically valid<br>
expression, SyntaxError is raised at compile time.  However, since names<br>
aren't resolved at that time, no attempt is made to validate whether<br>
used names are correct or not.<br>
<br>
Deprecation policy<br>
------------------<br>
<br>
Starting with Python 3.7, a ``__future__`` import is required to use the<br>
described functionality.  No warnings are raised.<br>
<br>
In Python 3.8 a ``PendingDeprecationWarning`` is raised by the<br>
compiler in the presence of type annotations in modules without the<br>
``__future__`` import.<br>
<br>
Starting with Python 3.9 the warning becomes a ``DeprecationWarning``.<br>
<br>
In Python 4.0 this will become the default behavior.  Use of annotations<br>
incompatible with this PEP is no longer supported.<br>
<br>
<br>
Forward References<br>
==================<br>
<br>
Deliberately using a name before it was defined in the module is called<br>
a forward reference.  For the purpose of this section, we'll call<br>
any name imported or defined within a ``if TYPE_CHECKING:`` block<br>
a forward reference, too.<br>
<br>
This PEP addresses the issue of forward references in *type annotations*.<br>
The use of string literals will no longer be required in this case.<br>
However, there are APIs in the ``typing`` module that use other syntactic<br>
constructs of the language, and those will still require working around<br>
forward references with string literals.  The list includes:<br>
<br>
* type definitions::<br>
<br>
    T = TypeVar('T', bound='<type>')<br>
    UserId = NewType('UserId', '<type>')<br>
    Employee = NamedTuple('Employee', [('name', '<type>', ('id', '<type>')])<br>
<br>
* aliases::<br>
<br>
    Alias = Optional['<type>']<br>
    AnotherAlias = Union['<type>', '<type>']<br>
    YetAnotherAlias = '<type>'<br>
<br>
* casting::<br>
<br>
    cast('<type>', value)<br>
<br>
* base classes::<br>
<br>
    class C(Tuple['<type>', '<type>']): ...<br>
<br>
Depending on the specific case, some of the cases listed above might be<br>
worked around by placing the usage in a ``if TYPE_CHECKING:`` block.<br>
This will not work for any code that needs to be available at runtime,<br>
notably for base classes and casting.  For named tuples, using the new<br>
class definition syntax introduced in Python 3.6 solves the issue.<br>
<br>
In general, fixing the issue for *all* forward references requires<br>
changing how module instantiation is performed in Python, from the<br>
current single-pass top-down model.  This would be a major change in the<br>
language and is out of scope for this PEP.<br>
<br>
<br>
Rejected Ideas<br>
==============<br>
<br>
Keeping the ability to use function local state when defining annotations<br>
------------------------------<wbr>------------------------------<wbr>-------------<br>
<br>
With postponed evaluation, this would require keeping a reference to<br>
the frame in which an annotation got created.  This could be achieved<br>
for example by storing all annotations as lambdas instead of strings.<br>
<br>
This would be prohibitively expensive for highly annotated code as the<br>
frames would keep all their objects alive. That includes predominantly<br>
objects that won't ever be accessed again.<br>
<br>
To be able to address class-level scope, the lambda approach would<br>
require a new kind of cell in the interpreter.  This would proliferate<br>
the number of types that can appear in ``__annotations__``, as well as<br>
wouldn't be as introspectable as strings.<br>
<br>
Note that in the case of nested classes, the functionality to get the<br>
effective "globals" and "locals" at definition time is provided by<br>
``typing.get_type_hints()``.<br>
<br>
If a function generates a class or a function with annotations that<br>
have to use local variables, it can populate the given generated<br>
object's ``__annotations__`` dictionary directly, without relying on<br>
the compiler.<br>
<br>
Disallowing local state usage for classes, too<br>
------------------------------<wbr>----------------<br>
<br>
This PEP originally proposed limiting names within annotations to only<br>
allow names from the model-level scope, including for classes.  The<br>
author argued this makes name resolution unambiguous, including in cases<br>
of conflicts between local names and module-level names.<br>
<br>
This idea was ultimately rejected in case of classes.  Instead,<br>
``typing.get_type_hints()`` got modified to populate the local namespace<br>
correctly if class-level annotations are needed.<br>
<br>
The reasons for rejecting the idea were that it goes against the<br>
intuition of how scoping works in Python, and would break enough<br>
existing type annotations to make the transition cumbersome.  Finally,<br>
local scope access is required for class decorators to be able to<br>
evaluate type annotations. This is because class decorators are applied<br>
before the class receives its name in the outer scope.<br>
<br>
Introducing a new dictionary for the string literal form instead<br>
------------------------------<wbr>------------------------------<wbr>----<br>
<br>
Yury Selivanov shared the following idea:<br>
<br>
1. Add a new special attribute to functions: ``__annotations_text__``.<br>
<br>
2. Make ``__annotations__`` a lazy dynamic mapping, evaluating<br>
   expressions from the corresponding key in ``__annotations_text__``<br>
   just-in-time.<br>
<br>
This idea is supposed to solve the backwards compatibility issue,<br>
removing the need for a new ``__future__`` import.  Sadly, this is not<br>
enough.  Postponed evaluation changes which state the annotation has<br>
access to.  While postponed evaluation fixes the forward reference<br>
problem, it also makes it impossible to access function-level locals<br>
anymore.  This alone is a source of backwards incompatibility which<br>
justifies a deprecation period.<br>
<br>
A ``__future__`` import is an obvious and explicit indicator of opting<br>
in for the new functionality.  It also makes it trivial for external<br>
tools to recognize the difference between a Python files using the old<br>
or the new approach.  In the former case, that tool would recognize that<br>
local state access is allowed, whereas in the latter case it would<br>
recognize that forward references are allowed.<br>
<br>
Finally, just-in-time evaluation in ``__annotations__`` is an<br>
unnecessary step if ``get_type_hints()`` is used later.<br>
<br>
Dropping annotations with -O<br>
----------------------------<br>
<br>
There are two reasons this is not satisfying for the purpose of this<br>
PEP.<br>
<br>
First, this only addresses runtime cost, not forward references, those<br>
still cannot be safely used in source code.  A library maintainer would<br>
never be able to use forward references since that would force the<br>
library users to use this new hypothetical -O switch.<br>
<br>
Second, this throws the baby out with the bath water. Now *no* runtime<br>
annotation use can be performed.  PEP 557 is one example of a recent<br>
development where evaluating type annotations at runtime is useful.<br>
<br>
All that being said, a granular -O option to drop annotations is<br>
a possibility in the future, as it's conceptually compatible with<br>
existing -O behavior (dropping docstrings and assert statements).  This<br>
PEP does not invalidate the idea.<br>
<br>
Pass string literals in annotations verbatim to ``__annotations__``<br>
------------------------------<wbr>------------------------------<wbr>-------<br>
<br>
This PEP originally suggested directly storing the contents of a string<br>
literal under its respective key in ``__annotations__``.  This was<br>
meant to simplify support for runtime type checkers.<br>
<br>
Mark Shannon pointed out this idea was flawed since it wasn't handling<br>
situations where strings are only part of a type annotation.<br>
<br>
The inconsistency of it was always apparent but given that it doesn't<br>
fully prevent cases of double-wrapping strings anyway, it is not worth<br>
it.<br>
<br>
Make the name of the future import more verbose<br>
------------------------------<wbr>-----------------<br>
<br>
Instead of requiring the following import::<br>
<br>
    from __future__ import annotations<br>
<br>
the PEP could call the feature more explicitly, for example<br>
``string_annotations``, ``stringify_annotations``,<br>
``annotation_strings``, ``annotations_as_strings``, ``lazy_anotations``,<br>
``static_annotations``, etc.<br>
<br>
The problem with those names is that they are very verbose.  Each of<br>
them besides ``lazy_annotations`` would constitute the longest future<br>
feature name in Python.  They are long to type and harder to remember<br>
than the single-word form.<br>
<br>
There is precedence of a future import name that sounds overly generic<br>
but in practice was obvious to users as to what it does::<br>
<br>
    from __future__ import division<br>
<br>
<br>
Prior discussion<br>
================<br>
<br>
In PEP 484<br>
----------<br>
<br>
The forward reference problem was discussed when PEP 484 was originally<br>
drafted, leading to the following statement in the document:<br>
<br>
    A compromise is possible where a ``__future__`` import could enable<br>
    turning *all* annotations in a given module into string literals, as<br>
    follows::<br>
<br>
      from __future__ import annotations<br>
<br>
      class ImSet:<br>
          def add(self, a: ImSet) -> List[ImSet]: ...<br>
<br>
      assert ImSet.add.__annotations__ == {<br>
          'a': 'ImSet', 'return': 'List[ImSet]'<br>
      }<br>
<br>
    Such a ``__future__`` import statement may be proposed in a separate<br>
    PEP.<br>
<br>
python/typing#400<br>
-----------------<br>
<br>
The problem was discussed at length on the typing module's GitHub<br>
project, under `Issue 400 <<a href="https://github.com/python/typing/issues/400" rel="noreferrer" target="_blank">https://github.com/python/<wbr>typing/issues/400</a>>`_.<br>
The problem statement there includes critique of generic types requiring<br>
imports from ``typing``.  This tends to be confusing to<br>
beginners:<br>
<br>
    Why this::<br>
<br>
        from typing import List, Set<br>
        def dir(o: object = ...) -> List[str]: ...<br>
        def add_friends(friends: Set[Friend]) -> None: ...<br>
<br>
    But not this::<br>
<br>
        def dir(o: object = ...) -> list[str]: ...<br>
        def add_friends(friends: set[Friend]) -> None ...<br>
<br>
    Why this::<br>
<br>
        up_to_ten = list(range(10))<br>
        friends = set()<br>
<br>
    But not this::<br>
<br>
        from typing import List, Set<br>
        up_to_ten = List[int](range(10))<br>
        friends = Set[Friend]()<br>
<br>
While typing usability is an interesting problem, it is out of scope<br>
of this PEP.  Specifically, any extensions of the typing syntax<br>
standardized in PEP 484 will require their own respective PEPs and<br>
approval.<br>
<br>
Issue 400 ultimately suggests postponing evaluation of annotations and<br>
keeping them as strings in ``__annotations__``, just like this PEP<br>
specifies.  This idea was received well.  Ivan Levkivskyi supported<br>
using the ``__future__`` import and suggested unparsing the AST in<br>
``compile.c``.  Jukka Lehtosalo pointed out that there are some cases<br>
of forward references where types are used outside of annotations and<br>
postponed evaluation will not help those.  For those cases using the<br>
string literal notation would still be required.  Those cases are<br>
discussed briefly in the "Forward References" section of this PEP.<br>
<br>
The biggest controversy on the issue was Guido van Rossum's concern<br>
that untokenizing annotation expressions back to their string form has<br>
no precedent in the Python programming language and feels like a hacky<br>
workaround.  He said:<br>
<br>
    One thing that comes to mind is that it's a very random change to<br>
    the language.  It might be useful to have a more compact way to<br>
    indicate deferred execution of expressions (using less syntax than<br>
    ``lambda:``).  But why would the use case of type annotations be so<br>
    all-important to change the language to do it there first (rather<br>
    than proposing a more general solution), given that there's already<br>
    a solution for this particular use case that requires very minimal<br>
    syntax?<br>
<br>
Eventually, Ethan Smith and schollii voiced that feedback gathered<br>
during PyCon US suggests that the state of forward references needs<br>
fixing.  Guido van Rossum suggested coming back to the ``__future__``<br>
idea, pointing out that to prevent abuse, it's important for the<br>
annotations to be kept both syntactically valid and evaluating correctly<br>
at runtime.<br>
<br>
First draft discussion on python-ideas<br>
------------------------------<wbr>--------<br>
<br>
Discussion happened largely in two threads, `the original announcement<br>
<<a href="https://mail.python.org/pipermail/python-ideas/2017-September/thread.html#47031" rel="noreferrer" target="_blank">https://mail.python.org/<wbr>pipermail/python-ideas/2017-<wbr>September/thread.html#47031</a>>`_<br>
and a follow-up called `PEP 563 and expensive backwards compatibility<br>
<<a href="https://mail.python.org/pipermail/python-ideas/2017-September/thread.html#47108" rel="noreferrer" target="_blank">https://mail.python.org/<wbr>pipermail/python-ideas/2017-<wbr>September/thread.html#47108</a>>`_<wbr>.<br>
<br>
The PEP received rather warm feedback (4 strongly in favor,<br>
2 in favor with concerns, 2 against). The biggest voice of concern on<br>
the former thread being Steven D'Aprano's review stating that the<br>
problem definition of the PEP doesn't justify breaking backwards<br>
compatibility.  In this response Steven seemed mostly concerned about<br>
Python no longer supporting evaluation of annotations that depended on<br>
local function/class state.<br>
<br>
A few people voiced concerns that there are libraries using annotations<br>
for non-typing purposes.  However, none of the named libraries would be<br>
invalidated by this PEP.  They do require adapting to the new<br>
requirement to call ``eval()`` on the annotation with the correct<br>
``globals`` and ``locals`` set.<br>
<br>
This detail about ``globals`` and ``locals`` having to be correct was<br>
picked up by a number of commenters.  Nick Coghlan benchmarked turning<br>
annotations into lambdas instead of strings, sadly this proved to be<br>
much slower at runtime than the current situation.<br>
<br>
The latter thread was started by Jim J. Jewett who stressed that<br>
the ability to properly evaluate annotations is an important requirement<br>
and backwards compatibility in that regard is valuable.  After some<br>
discussion he admitted that side effects in annotations are a code smell<br>
and modal support to either perform or not perform evaluation is<br>
a messy solution.  His biggest concern remained loss of functionality<br>
stemming from the evaluation restrictions on global and local scope.<br>
<br>
Nick Coghlan pointed out that some of those evaluation restrictions from<br>
the PEP could be lifted by a clever implementation of an evaluation<br>
helper, which could solve self-referencing classes even in the form of a<br>
class decorator.  He suggested the PEP should provide this helper<br>
function in the standard library.<br>
<br>
Second draft discussion on python-dev<br>
------------------------------<wbr>-------<br>
<br>
Discussion happened mainly in the `announcement thread <<a href="https://mail.python.org/pipermail/python-dev/2017-November/150062.html" rel="noreferrer" target="_blank">https://mail.python.org/<wbr>pipermail/python-dev/2017-<wbr>November/150062.html</a>>`_,<br>
followed by a brief discussion under `Mark Shannon's post<br>
<<a href="https://mail.python.org/pipermail/python-dev/2017-November/150637.html" rel="noreferrer" target="_blank">https://mail.python.org/<wbr>pipermail/python-dev/2017-<wbr>November/150637.html</a>>`_.<br>
<br>
Steven D'Aprano was concerned whether it's acceptable for typos to be<br>
allowed in annotations after the change proposed by the PEP.  Brett<br>
Cannon responded that type checkers and other static analyzers (like<br>
linters or programming text editors) will catch this type of error.<br>
Jukka Lehtosalo added that this situation is analogous to how names in<br>
function bodies are not resolved until the function is called.<br>
<br>
A major topic of discussion was Nick Coghlan's suggestion to store<br>
annotations in "thunk form", in other words as a specialized lambda<br>
which would be able to access class-level scope (and allow for scope<br>
customization at call time).  He presented a possible design for it<br>
(`indirect attribute cells<br>
<<a href="https://mail.python.org/pipermail/python-dev/2017-November/150141.html" rel="noreferrer" target="_blank">https://mail.python.org/<wbr>pipermail/python-dev/2017-<wbr>November/150141.html</a>>`_).<br>
This was later seen as equivalent to "special forms" in Lisp.  Guido van<br>
Rossum expressed worry that this sort of feature cannot be safely<br>
implemented in twelve weeks (i.e. in time before the Python 3.7 beta<br>
freeze).<br>
<br>
After a while it became clear that the point of division between<br>
supporters of the string form vs. supporters of the thunk form is<br>
actually about whether annotations should be perceived as a general<br>
syntactic element vs. something tied to the type checking use case.<br>
<br>
Finally, Guido van Rossum declared he's rejecting the thunk idea<br>
based on the fact that it would require a new building block in the<br>
interpreter.  This block would be exposed in annotations, multiplying<br>
possible types of values stored in ``__annotations__`` (arbitrary<br>
objects, strings, and now thunks).  Moreover, thunks aren't as<br>
introspectable as strings.  Most importantly, Guido van Rossum<br>
explicitly stated interest in gradually restricting the use of<br>
annotations to static typing (with an optional runtime component).<br>
<br>
Nick Coghlan got convinced to PEP 563, too, promptly beginning<br>
the mandatory bike shedding session on the name of the ``__future__``<br>
import.  Many debaters agreed that ``annotations`` seems like<br>
an overly broad name for the feature name.  Guido van Rossum briefly<br>
decided to call it ``string_annotations`` but then changed his mind,<br>
arguing that ``division`` is a precedent of a broad name with a clear<br>
meaning.<br>
<br>
The final improvement to the PEP suggested in the discussion by Mark<br>
Shannon was the rejection of the temptation to pass string literals<br>
through to ``__annotations__`` verbatim.<br>
<br>
A side-thread of discussion started around the runtime penalty of<br>
static typing, with topic like the import time of the ``typing``<br>
module (which is comparable to ``re`` without dependencies, and<br>
three times as heavy as ``re`` when counting dependencies).<br>
<br>
<br>
Acknowledgements<br>
================<br>
<br>
This document could not be completed without valuable input,<br>
encouragement and advice from Guido van Rossum, Jukka Lehtosalo, and<br>
Ivan Levkivskyi.<br>
<br>
<br>
Copyright<br>
=========<br>
<br>
This document has been placed in the public domain.<br>
<br>
<br>
<br>
..<br>
   Local Variables:<br>
   mode: indented-text<br>
   indent-tabs-mode: nil<br>
   sentence-end-double-space: t<br>
   fill-column: 70<br>
   coding: utf-8<br>
   End:<br>
<br>
<br>______________________________<wbr>_________________<br>
Python-Dev mailing list<br>
<a href="mailto:Python-Dev@python.org">Python-Dev@python.org</a><br>
<a href="https://mail.python.org/mailman/listinfo/python-dev" rel="noreferrer" target="_blank">https://mail.python.org/<wbr>mailman/listinfo/python-dev</a><br>
Unsubscribe: <a href="https://mail.python.org/mailman/options/python-dev/guido%40python.org" rel="noreferrer" target="_blank">https://mail.python.org/<wbr>mailman/options/python-dev/<wbr>guido%40python.org</a><br>
<br></blockquote></div><br><br clear="all"><br>-- <br><div class="gmail_signature" data-smartmail="gmail_signature">--Guido van Rossum (<a href="http://python.org/~guido" target="_blank">python.org/~guido</a>)</div>
</div>