summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/functions.rst7
-rw-r--r--Lib/test/test_super.py27
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2023-12-20-08-54-54.gh-issue-113212.62AUlw.rst1
-rw-r--r--Objects/typeobject.c19
4 files changed, 51 insertions, 3 deletions
diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst
index c731b6f..4682ec9 100644
--- a/Doc/library/functions.rst
+++ b/Doc/library/functions.rst
@@ -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
diff --git a/Lib/test/test_super.py b/Lib/test/test_super.py
index 43162c5..f8e968b 100644
--- a/Lib/test/test_super.py
+++ b/Lib/test/test_super.py
@@ -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
index 0000000..6edbc9c
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2023-12-20-08-54-54.gh-issue-113212.62AUlw.rst
@@ -0,0 +1 @@
+Improve :py:class:`super` error messages.
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 5261ef9..ea29a38 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -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;
}