Skip to content

Commit cbb415e

Browse files
[3.13] pythongh-139905: Provide suggestion in error message if Generic.__init_subclass__ was not called (pythonGH-139943) (python#139956)
pythongh-139905: Provide suggestion in error message if `Generic.__init_subclass__` was not called (pythonGH-139943) (cherry picked from commit 5776d0d) Co-authored-by: Stan Ulbrych <[email protected]>
1 parent 6a9908e commit cbb415e

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
@@ -4582,6 +4582,34 @@ class D(Generic[T]): pass
45824582
with self.assertRaises(TypeError):
45834583
D[()]
45844584

4585+
def test_generic_init_subclass_not_called_error(self):
4586+
notes = ["Note: this exception may have been caused by "
4587+
r"'GenericTests.test_generic_init_subclass_not_called_error.<locals>.Base.__init_subclass__' "
4588+
"(or the '__init_subclass__' method on a superclass) not calling 'super().__init_subclass__()'"]
4589+
4590+
class Base:
4591+
def __init_subclass__(cls) -> None:
4592+
# Oops, I forgot super().__init_subclass__()!
4593+
pass
4594+
4595+
with self.subTest():
4596+
class Sub(Base, Generic[T]):
4597+
pass
4598+
4599+
with self.assertRaises(AttributeError) as cm:
4600+
Sub[int]
4601+
4602+
self.assertEqual(cm.exception.__notes__, notes)
4603+
4604+
with self.subTest():
4605+
class Sub[U](Base):
4606+
pass
4607+
4608+
with self.assertRaises(AttributeError) as cm:
4609+
Sub[int]
4610+
4611+
self.assertEqual(cm.exception.__notes__, notes)
4612+
45854613
def test_generic_subclass_checks(self):
45864614
for typ in [list[int], List[int],
45874615
tuple[int, str], Tuple[int, str],

Lib/typing.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1236,14 +1236,26 @@ def _generic_class_getitem(cls, args):
12361236
f"Parameters to {cls.__name__}[...] must all be unique")
12371237
else:
12381238
# Subscripting a regular Generic subclass.
1239-
for param in cls.__parameters__:
1239+
try:
1240+
parameters = cls.__parameters__
1241+
except AttributeError as e:
1242+
init_subclass = getattr(cls, '__init_subclass__', None)
1243+
if init_subclass not in {None, Generic.__init_subclass__}:
1244+
e.add_note(
1245+
f"Note: this exception may have been caused by "
1246+
f"{init_subclass.__qualname__!r} (or the "
1247+
f"'__init_subclass__' method on a superclass) not "
1248+
f"calling 'super().__init_subclass__()'"
1249+
)
1250+
raise
1251+
for param in parameters:
12401252
prepare = getattr(param, '__typing_prepare_subst__', None)
12411253
if prepare is not None:
12421254
args = prepare(cls, args)
12431255
_check_generic_specialization(cls, args)
12441256

12451257
new_args = []
1246-
for param, new_arg in zip(cls.__parameters__, args):
1258+
for param, new_arg in zip(parameters, args):
12471259
if isinstance(param, TypeVarTuple):
12481260
new_args.extend(new_arg)
12491261
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)