summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorInada Naoki <songofacandy@gmail.com>2019-04-01 08:56:11 (GMT)
committerGitHub <noreply@github.com>2019-04-01 08:56:11 (GMT)
commit62f9588663ebfea1735e9d142ef527395a6c2b95 (patch)
tree281abcf508ff4f13109a2be4bab61b5205464a1a
parent42a139ed88c487f325a241c6ee8b308b3c045975 (diff)
downloadcpython-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.py31
-rw-r--r--Objects/descrobject.c26
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);