]> granicus.if.org Git - python/commitdiff
bpo-31506: Improve the error message logic for class instantiation (GH-4740)
authorSanyam Khurana <8039608+CuriousLearner@users.noreply.github.com>
Sun, 10 Dec 2017 00:14:22 +0000 (05:44 +0530)
committerNick Coghlan <ncoghlan@gmail.com>
Sun, 10 Dec 2017 00:14:22 +0000 (10:14 +1000)
The error messages in `object.__new__` and `object.__init__` now aim
to point the user more directly at the name of the class being instantiated
in cases where they *haven't* been overridden (on the assumption that
the actual problem is a missing `__new__` or `__init__` definition in the
class body).

When they *have* been overridden, the errors still report themselves as
coming from object, on the assumption that the problem is with the call
up to the base class in the method implementation, rather than with the
way the constructor is being called.

Lib/test/test_class.py
Misc/NEWS.d/next/Core and Builtins/2017-12-07-23-44-29.bpo-31506.j1U2fU.rst [new file with mode: 0644]
Objects/typeobject.c

index ecc01f277954d53a871d8ff7126191080f92d1f8..a916e878b7a7db7fb292a8a4c1e83d1d9f0a0772 100644 (file)
@@ -595,5 +595,54 @@ class ClassTests(unittest.TestCase):
         with self.assertRaises(TypeError):
             type.__setattr__(A, b'x', None)
 
+    def testConstructorErrorMessages(self):
+        # bpo-31506: Improves the error message logic for object_new & object_init
+
+        # Class without any method overrides
+        class C:
+            pass
+
+        with self.assertRaisesRegex(TypeError, r'C\(\) takes no arguments'):
+            C(42)
+
+        with self.assertRaisesRegex(TypeError, r'C\(\) takes no arguments'):
+            C.__new__(C, 42)
+
+        with self.assertRaisesRegex(TypeError, r'C\(\).__init__\(\) takes no arguments'):
+            C().__init__(42)
+
+        with self.assertRaisesRegex(TypeError, r'C\(\) takes no arguments'):
+            object.__new__(C, 42)
+
+        with self.assertRaisesRegex(TypeError, r'C\(\).__init__\(\) takes no arguments'):
+            object.__init__(C(), 42)
+
+        # Class with both `__init__` & `__new__` method overriden
+        class D:
+            def __new__(cls, *args, **kwargs):
+                super().__new__(cls, *args, **kwargs)
+            def __init__(self, *args, **kwargs):
+                super().__init__(*args, **kwargs)
+
+        with self.assertRaisesRegex(TypeError, r'object.__new__\(\) takes no argument'):
+            D(42)
+
+        with self.assertRaisesRegex(TypeError, r'object.__new__\(\) takes no argument'):
+            D.__new__(D, 42)
+
+        with self.assertRaisesRegex(TypeError, r'object.__new__\(\) takes no argument'):
+            object.__new__(D, 42)
+
+        # Class that only overrides __init__
+        class E:
+            def __init__(self, *args, **kwargs):
+                super().__init__(*args, **kwargs)
+
+        with self.assertRaisesRegex(TypeError, r'object.__init__\(\) takes no argument'):
+            E().__init__(42)
+
+        with self.assertRaisesRegex(TypeError, r'object.__init__\(\) takes no argument'):
+            object.__init__(E(), 42)
+
 if __name__ == '__main__':
     unittest.main()
diff --git a/Misc/NEWS.d/next/Core and Builtins/2017-12-07-23-44-29.bpo-31506.j1U2fU.rst b/Misc/NEWS.d/next/Core and Builtins/2017-12-07-23-44-29.bpo-31506.j1U2fU.rst
new file mode 100644 (file)
index 0000000..ceb9ee2
--- /dev/null
@@ -0,0 +1,2 @@
+Improve the error message logic for object.__new__ and object.__init__.
+Patch by Sanyam Khurana.
index 2a8118b43c5a0a7a1d11cb8cb5f0216018f80158..73f94e76c90d43d694fca3ead3c45663d1aeae98 100644 (file)
@@ -3592,11 +3592,11 @@ object_init(PyObject *self, PyObject *args, PyObject *kwds)
     PyTypeObject *type = Py_TYPE(self);
     if (excess_args(args, kwds)) {
         if (type->tp_init != object_init) {
-            PyErr_SetString(PyExc_TypeError, "object() takes no arguments");
+            PyErr_SetString(PyExc_TypeError, "object.__init__() takes no arguments");
             return -1;
         }
         if (type->tp_new == object_new) {
-            PyErr_Format(PyExc_TypeError, "%.200s() takes no arguments",
+            PyErr_Format(PyExc_TypeError, "%.200s().__init__() takes no arguments",
                          type->tp_name);
             return -1;
         }
@@ -3609,7 +3609,7 @@ object_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 {
     if (excess_args(args, kwds)) {
         if (type->tp_new != object_new) {
-            PyErr_SetString(PyExc_TypeError, "object() takes no arguments");
+            PyErr_SetString(PyExc_TypeError, "object.__new__() takes no arguments");
             return NULL;
         }
         if (type->tp_init == object_init) {