mirror of
https://github.com/python/cpython.git
synced 2025-11-01 06:01:29 +00:00
[3.13] gh-139905: Provide suggestion in error message if Generic.__init_subclass__ was not called (GH-139943) (#139956)
gh-139905: Provide suggestion in error message if `Generic.__init_subclass__` was not called (GH-139943)
(cherry picked from commit 5776d0d2e0)
Co-authored-by: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com>
This commit is contained in:
parent
6a9908ee94
commit
cbb415e992
3 changed files with 45 additions and 2 deletions
|
|
@ -4582,6 +4582,34 @@ class D(Generic[T]): pass
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
D[()]
|
D[()]
|
||||||
|
|
||||||
|
def test_generic_init_subclass_not_called_error(self):
|
||||||
|
notes = ["Note: this exception may have been caused by "
|
||||||
|
r"'GenericTests.test_generic_init_subclass_not_called_error.<locals>.Base.__init_subclass__' "
|
||||||
|
"(or the '__init_subclass__' method on a superclass) not calling 'super().__init_subclass__()'"]
|
||||||
|
|
||||||
|
class Base:
|
||||||
|
def __init_subclass__(cls) -> None:
|
||||||
|
# Oops, I forgot super().__init_subclass__()!
|
||||||
|
pass
|
||||||
|
|
||||||
|
with self.subTest():
|
||||||
|
class Sub(Base, Generic[T]):
|
||||||
|
pass
|
||||||
|
|
||||||
|
with self.assertRaises(AttributeError) as cm:
|
||||||
|
Sub[int]
|
||||||
|
|
||||||
|
self.assertEqual(cm.exception.__notes__, notes)
|
||||||
|
|
||||||
|
with self.subTest():
|
||||||
|
class Sub[U](Base):
|
||||||
|
pass
|
||||||
|
|
||||||
|
with self.assertRaises(AttributeError) as cm:
|
||||||
|
Sub[int]
|
||||||
|
|
||||||
|
self.assertEqual(cm.exception.__notes__, notes)
|
||||||
|
|
||||||
def test_generic_subclass_checks(self):
|
def test_generic_subclass_checks(self):
|
||||||
for typ in [list[int], List[int],
|
for typ in [list[int], List[int],
|
||||||
tuple[int, str], Tuple[int, str],
|
tuple[int, str], Tuple[int, str],
|
||||||
|
|
|
||||||
|
|
@ -1236,14 +1236,26 @@ def _generic_class_getitem(cls, args):
|
||||||
f"Parameters to {cls.__name__}[...] must all be unique")
|
f"Parameters to {cls.__name__}[...] must all be unique")
|
||||||
else:
|
else:
|
||||||
# Subscripting a regular Generic subclass.
|
# Subscripting a regular Generic subclass.
|
||||||
for param in cls.__parameters__:
|
try:
|
||||||
|
parameters = cls.__parameters__
|
||||||
|
except AttributeError as e:
|
||||||
|
init_subclass = getattr(cls, '__init_subclass__', None)
|
||||||
|
if init_subclass not in {None, Generic.__init_subclass__}:
|
||||||
|
e.add_note(
|
||||||
|
f"Note: this exception may have been caused by "
|
||||||
|
f"{init_subclass.__qualname__!r} (or the "
|
||||||
|
f"'__init_subclass__' method on a superclass) not "
|
||||||
|
f"calling 'super().__init_subclass__()'"
|
||||||
|
)
|
||||||
|
raise
|
||||||
|
for param in parameters:
|
||||||
prepare = getattr(param, '__typing_prepare_subst__', None)
|
prepare = getattr(param, '__typing_prepare_subst__', None)
|
||||||
if prepare is not None:
|
if prepare is not None:
|
||||||
args = prepare(cls, args)
|
args = prepare(cls, args)
|
||||||
_check_generic_specialization(cls, args)
|
_check_generic_specialization(cls, args)
|
||||||
|
|
||||||
new_args = []
|
new_args = []
|
||||||
for param, new_arg in zip(cls.__parameters__, args):
|
for param, new_arg in zip(parameters, args):
|
||||||
if isinstance(param, TypeVarTuple):
|
if isinstance(param, TypeVarTuple):
|
||||||
new_args.extend(new_arg)
|
new_args.extend(new_arg)
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
Add suggestion to error message for :class:`typing.Generic` subclasses when
|
||||||
|
``cls.__parameters__`` is missing due to a parent class failing to call
|
||||||
|
:meth:`super().__init_subclass__() <object.__init_subclass__>` in its ``__init_subclass__``.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue