]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-113212: Improve error message & document zero-arg super inside nested functions...
authorYan Yanchii <yyanchiy@gmail.com>
Fri, 22 Dec 2023 15:12:08 +0000 (16:12 +0100)
committerGitHub <noreply@github.com>
Fri, 22 Dec 2023 15:12:08 +0000 (17:12 +0200)
Doc/library/functions.rst
Lib/test/test_super.py
Misc/NEWS.d/next/Core and Builtins/2023-12-20-08-54-54.gh-issue-113212.62AUlw.rst [new file with mode: 0644]
Objects/typeobject.c

index c731b6fd3332753635468b71f6593684f8898c00..4682ec9c9247578e3dcc1828fca1cab516214b44 100644 (file)
@@ -1800,6 +1800,13 @@ are always available.  They are listed here in alphabetical order.
    the second argument is a type, ``issubclass(type2, type)`` must be true (this
    is useful for classmethods).
 
+   When called directly within an ordinary method of a class, both arguments may
+   be omitted ("zero-argument :func:`!super`"). In this case, *type* will be the
+   enclosing class, and *obj* will be the first argument of the immediately
+   enclosing function (typically ``self``). (This means that zero-argument
+   :func:`!super` will not work as expected within nested functions, including
+   generator expressions, which implicitly create nested functions.)
+
    There are two typical use cases for *super*.  In a class hierarchy with
    single inheritance, *super* can be used to refer to parent classes without
    naming them explicitly, thus making the code more maintainable.  This use
index 43162c540b55aef8870cf8aa1b4929ea9e9045c3..f8e968b9b56f828d3593afd110532d23150964aa 100644 (file)
@@ -396,6 +396,33 @@ class TestSuper(unittest.TestCase):
         with self.assertRaisesRegex(TypeError, "argument 1 must be a type"):
             C().method()
 
+    def test_supercheck_fail(self):
+        class C:
+            def method(self, type_, obj):
+                return super(type_, obj).method()
+
+        c = C()
+        err_msg = (
+            r"super\(type, obj\): obj \({} {}\) is not "
+            r"an instance or subtype of type \({}\)."
+        )
+
+        cases = (
+            (int, c, int.__name__, C.__name__, "instance of"),
+            # obj is instance of type
+            (C, list(), C.__name__, list.__name__, "instance of"),
+            # obj is type itself
+            (C, list, C.__name__, list.__name__, "type"),
+        )
+
+        for case in cases:
+            with self.subTest(case=case):
+                type_, obj, type_str, obj_str, instance_or_type = case
+                regex = err_msg.format(instance_or_type, obj_str, type_str)
+
+                with self.assertRaisesRegex(TypeError, regex):
+                    c.method(type_, obj)
+
     def test_super___class__(self):
         class C:
             def method(self):
diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-12-20-08-54-54.gh-issue-113212.62AUlw.rst b/Misc/NEWS.d/next/Core and Builtins/2023-12-20-08-54-54.gh-issue-113212.62AUlw.rst
new file mode 100644 (file)
index 0000000..6edbc9c
--- /dev/null
@@ -0,0 +1 @@
+Improve :py:class:`super` error messages.
index 5261ef924136153a3ccd9ebb1d458f04ee32e366..ea29a38d74ae3ed3c475d07efec778ddc1c68462 100644 (file)
@@ -10404,9 +10404,22 @@ supercheck(PyTypeObject *type, PyObject *obj)
         Py_XDECREF(class_attr);
     }
 
-    PyErr_SetString(PyExc_TypeError,
-                    "super(type, obj): "
-                    "obj must be an instance or subtype of type");
+    const char *type_or_instance, *obj_str;
+
+    if (PyType_Check(obj)) {
+        type_or_instance = "type";
+        obj_str = ((PyTypeObject*)obj)->tp_name;
+    }
+    else {
+        type_or_instance = "instance of";
+        obj_str = Py_TYPE(obj)->tp_name;
+    }
+
+    PyErr_Format(PyExc_TypeError,
+                "super(type, obj): obj (%s %.200s) is not "
+                "an instance or subtype of type (%.200s).",
+                type_or_instance, obj_str, type->tp_name);
+
     return NULL;
 }