-
-
Notifications
You must be signed in to change notification settings - Fork 34.5k
__class_getitem__ Unexpectedly Falls Back to the Metaclass #122634
Copy link
Copy link
Open
Labels
3.10only security fixesonly security fixes3.11only security fixesonly security fixes3.12only security fixesonly security fixes3.13bugs and security fixesbugs and security fixes3.14bugs and security fixesbugs and security fixes3.7 (EOL)end of lifeend of life3.8 (EOL)end of lifeend of life3.9 (EOL)end of lifeend of lifeinterpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)topic-typingtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
Metadata
Metadata
Assignees
Labels
3.10only security fixesonly security fixes3.11only security fixesonly security fixes3.12only security fixesonly security fixes3.13bugs and security fixesbugs and security fixes3.14bugs and security fixesbugs and security fixes3.7 (EOL)end of lifeend of life3.8 (EOL)end of lifeend of life3.9 (EOL)end of lifeend of lifeinterpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)topic-typingtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
Bug report
Bug description:
A major point of
__class_getitem__(PEP 560) is to avoid metaclasses. This implies that the metaclass should never be involved in the mechanism. However, currently (and since the feature landed) we actually do fall back to the metaclass:(example)
Output:
I was expecting the last one to raise
TypeError: type 'object' is not subscriptable, sinceHamdoes not implement__class_getitem__. The actual outcome is surprising because the metaclass can already define__getitem__. Falling back to the metaclass__class_getitem__doesn't make much sense and can be confusing. The metaclass__class_getitem__should only be used when subscripting the metaclass, not its instances.The PEP doesn't really address the question of a metaclass that defines
__class_getiem__1 (nor does the documentation as far as I noticed). Overall, it seems like this was simply not noticed nor considered. It's certainly not an obvious case. Regardless, I think we should fix it.The fix would involve skipping the metaclass part. That would be in the implementation for the subscript syntax (
PyObject_GetItem()in Objects/abstract.c). There's a part where it specially handles the case where a class is being subscripted. In that case it looks up__class_getitem__on the class. (See gh-4732.) However, currently it usesPyObject_GetOptionalAttr(), which involves descriptors and the metaclass (the object's type) and the type's__mro__. Again, the fix is to skip the metaclass part.FWIW,
__init_subclass__is fairly similar, but it doesn't fall back to the metaclass. Instead, it effectively doesgetattr(super(cls), '__init_subclass__'). (Seetype_new_init_subclass()in Objects/typeobject.c.) We should probably do something similar inPyObject_GetItem().CC @ilevkivskyi @gvanrossum
CPython versions tested on:
CPython main branch
Operating systems tested on:
No response
Linked PRs
__class_getitem__on metaclasses #122743Footnotes
The PEP does say
Note that this method is used as a fallback, so if a metaclass defines __getitem__, then that will have the priority.but that's specifically about falling back tometa.__getitem__. ↩