Skip to content

Commit 5776d0d

Browse files
pythongh-139905: Provide suggestion in error message if Generic.__init_subclass__ was not called (python#139943)
1 parent 2eb32ad commit 5776d0d

File tree

3 files changed

+45
-2
lines changed

3 files changed

+45
-2
lines changed

Lib/test/test_typing.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4722,6 +4722,34 @@ class D(Generic[T]): pass
47224722
with self.assertRaises(TypeError):
47234723
D[()]
47244724

4725+
def test_generic_init_subclass_not_called_error(self):
4726+
notes = ["Note: this exception may have been caused by "
4727+
r"'GenericTests.test_generic_init_subclass_not_called_error.<locals>.Base.__init_subclass__' "
4728+
"(or the '__init_subclass__' method on a superclass) not calling 'super().__init_subclass__()'"]
4729+
4730+
class Base:
4731+
def __init_subclass__(cls) -> None:
4732+
# Oops, I forgot super().__init_subclass__()!
4733+
pass
4734+
4735+
with self.subTest():
4736+
class Sub(Base, Generic[T]):
4737+
pass
4738+
4739+
with self.assertRaises(AttributeError) as cm:
4740+
Sub[int]
4741+
4742+
self.assertEqual(cm.exception.__notes__, notes)
4743+
4744+
with self.subTest():
4745+
class Sub[U](Base):
4746+
pass
4747+
4748+
with self.assertRaises(AttributeError) as cm:
4749+
Sub[int]
4750+
4751+
self.assertEqual(cm.exception.__notes__, notes)
4752+
47254753
def test_generic_subclass_checks(self):
47264754
for typ in [list[int], List[int],
47274755
tuple[int, str], Tuple[int, str],

Lib/typing.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1158,14 +1158,26 @@ def _generic_class_getitem(cls, args):
11581158
f"Parameters to {cls.__name__}[...] must all be unique")
11591159
else:
11601160
# Subscripting a regular Generic subclass.
1161-
for param in cls.__parameters__:
1161+
try:
1162+
parameters = cls.__parameters__
1163+
except AttributeError as e:
1164+
init_subclass = getattr(cls, '__init_subclass__', None)
1165+
if init_subclass not in {None, Generic.__init_subclass__}:
1166+
e.add_note(
1167+
f"Note: this exception may have been caused by "
1168+
f"{init_subclass.__qualname__!r} (or the "
1169+
f"'__init_subclass__' method on a superclass) not "
1170+
f"calling 'super().__init_subclass__()'"
1171+
)
1172+
raise
1173+
for param in parameters:
11621174
prepare = getattr(param, '__typing_prepare_subst__', None)
11631175
if prepare is not None:
11641176
args = prepare(cls, args)
11651177
_check_generic_specialization(cls, args)
11661178

11671179
new_args = []
1168-
for param, new_arg in zip(cls.__parameters__, args):
1180+
for param, new_arg in zip(parameters, args):
11691181
if isinstance(param, TypeVarTuple):
11701182
new_args.extend(new_arg)
11711183
else:
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Add suggestion to error message for :class:`typing.Generic` subclasses when
2+
``cls.__parameters__`` is missing due to a parent class failing to call
3+
:meth:`super().__init_subclass__() <object.__init_subclass__>` in its ``__init_subclass__``.

0 commit comments

Comments
 (0)