Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 62f9588

Browse files
authored
bpo-36026: make descr error message consistent (GH-11930)
set.add(0) and set.add.__get__(0) now raise TypeError with same error message.
1 parent 42a139e commit 62f9588

2 files changed

Lines changed: 41 additions & 16 deletions

File tree

Lib/test/test_descr.py

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1614,10 +1614,14 @@ class SubSpam(spam.spamlist): pass
16141614

16151615
with self.assertRaises(TypeError) as cm:
16161616
spam_cm(list)
1617-
self.assertEqual(
1618-
str(cm.exception),
1617+
expected_errmsg = (
16191618
"descriptor 'classmeth' requires a subtype of 'xxsubtype.spamlist' "
16201619
"but received 'list'")
1620+
self.assertEqual(str(cm.exception), expected_errmsg)
1621+
1622+
with self.assertRaises(TypeError) as cm:
1623+
spam_cm.__get__(None, list)
1624+
self.assertEqual(str(cm.exception), expected_errmsg)
16211625

16221626
def test_staticmethods(self):
16231627
# Testing static methods...
@@ -1952,6 +1956,29 @@ class E(object):
19521956
self.assertEqual(E().foo.__func__, C.foo) # i.e., unbound
19531957
self.assertTrue(repr(C.foo.__get__(C(1))).startswith("<bound method "))
19541958

1959+
@support.impl_detail("testing error message from implementation")
1960+
def test_methods_in_c(self):
1961+
# This test checks error messages in builtin method descriptor.
1962+
# It is allowed that other Python implementations use
1963+
# different error messages.
1964+
set_add = set.add
1965+
1966+
expected_errmsg = "descriptor 'add' of 'set' object needs an argument"
1967+
1968+
with self.assertRaises(TypeError) as cm:
1969+
set_add()
1970+
self.assertEqual(cm.exception.args[0], expected_errmsg)
1971+
1972+
expected_errmsg = "descriptor 'add' for 'set' objects doesn't apply to a 'int' object"
1973+
1974+
with self.assertRaises(TypeError) as cm:
1975+
set_add(0)
1976+
self.assertEqual(cm.exception.args[0], expected_errmsg)
1977+
1978+
with self.assertRaises(TypeError) as cm:
1979+
set_add.__get__(0)
1980+
self.assertEqual(cm.exception.args[0], expected_errmsg)
1981+
19551982
def test_special_method_lookup(self):
19561983
# The lookup of special methods bypasses __getattr__ and
19571984
# __getattribute__, but they still can be descriptors.

Objects/descrobject.c

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ descr_check(PyDescrObject *descr, PyObject *obj, PyObject **pres)
7878
}
7979
if (!PyObject_TypeCheck(obj, descr->d_type)) {
8080
PyErr_Format(PyExc_TypeError,
81-
"descriptor '%V' for '%s' objects "
82-
"doesn't apply to '%s' object",
81+
"descriptor '%V' for '%.100s' objects "
82+
"doesn't apply to a '%.100s' object",
8383
descr_name((PyDescrObject *)descr), "?",
8484
descr->d_type->tp_name,
8585
obj->ob_type->tp_name);
@@ -99,7 +99,7 @@ classmethod_get(PyMethodDescrObject *descr, PyObject *obj, PyObject *type)
9999
else {
100100
/* Wot - no type?! */
101101
PyErr_Format(PyExc_TypeError,
102-
"descriptor '%V' for type '%s' "
102+
"descriptor '%V' for type '%.100s' "
103103
"needs either an object or a type",
104104
descr_name((PyDescrObject *)descr), "?",
105105
PyDescr_TYPE(descr)->tp_name);
@@ -108,17 +108,17 @@ classmethod_get(PyMethodDescrObject *descr, PyObject *obj, PyObject *type)
108108
}
109109
if (!PyType_Check(type)) {
110110
PyErr_Format(PyExc_TypeError,
111-
"descriptor '%V' for type '%s' "
112-
"needs a type, not a '%s' as arg 2",
111+
"descriptor '%V' for type '%.100s' "
112+
"needs a type, not a '%.100s' as arg 2",
113113
descr_name((PyDescrObject *)descr), "?",
114114
PyDescr_TYPE(descr)->tp_name,
115115
type->ob_type->tp_name);
116116
return NULL;
117117
}
118118
if (!PyType_IsSubtype((PyTypeObject *)type, PyDescr_TYPE(descr))) {
119119
PyErr_Format(PyExc_TypeError,
120-
"descriptor '%V' for type '%s' "
121-
"doesn't apply to type '%s'",
120+
"descriptor '%V' requires a subtype of '%.100s' "
121+
"but received '%.100s'",
122122
descr_name((PyDescrObject *)descr), "?",
123123
PyDescr_TYPE(descr)->tp_name,
124124
((PyTypeObject *)type)->tp_name);
@@ -181,7 +181,7 @@ descr_setcheck(PyDescrObject *descr, PyObject *obj, PyObject *value,
181181
if (!PyObject_TypeCheck(obj, descr->d_type)) {
182182
PyErr_Format(PyExc_TypeError,
183183
"descriptor '%V' for '%.100s' objects "
184-
"doesn't apply to '%.100s' object",
184+
"doesn't apply to a '%.100s' object",
185185
descr_name(descr), "?",
186186
descr->d_type->tp_name,
187187
obj->ob_type->tp_name);
@@ -239,9 +239,8 @@ methoddescr_call(PyMethodDescrObject *descr, PyObject *args, PyObject *kwargs)
239239
if (!_PyObject_RealIsSubclass((PyObject *)Py_TYPE(self),
240240
(PyObject *)PyDescr_TYPE(descr))) {
241241
PyErr_Format(PyExc_TypeError,
242-
"descriptor '%V' "
243-
"requires a '%.100s' object "
244-
"but received a '%.100s'",
242+
"descriptor '%V' for '%.100s' objects "
243+
"doesn't apply to a '%.100s' object",
245244
descr_name((PyDescrObject *)descr), "?",
246245
PyDescr_TYPE(descr)->tp_name,
247246
self->ob_type->tp_name);
@@ -278,9 +277,8 @@ _PyMethodDescr_FastCallKeywords(PyObject *descrobj,
278277
if (!_PyObject_RealIsSubclass((PyObject *)Py_TYPE(self),
279278
(PyObject *)PyDescr_TYPE(descr))) {
280279
PyErr_Format(PyExc_TypeError,
281-
"descriptor '%V' "
282-
"requires a '%.100s' object "
283-
"but received a '%.100s'",
280+
"descriptor '%V' for '%.100s' objects "
281+
"doesn't apply to a '%.100s' object",
284282
descr_name((PyDescrObject *)descr), "?",
285283
PyDescr_TYPE(descr)->tp_name,
286284
self->ob_type->tp_name);

0 commit comments

Comments
 (0)