]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.13] gh-139905: Provide suggestion in error message if `Generic.__init_subclass__...
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Sat, 11 Oct 2025 15:36:44 +0000 (17:36 +0200)
committerGitHub <noreply@github.com>
Sat, 11 Oct 2025 15:36:44 +0000 (15:36 +0000)
gh-139905: Provide suggestion in error message if `Generic.__init_subclass__` was not called (GH-139943)
(cherry picked from commit 5776d0d2e08f4d93dcd62d875bae8c1396a04ba4)

Co-authored-by: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com>
Lib/test/test_typing.py
Lib/typing.py
Misc/NEWS.d/next/Library/2025-10-11-10-02-56.gh-issue-139905.UyJIR_.rst [new file with mode: 0644]

index 5f49bf757096f069f058e53d06b754eb0531728b..fb4cf26982d3b58ef2b6e7ab06b3672215f5c8ad 100644 (file)
@@ -4582,6 +4582,34 @@ class GenericTests(BaseTestCase):
         with self.assertRaises(TypeError):
             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):
         for typ in [list[int], List[int],
                     tuple[int, str], Tuple[int, str],
index f9141640997933442adfd52ee1fb4e657bddb50c..95e469c4fff74aaa6601687621b53d15e0d0be72 100644 (file)
@@ -1236,14 +1236,26 @@ def _generic_class_getitem(cls, args):
                 f"Parameters to {cls.__name__}[...] must all be unique")
     else:
         # 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)
             if prepare is not None:
                 args = prepare(cls, args)
         _check_generic_specialization(cls, args)
 
         new_args = []
-        for param, new_arg in zip(cls.__parameters__, args):
+        for param, new_arg in zip(parameters, args):
             if isinstance(param, TypeVarTuple):
                 new_args.extend(new_arg)
             else:
diff --git a/Misc/NEWS.d/next/Library/2025-10-11-10-02-56.gh-issue-139905.UyJIR_.rst b/Misc/NEWS.d/next/Library/2025-10-11-10-02-56.gh-issue-139905.UyJIR_.rst
new file mode 100644 (file)
index 0000000..a6876ca
--- /dev/null
@@ -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__``.