diff options
author | Alex-Blade <44120047+Alex-Blade@users.noreply.github.com> | 2022-02-16 07:07:34 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-02-16 07:07:34 (GMT) |
commit | 0cb765b2cec9b020224af016a83bf35c45b71932 (patch) | |
tree | e56baf1ed502d969431a3721d58bfb683d9eb705 | |
parent | 4d8a515d193a4c9f3844704f974ddb870d7ee383 (diff) | |
download | cpython-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.rst | 8 | ||||
-rw-r--r-- | Lib/test/test_property.py | 10 | ||||
-rw-r--r-- | Misc/ACKS | 1 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Core and Builtins/2022-02-14-21-04-43.bpo-46730.rYJ1w5.rst | 3 | ||||
-rw-r--r-- | Objects/descrobject.c | 40 |
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 @@ -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; } |