PEP 649: Deferred evaluation of annotations, tentatively accepted

I now have a working implementation of much of the C part of PEP 649 here: gh-119180: PEP 649 compiler changes by JelleZijlstra · Pull Request #119361 · python/cpython · GitHub. There are many issues still to work out and I haven’t run it on any third-party code yet.

However, CPython’s own test suite found a lot of interesting issues already. Here are some of my findings:

  • Contrary to my earlier post, I don’t think we can apply PEP 649 semantics if from __future__ import annotations is active. This would break too much working code, in cases where a library inspects the annotations eagerly (e.g., in a decorator) and the user is relying on the future import to use forward references. Therefore, I kept the from __future__ import annotations behavior in place and applied PEP 649 semantics only in cases where the future import is off.
  • Code that does cls.__dict__.get("__annotations__") to get to a class’s annotations dict will break. There is no longer going to be an annotations key in the class dict unless the user has already accessed the cls.__annotations__ descriptor. This affected several parts of typing.py. It is fixable by using cls.__annotations__ directly.
  • Code that interacts directly with the __annotations__ name in the local namespace stops working. I don’t know why people would do this, but there are test cases in CPython that rely on it (e.g., the ones removed in this commit: gh-119180: PEP 649 compiler changes by JelleZijlstra · Pull Request #119361 · python/cpython · GitHub).

Both of these cases are also called out in the PEP: PEP 649 – Deferred Evaluation Of Annotations Using Descriptors | peps.python.org. The PEP was accepted with the understanding that some unusual code patterns would break; we’ll have to see how problematic this is in practice.

8 Likes