
On Mon, 2021-01-11 at 13:16 -0800, Larry Hastings wrote:
Thanks for your feedback! I'll reply piecemeal.
On 1/11/21 12:32 PM, Paul Bryan wrote:
1. Backwards Compatibility
PEP 563 changed the semantics of annotations. When its semantics are active, annotations must assume they will be evaluated in module-level scope. They may no longer refer directly to local variables or class attributes.
Given get_type_hints can be provided localns argument, this statement is not exactly true. PEP 563 states:
For code that uses type hints, the typing.get_type_hints(obj, globalns=None, localns=None) function correctly evaluates expressions back from its string form. So, if you are passing in a localns argument that isn't None, okay, but you're not using them "correctly" according to the language. Also, this usage won't be compatible with static type checkers.
I acknowledge that this will not fly with static type checkers. I also want to make sure that annotations can continue to serve runtime type validation. PEP 563 does go on to state:
For code which uses annotations for other purposes, a regulareval(ann, globals, locals) call is enough to resolve the annotation.
And I believe this would no longer be true under PEP 649; further, localns (and maybe globalns) parameters in get_type_hints would become meaningless. This passage in PEP 563 appears not true in Python 3.9 with __future__ annotations, emphasis mine:
The get_type_hints() function automatically resolves the correct value of globalns for functions and classes. It also automatically provides the correct localns for classes.
If this passage was true, I believe the issue that resulted in my affixing type hints could have been averted.
Under PEP 649, when __co_annotations__ is called (presumably by calling get_type_hints), would localns effectively be ignored? Yes. You can experiment with this in Python 3.9--just turn off annotation stringizing. It seems that you can still use strings as annotations and typing.get_type_hints() will evaluate them--and I assume it'll use localns at that point, just as it does today. OK, would string representations of type hints continue be supported under PEP 649 if strings are used as annotations? And, would get_type_hints continue evaluate the annotations in that case?
2. __co_annotations__ scope?
I'm wondering why __co_annotations__ function could not be scoped (within a closure?) such that it can access the values where the function, method, class or module are being declared? I acknowledge that I'm railing against PEP 563 again, trying to reclaim lost ground. This is addressed in PEP 563, when it rejected the idea of using "function local state when defining annotations":
I wasn't thinking the function local state of that being annotated (agree, this would be prohibitive), but rather the scope in which the annotated function, class, module, etc. are being defined.
This would be prohibitively expensive for highly annotated code as the frames would keep all their objects alive. That includes predominantly objects that won't ever be accessed again.
https://www.python.org/dev/peps/pep-0563/#keeping-the-ability-to-use-functio... Doing this automatically would indeed incur a sizeable runtime cost, for a feature that is already rarely used at runtime. I guess it would be remotely possible? to add this as an optional feature? But this gets crazy quickly--what if it's defined inside a function inside another function inside a class inside another function?--and the use cases seem few, and TOOWTDI.
I think this exactly the case for closures today.
I've never understood how closures work in Python, so I'm not the guy to ask how possible / hard this would be. Then again, the implementation of closures is obscure enough that I've never been able to understand them, so that seems to establish at least a base level of difficulty. Anyway, one of the concepts my PEP is built on is that "annotations are always evaluated at module-level scope". I'd be against changing that unless it could be achieved without runtime cost--which AFAIK is impossible.
Cheers,
/arry