larryhastings / co_annotations

Other
9 stars 5 forks source link

Clarify the future of `from __future__ import annotations` #19

Closed ncoghlan closed 3 months ago

ncoghlan commented 1 year ago

As mentioned in a couple of other tickets, I was recently re-reading https://lukasz.langa.pl/61df599c-d9d8-4938-868b-36b67fdb4448/ (prompted by the release of Python 3.11) and considering the concerns it raises with PEP 649.

The circular reference and if TYPE_CHECKING: questions are already discussed in #1, #2, and #17, so I won't repeat them here.

This ticket is instead about:

  1. Backporting type hinting syntax from newer versions of Python to older versions
  2. Backwards compatibility for code that currently uses from __future__ import annotations

For the first, I think "Explicitly quote annotations that use type hinting syntax that only becomes valid at runtime in later Python versions" is sufficient to address the point. Even string annotations would need to rely on explicit quoting to backport type hinting syntax that only becomes valid at compile time in future versions, and code object annotations would be starting from the much richer Python 3.12 type hinting syntax baseline.

For the second, PEP 649 doesn't currently spell out the expected fate of from __future__ import annotations. Łukasz's article assumes it would be deprecated, emit deprecation warnings, and eventually stop working outright, but I'm not sure that's the right thing for it to do (due to the cross-version compatibility concerns that Łukasz raises in his post).

Instead, the following seems like a cleaner way forward:

  1. While PEP 649 still requires its own __future__ import, from __future__ import annotations continues to function as it does in 3.9, without any deprecation warnings (just as regular annotations wouldn't have any deprecation warnings)
  2. When PEP 649 becomes the default behaviour for annotations, it also becomes the behaviour for code using from __future__ import annotations (so the semantics of the latter import still change from what they were previously, just as they would for code with no __future__ import, but there's no hard error on the __future__ import itself)

That way, just as concerns with migrating from eager annotations to lazy annotations can be resolved during the __future__ import period, so can concerns with migrating from string annotations to code object based annotations, with the goal being that the eventual semantic change doesn't cause any significant problems in either case.

This idea assumes that the practical issues with migrating from string annotations to code object based annotations are resolved, but those are presumably going to have to be resolved anyway in order for PEP 649 to be accepted by the SC.

ncoghlan commented 1 year ago

As per @zzzeek's comment at https://github.com/larryhastings/co_annotations/issues/17#issuecomment-1295863493 , pursuing this "future annotations become equivalent" approach would require that code object annotations keep the "No default eval" property of string annotations.

If that property is preserved, libraries updated to deal with string annotations wouldn't need to switch to new APIs to get the raw annotations.

The downside is that this would make the transition from regular annotations more disruptive.

ncoghlan commented 3 months ago

The accepted version of PEP 649 covers this.