Skip to content

Commit 46148d1

Browse files
[3.14] pythongh-139905: Provide suggestion in error message if Generic.__init_subclass__ was not called (pythonGH-139943) (python#139955)
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 1a355d7 commit 46148d1

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
@@ -4674,6 +4674,34 @@ class D(Generic[T]): pass
46744674
with self.assertRaises(TypeError):
46754675
D[()]
46764676

4677+
def test_generic_init_subclass_not_called_error(self):
4678+
notes = ["Note: this exception may have been caused by "
4679+
r"'GenericTests.test_generic_init_subclass_not_called_error.<locals>.Base.__init_subclass__' "
4680+
"(or the '__init_subclass__' method on a superclass) not calling 'super().__init_subclass__()'"]
4681+
4682+
class Base:
4683+
def __init_subclass__(cls) -> None:
4684+
# Oops, I forgot super().__init_subclass__()!
4685+
pass
4686+
4687+
with self.subTest():
4688+
class Sub(Base, Generic[T]):
4689+
pass
4690+
4691+
with self.assertRaises(AttributeError) as cm:
4692+
Sub[int]
4693+
4694+
self.assertEqual(cm.exception.__notes__, notes)
4695+
4696+
with self.subTest():
4697+
class Sub[U](Base):
4698+
pass
4699+
4700+
with self.assertRaises(AttributeError) as cm:
4701+
Sub[int]
4702+
4703+
self.assertEqual(cm.exception.__notes__, notes)
4704+
46774705
def test_generic_subclass_checks(self):
46784706
for typ in [list[int], List[int],
46794707
tuple[int, str], Tuple[int, str],

Lib/typing.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1142,14 +1142,26 @@ def _generic_class_getitem(cls, args):
11421142
f"Parameters to {cls.__name__}[...] must all be unique")
11431143
else:
11441144
# Subscripting a regular Generic subclass.
1145-
for param in cls.__parameters__:
1145+
try:
1146+
parameters = cls.__parameters__
1147+
except AttributeError as e:
1148+
init_subclass = getattr(cls, '__init_subclass__', None)
1149+
if init_subclass not in {None, Generic.__init_subclass__}:
1150+
e.add_note(
1151+
f"Note: this exception may have been caused by "
1152+
f"{init_subclass.__qualname__!r} (or the "
1153+
f"'__init_subclass__' method on a superclass) not "
1154+
f"calling 'super().__init_subclass__()'"
1155+
)
1156+
raise
1157+
for param in parameters:
11461158
prepare = getattr(param, '__typing_prepare_subst__', None)
11471159
if prepare is not None:
11481160
args = prepare(cls, args)
11491161
_check_generic_specialization(cls, args)
11501162

11511163
new_args = []
1152-
for param, new_arg in zip(cls.__parameters__, args):
1164+
for param, new_arg in zip(parameters, args):
11531165
if isinstance(param, TypeVarTuple):
11541166
new_args.extend(new_arg)
11551167
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)