summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex-Blade <44120047+Alex-Blade@users.noreply.github.com>2022-02-16 07:07:34 (GMT)
committerGitHub <noreply@github.com>2022-02-16 07:07:34 (GMT)
commit0cb765b2cec9b020224af016a83bf35c45b71932 (patch)
treee56baf1ed502d969431a3721d58bfb683d9eb705
parent4d8a515d193a4c9f3844704f974ddb870d7ee383 (diff)
downloadcpython-0cb765b2cec9b020224af016a83bf35c45b71932.zip
cpython-0cb765b2cec9b020224af016a83bf35c45b71932.tar.gz
cpython-0cb765b2cec9b020224af016a83bf35c45b71932.tar.bz2
bpo-46730: Add more info to @property AttributeError messages (GH-31311)
On `obj.read_only_property = x`, raise `AttributeError: property 'read_only_property' of 'A' object has no setter`.
-rw-r--r--Doc/howto/descriptor.rst8
-rw-r--r--Lib/test/test_property.py10
-rw-r--r--Misc/ACKS1
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2022-02-14-21-04-43.bpo-46730.rYJ1w5.rst3
-rw-r--r--Objects/descrobject.c40
5 files changed, 40 insertions, 22 deletions
diff --git a/Doc/howto/descriptor.rst b/Doc/howto/descriptor.rst
index f8b1e00..4f6389e 100644
--- a/Doc/howto/descriptor.rst
+++ b/Doc/howto/descriptor.rst
@@ -991,17 +991,17 @@ here is a pure Python equivalent:
if obj is None:
return self
if self.fget is None:
- raise AttributeError(f'unreadable attribute {self._name}')
+ raise AttributeError(f"property '{self._name}' has no getter")
return self.fget(obj)
def __set__(self, obj, value):
if self.fset is None:
- raise AttributeError(f"can't set attribute {self._name}")
+ raise AttributeError(f"property '{self._name}' has no setter")
self.fset(obj, value)
def __delete__(self, obj):
if self.fdel is None:
- raise AttributeError(f"can't delete attribute {self._name}")
+ raise AttributeError(f"property '{self._name}' has no deleter")
self.fdel(obj)
def getter(self, fget):
@@ -1456,7 +1456,7 @@ attributes stored in ``__slots__``:
>>> mark.dept = 'Space Pirate'
Traceback (most recent call last):
...
- AttributeError: can't set attribute
+ AttributeError: property 'dept' of 'Immutable' object has no setter
>>> mark.location = 'Mars'
Traceback (most recent call last):
...
diff --git a/Lib/test/test_property.py b/Lib/test/test_property.py
index 7f3813f..7d1c4a1 100644
--- a/Lib/test/test_property.py
+++ b/Lib/test/test_property.py
@@ -322,27 +322,27 @@ class _PropertyUnreachableAttribute:
cls.obj = cls.cls()
def test_get_property(self):
- with self.assertRaisesRegex(AttributeError, self._format_exc_msg("unreadable attribute")):
+ with self.assertRaisesRegex(AttributeError, self._format_exc_msg("has no getter")):
self.obj.foo
def test_set_property(self):
- with self.assertRaisesRegex(AttributeError, self._format_exc_msg("can't set attribute")):
+ with self.assertRaisesRegex(AttributeError, self._format_exc_msg("has no setter")):
self.obj.foo = None
def test_del_property(self):
- with self.assertRaisesRegex(AttributeError, self._format_exc_msg("can't delete attribute")):
+ with self.assertRaisesRegex(AttributeError, self._format_exc_msg("has no deleter")):
del self.obj.foo
class PropertyUnreachableAttributeWithName(_PropertyUnreachableAttribute, unittest.TestCase):
- msg_format = "^{} 'foo'$"
+ msg_format = r"^property 'foo' of 'PropertyUnreachableAttributeWithName\.cls' object {}$"
class cls:
foo = property()
class PropertyUnreachableAttributeNoName(_PropertyUnreachableAttribute, unittest.TestCase):
- msg_format = "^{}$"
+ msg_format = "^property of 'PropertyUnreachableAttributeNoName\.cls' object {}$"
class cls:
pass
diff --git a/Misc/ACKS b/Misc/ACKS
index 6df339c..64bd91d 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -985,6 +985,7 @@ Erno Kuusela
Ross Lagerwall
Cameron Laird
Loïc Lajeanne
+Alexander Lakeev
David Lam
Thomas Lamb
Valerie Lambert
diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-02-14-21-04-43.bpo-46730.rYJ1w5.rst b/Misc/NEWS.d/next/Core and Builtins/2022-02-14-21-04-43.bpo-46730.rYJ1w5.rst
new file mode 100644
index 0000000..473b595
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2022-02-14-21-04-43.bpo-46730.rYJ1w5.rst
@@ -0,0 +1,3 @@
+Message of AttributeError caused by getting, setting or deleting a property
+without the corresponding function now mentions that the attribute is in fact
+a property and also specifies type of the class that it belongs to.
diff --git a/Objects/descrobject.c b/Objects/descrobject.c
index 9516268..9379ad6 100644
--- a/Objects/descrobject.c
+++ b/Objects/descrobject.c
@@ -1463,17 +1463,17 @@ class property(object):
if inst is None:
return self
if self.__get is None:
- raise AttributeError, "unreadable attribute"
+ raise AttributeError, "property has no getter"
return self.__get(inst)
def __set__(self, inst, value):
if self.__set is None:
- raise AttributeError, "can't set attribute"
+ raise AttributeError, "property has no setter"
return self.__set(inst, value)
def __delete__(self, inst):
if self.__del is None:
- raise AttributeError, "can't delete attribute"
+ raise AttributeError, "property has no deleter"
return self.__del(inst)
*/
@@ -1586,9 +1586,15 @@ property_descr_get(PyObject *self, PyObject *obj, PyObject *type)
propertyobject *gs = (propertyobject *)self;
if (gs->prop_get == NULL) {
if (gs->prop_name != NULL) {
- PyErr_Format(PyExc_AttributeError, "unreadable attribute %R", gs->prop_name);
- } else {
- PyErr_SetString(PyExc_AttributeError, "unreadable attribute");
+ PyErr_Format(PyExc_AttributeError,
+ "property %R of %R object has no getter",
+ gs->prop_name,
+ PyType_GetQualName(Py_TYPE(obj)));
+ }
+ else {
+ PyErr_Format(PyExc_AttributeError,
+ "property of %R object has no getter",
+ PyType_GetQualName(Py_TYPE(obj)));
}
return NULL;
@@ -1611,18 +1617,26 @@ property_descr_set(PyObject *self, PyObject *obj, PyObject *value)
}
if (func == NULL) {
- if (gs->prop_name != NULL) {
+ if (gs->prop_name != NULL && obj != NULL) {
PyErr_Format(PyExc_AttributeError,
value == NULL ?
- "can't delete attribute %R" :
- "can't set attribute %R",
- gs->prop_name);
+ "property %R of %R object has no deleter" :
+ "property %R of %R object has no setter",
+ gs->prop_name,
+ PyType_GetQualName(Py_TYPE(obj)));
+ }
+ else if (obj != NULL) {
+ PyErr_Format(PyExc_AttributeError,
+ value == NULL ?
+ "property of %R object has no deleter" :
+ "property of %R object has no setter",
+ PyType_GetQualName(Py_TYPE(obj)));
}
else {
PyErr_SetString(PyExc_AttributeError,
- value == NULL ?
- "can't delete attribute" :
- "can't set attribute");
+ value == NULL ?
+ "property has no deleter" :
+ "property has no setter");
}
return -1;
}