Hi Larry, all, I was thinking also of a compromise but a slightly different approach:

Store annotations as a subclass of string but with the required frames attached to evaluate them as though they were in their local context. Then have a function "get_annotation_values" that knows how to evaluate these string subclasses with the attached frames.

This would allow those who use runtime annotations to access local scope like PEP 649, and allow those who use static type checking to relax the syntax (as long as they don't try and evaluate the syntax at runtime) as per PEP 563.

E.g this would work:

def f():
    class X: pass
    def inner() -> X: pass
    return innfer

f().__annotations__ # Similar to PEP 563, values of dict look like strings
get_annotation_values(f()) # Gets the same values as you would from accessing __annotations__ in PEP 649

Please ignore this idea if it doesn't make any sense, I'm working on limited technical knowledge of CPython.


On Sun, Apr 18, 2021 at 10:09 AM Jelle Zijlstra <jelle.zijlstra@gmail.com> wrote:


El sáb, 17 abr 2021 a las 20:45, Larry Hastings (<larry@hastings.org>) escribió:


The heart of the debate between PEPs 563 and 649 is the question: what should an annotation be?  Should it be a string or a Python value?  It seems people who are pro-PEP 563 want it to be a string, and people who are pro-PEP 649 want it to be a value.

Actually, let me amend that slightly.  Most people who are pro-PEP 563 don't actually care that annotations are strings, per se.  What they want are specific runtime behaviors, and they get those behaviors when PEP 563 turns their annotations into strings.

I have an idea--a rough proposal--on how we can mix together aspects of PEP 563 and PEP 649.  I think it satisfies everyone's use cases for both PEPs.  The behavior it gets us:

  • annotations can be stored as strings
  • annotations stored as strings can be examined as strings
  • annotations can be examined as values


The idea:

We add a new type of compile-time flag, akin to a "from __future__" import, but not from the future.  Let's not call it "from __present__", for now how about "from __behavior__".

In this specific case, we call it "from __behavior__ import str_annotations".  It behaves much like Python 3.9 does when you say "from __future__ import annotations", except: it stores the dictionary with stringized values in a new member on the function/class/module called "__str_annotations__".

If an object "o" has "__str_annotations__", set, you can access it and see the stringized values.

If you access "o.__annotations__", and the object has "o.__str_annotations__" set but "o.__annotations__" is not set, it builds (and caches) a new dict by iterating over o.__str_annotations__, calling eval() on each value in "o.__str_annotations__".  It gets the globals() dict the same way that PEP 649 does (including, if you compile a class with str_annotations, it sets __globals__ on the class).  It does not unset "o.__str_annotations__" unless someone explicitly sets "o.__annotations__".  This is so you can write your code assuming that "o.__str_annotations__" is set, and it doesn't explode if somebody somewhere ever looks at "o.__annotations__".  (This could lead to them getting out of sync, if someone modified "o.__annotations__".  But I suspect practicality beats purity here.)

How would this work with annotations that access a local scope?

def f():
    class X: pass
    def inner() -> X: pass
    return innfer

f().__annotations__

From your description it sounds like it would fail, just like calling typing.get_type_hints() would fail on it today.

If so I don't see this as much better than the current situation: all it does is provide a builtin way of calling get_type_hints().
_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-leave@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/SZA3BUYWYVYS45PZT5NS4TOEZH6SLZIP/
Code of Conduct: http://python.org/psf/codeofconduct/