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:
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.)
This means:
Also, yes, of course we can keep the optimization where
stringized annotations are stored as a tuple containing an even
number of strings. Similarly to PEP 649's automatic binding of an
unbound code object, if you set "o.__str_annotations__" to a tuple
containing an even number of strings, and you then access
"o.__str_annotations__", you get back a dict.
TBD: how this interacts with PEP 649. I don't know if it means
we only do this, or if it would be a good idea to do both this and
649. I just haven't thought about it. (It would be a runtime
error to set both "o.__str_annotations__" and
"o.__co_annotations__", though.)
Well, whaddya think? Any good?
I considered calling this "PEP 1212", which is 563 + 649,
/arry