diff options
author | Inada Naoki <songofacandy@gmail.com> | 2019-04-01 08:56:11 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-04-01 08:56:11 (GMT) |
commit | 62f9588663ebfea1735e9d142ef527395a6c2b95 (patch) | |
tree | 281abcf508ff4f13109a2be4bab61b5205464a1a | |
parent | 42a139ed88c487f325a241c6ee8b308b3c045975 (diff) | |
download | cpython-62f9588663ebfea1735e9d142ef527395a6c2b95.zip cpython-62f9588663ebfea1735e9d142ef527395a6c2b95.tar.gz cpython-62f9588663ebfea1735e9d142ef527395a6c2b95.tar.bz2 |
bpo-36026: make descr error message consistent (GH-11930)
set.add(0) and set.add.__get__(0) now raise TypeError
with same error message.
-rw-r--r-- | Lib/test/test_descr.py | 31 | ||||
-rw-r--r-- | Objects/descrobject.c | 26 |
2 files changed, 41 insertions, 16 deletions
diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 09eef8c..e37a984 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -1614,10 +1614,14 @@ order (MRO) for bases """ with self.assertRaises(TypeError) as cm: spam_cm(list) - self.assertEqual( - str(cm.exception), + expected_errmsg = ( "descriptor 'classmeth' requires a subtype of 'xxsubtype.spamlist' " "but received 'list'") + self.assertEqual(str(cm.exception), expected_errmsg) + + with self.assertRaises(TypeError) as cm: + spam_cm.__get__(None, list) + self.assertEqual(str(cm.exception), expected_errmsg) def test_staticmethods(self): # Testing static methods... @@ -1952,6 +1956,29 @@ order (MRO) for bases """ self.assertEqual(E().foo.__func__, C.foo) # i.e., unbound self.assertTrue(repr(C.foo.__get__(C(1))).startswith("<bound method ")) + @support.impl_detail("testing error message from implementation") + def test_methods_in_c(self): + # This test checks error messages in builtin method descriptor. + # It is allowed that other Python implementations use + # different error messages. + set_add = set.add + + expected_errmsg = "descriptor 'add' of 'set' object needs an argument" + + with self.assertRaises(TypeError) as cm: + set_add() + self.assertEqual(cm.exception.args[0], expected_errmsg) + + expected_errmsg = "descriptor 'add' for 'set' objects doesn't apply to a 'int' object" + + with self.assertRaises(TypeError) as cm: + set_add(0) + self.assertEqual(cm.exception.args[0], expected_errmsg) + + with self.assertRaises(TypeError) as cm: + set_add.__get__(0) + self.assertEqual(cm.exception.args[0], expected_errmsg) + def test_special_method_lookup(self): # The lookup of special methods bypasses __getattr__ and # __getattribute__, but they still can be descriptors. diff --git a/Objects/descrobject.c b/Objects/descrobject.c index ab4151e..0fe9a44 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -78,8 +78,8 @@ descr_check(PyDescrObject *descr, PyObject *obj, PyObject **pres) } if (!PyObject_TypeCheck(obj, descr->d_type)) { PyErr_Format(PyExc_TypeError, - "descriptor '%V' for '%s' objects " - "doesn't apply to '%s' object", + "descriptor '%V' for '%.100s' objects " + "doesn't apply to a '%.100s' object", descr_name((PyDescrObject *)descr), "?", descr->d_type->tp_name, obj->ob_type->tp_name); @@ -99,7 +99,7 @@ classmethod_get(PyMethodDescrObject *descr, PyObject *obj, PyObject *type) else { /* Wot - no type?! */ PyErr_Format(PyExc_TypeError, - "descriptor '%V' for type '%s' " + "descriptor '%V' for type '%.100s' " "needs either an object or a type", descr_name((PyDescrObject *)descr), "?", PyDescr_TYPE(descr)->tp_name); @@ -108,8 +108,8 @@ classmethod_get(PyMethodDescrObject *descr, PyObject *obj, PyObject *type) } if (!PyType_Check(type)) { PyErr_Format(PyExc_TypeError, - "descriptor '%V' for type '%s' " - "needs a type, not a '%s' as arg 2", + "descriptor '%V' for type '%.100s' " + "needs a type, not a '%.100s' as arg 2", descr_name((PyDescrObject *)descr), "?", PyDescr_TYPE(descr)->tp_name, type->ob_type->tp_name); @@ -117,8 +117,8 @@ classmethod_get(PyMethodDescrObject *descr, PyObject *obj, PyObject *type) } if (!PyType_IsSubtype((PyTypeObject *)type, PyDescr_TYPE(descr))) { PyErr_Format(PyExc_TypeError, - "descriptor '%V' for type '%s' " - "doesn't apply to type '%s'", + "descriptor '%V' requires a subtype of '%.100s' " + "but received '%.100s'", descr_name((PyDescrObject *)descr), "?", PyDescr_TYPE(descr)->tp_name, ((PyTypeObject *)type)->tp_name); @@ -181,7 +181,7 @@ descr_setcheck(PyDescrObject *descr, PyObject *obj, PyObject *value, if (!PyObject_TypeCheck(obj, descr->d_type)) { PyErr_Format(PyExc_TypeError, "descriptor '%V' for '%.100s' objects " - "doesn't apply to '%.100s' object", + "doesn't apply to a '%.100s' object", descr_name(descr), "?", descr->d_type->tp_name, obj->ob_type->tp_name); @@ -239,9 +239,8 @@ methoddescr_call(PyMethodDescrObject *descr, PyObject *args, PyObject *kwargs) if (!_PyObject_RealIsSubclass((PyObject *)Py_TYPE(self), (PyObject *)PyDescr_TYPE(descr))) { PyErr_Format(PyExc_TypeError, - "descriptor '%V' " - "requires a '%.100s' object " - "but received a '%.100s'", + "descriptor '%V' for '%.100s' objects " + "doesn't apply to a '%.100s' object", descr_name((PyDescrObject *)descr), "?", PyDescr_TYPE(descr)->tp_name, self->ob_type->tp_name); @@ -278,9 +277,8 @@ _PyMethodDescr_FastCallKeywords(PyObject *descrobj, if (!_PyObject_RealIsSubclass((PyObject *)Py_TYPE(self), (PyObject *)PyDescr_TYPE(descr))) { PyErr_Format(PyExc_TypeError, - "descriptor '%V' " - "requires a '%.100s' object " - "but received a '%.100s'", + "descriptor '%V' for '%.100s' objects " + "doesn't apply to a '%.100s' object", descr_name((PyDescrObject *)descr), "?", PyDescr_TYPE(descr)->tp_name, self->ob_type->tp_name); |