Good point, thanks for bringing that up! Yeah, annotations inside a class definition that refer to the class itself are currently impossible to resolve to a real value in a class decorator, and it’s not even fixable.
For those of you unfamiliar with the problem, this:
@decorate
class Node:
child: Node
is compiled into something akin to this pseudocode:
Node = decorate(__build_class__('Node', ...))
That is, all class decorators are called before the class name is bound.
PEP 649 ameliorates a lot of class definition ordering problems by delaying evaluation of the annotations from class definition time (just before the __build_class__
call) to some arbitrary future time when code asks for the annotations. But if that arbitrary future time is “while running a class decorator”, the name still isn’t bound, so the lookup to populate the annotation fails.
I remember discussions where we discussed trying to fix this. What if Python instead bound the class name before calling the decorator(s)? This wouldn’t work, because class decorators can return a different class, or even a different object altogether. And this isn’t exactly a rare technique. For example, dataclass
does this when the class it’s decorating uses slots.
The closest you could get would be to bind the class name before the first decorator, then rebind it after every decorator. But this could still get you into trouble. If you decorate a class with two decorators, and the first class decorator examines the annotations, and the second class decorator returns a different class, the first class decorator may have kept a reference to the old class object. This is a danger today too, but it seems to me that binding the class name would invite more inobvious corner case bugs.
(Perhaps we’d also need to forget any calculated annotations after every class decorator call, to drop any possible references to the old class, just in case. But this still wouldn’t solve every corner case.)
The good news is, we’ve solved the problem in a different way, with PEP 673, the “Self” type hint. Instead of referring to the bound name of the class inside of the class definition, best practice will be to use typing.Self
. So my take is, PEP 649 doesn’t have to do anything to address this situation, as typing.Self
will work perfectly with 649.