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