mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 05:31:20 +00:00 
			
		
		
		
	bpo-43857: Improve the AttributeError message when deleting a missing attribute (#25424)
Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
This commit is contained in:
		
							parent
							
								
									43b135f94e
								
							
						
					
					
						commit
						a95138b2c5
					
				
					 4 changed files with 62 additions and 5 deletions
				
			
		|  | @ -611,6 +611,49 @@ class A: | ||||||
|         with self.assertRaises(TypeError): |         with self.assertRaises(TypeError): | ||||||
|             type.__setattr__(A, b'x', None) |             type.__setattr__(A, b'x', None) | ||||||
| 
 | 
 | ||||||
|  |     def testTypeAttributeAccessErrorMessages(self): | ||||||
|  |         class A: | ||||||
|  |             pass | ||||||
|  | 
 | ||||||
|  |         error_msg = "type object 'A' has no attribute 'x'" | ||||||
|  |         with self.assertRaisesRegex(AttributeError, error_msg): | ||||||
|  |             A.x | ||||||
|  |         with self.assertRaisesRegex(AttributeError, error_msg): | ||||||
|  |             del A.x | ||||||
|  | 
 | ||||||
|  |     def testObjectAttributeAccessErrorMessages(self): | ||||||
|  |         class A: | ||||||
|  |             pass | ||||||
|  |         class B: | ||||||
|  |             y = 0 | ||||||
|  |             __slots__ = ('z',) | ||||||
|  | 
 | ||||||
|  |         error_msg = "'A' object has no attribute 'x'" | ||||||
|  |         with self.assertRaisesRegex(AttributeError, error_msg): | ||||||
|  |             A().x | ||||||
|  |         with self.assertRaisesRegex(AttributeError, error_msg): | ||||||
|  |             del A().x | ||||||
|  | 
 | ||||||
|  |         error_msg = "'B' object has no attribute 'x'" | ||||||
|  |         with self.assertRaisesRegex(AttributeError, error_msg): | ||||||
|  |             B().x | ||||||
|  |         with self.assertRaisesRegex(AttributeError, error_msg): | ||||||
|  |             del B().x | ||||||
|  |         with self.assertRaisesRegex(AttributeError, error_msg): | ||||||
|  |             B().x = 0 | ||||||
|  | 
 | ||||||
|  |         error_msg = "'B' object attribute 'y' is read-only" | ||||||
|  |         with self.assertRaisesRegex(AttributeError, error_msg): | ||||||
|  |             del B().y | ||||||
|  |         with self.assertRaisesRegex(AttributeError, error_msg): | ||||||
|  |             B().y = 0 | ||||||
|  | 
 | ||||||
|  |         error_msg = 'z' | ||||||
|  |         with self.assertRaisesRegex(AttributeError, error_msg): | ||||||
|  |             B().z | ||||||
|  |         with self.assertRaisesRegex(AttributeError, error_msg): | ||||||
|  |             del B().z | ||||||
|  | 
 | ||||||
|     def testConstructorErrorMessages(self): |     def testConstructorErrorMessages(self): | ||||||
|         # bpo-31506: Improves the error message logic for object_new & object_init |         # bpo-31506: Improves the error message logic for object_new & object_init | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -0,0 +1,2 @@ | ||||||
|  | Improve the :exc:`AttributeError` message when deleting a missing attribute. | ||||||
|  | Patch by Géry Ogam. | ||||||
|  | @ -5472,7 +5472,9 @@ _PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values, | ||||||
|     values->values[ix] = value; |     values->values[ix] = value; | ||||||
|     if (old_value == NULL) { |     if (old_value == NULL) { | ||||||
|         if (value == NULL) { |         if (value == NULL) { | ||||||
|             PyErr_SetObject(PyExc_AttributeError, name); |             PyErr_Format(PyExc_AttributeError, | ||||||
|  |                          "'%.100s' object has no attribute '%U'", | ||||||
|  |                          Py_TYPE(obj)->tp_name, name); | ||||||
|             return -1; |             return -1; | ||||||
|         } |         } | ||||||
|         _PyDictValues_AddToInsertionOrder(values, ix); |         _PyDictValues_AddToInsertionOrder(values, ix); | ||||||
|  |  | ||||||
|  | @ -1382,7 +1382,7 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name, | ||||||
|         return -1; |         return -1; | ||||||
| 
 | 
 | ||||||
|     Py_INCREF(name); |     Py_INCREF(name); | ||||||
| 
 |     Py_INCREF(tp); | ||||||
|     descr = _PyType_Lookup(tp, name); |     descr = _PyType_Lookup(tp, name); | ||||||
| 
 | 
 | ||||||
|     if (descr != NULL) { |     if (descr != NULL) { | ||||||
|  | @ -1426,11 +1426,21 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name, | ||||||
|             res = PyDict_SetItem(dict, name, value); |             res = PyDict_SetItem(dict, name, value); | ||||||
|         Py_DECREF(dict); |         Py_DECREF(dict); | ||||||
|     } |     } | ||||||
|     if (res < 0 && PyErr_ExceptionMatches(PyExc_KeyError)) |     if (res < 0 && PyErr_ExceptionMatches(PyExc_KeyError)) { | ||||||
|         PyErr_SetObject(PyExc_AttributeError, name); |         if (PyType_IsSubtype(tp, &PyType_Type)) { | ||||||
| 
 |             PyErr_Format(PyExc_AttributeError, | ||||||
|  |                          "type object '%.50s' has no attribute '%U'", | ||||||
|  |                          ((PyTypeObject*)obj)->tp_name, name); | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |             PyErr_Format(PyExc_AttributeError, | ||||||
|  |                          "'%.100s' object has no attribute '%U'", | ||||||
|  |                          tp->tp_name, name); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|   done: |   done: | ||||||
|     Py_XDECREF(descr); |     Py_XDECREF(descr); | ||||||
|  |     Py_DECREF(tp); | ||||||
|     Py_DECREF(name); |     Py_DECREF(name); | ||||||
|     return res; |     return res; | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Géry Ogam
						Géry Ogam