From 8221e0ffd4fec3b370b958597df2c881a6c811a4 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 7 Oct 2025 18:20:38 +0300 Subject: [PATCH 1/2] gh-137508: Unify error messages for index() and remove() Unify error messages for the index() and remove() methods of classes list, tuple, range, memoryview, str, bytes, bytearray, array.array, and collections.deque, and the operator.indexOf() function. Improve error message for xml.etree.ElementTree.Element.remove(). --- Include/internal/pycore_bytes_methods.h | 6 ++++-- Lib/operator.py | 2 +- Lib/test/test_xml_etree.py | 2 +- Lib/xml/etree/ElementTree.py | 2 +- .../2025-10-07-18-20-33.gh-issue-137508.Llt4Ei.rst | 5 +++++ Modules/_collectionsmodule.c | 4 ++-- Modules/_elementtree.c | 3 +-- Modules/arraymodule.c | 4 ++-- Objects/abstract.c | 3 +-- Objects/bytearrayobject.c | 6 +++--- Objects/bytes_methods.c | 14 ++++++++------ Objects/bytesobject.c | 4 ++-- Objects/listobject.c | 4 ++-- Objects/memoryobject.c | 2 +- Objects/rangeobject.c | 2 +- Objects/tupleobject.c | 2 +- 16 files changed, 36 insertions(+), 29 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-07-18-20-33.gh-issue-137508.Llt4Ei.rst diff --git a/Include/internal/pycore_bytes_methods.h b/Include/internal/pycore_bytes_methods.h index 3e1474c1c010f9..02bd8c53c1e126 100644 --- a/Include/internal/pycore_bytes_methods.h +++ b/Include/internal/pycore_bytes_methods.h @@ -29,11 +29,13 @@ extern void _Py_bytes_swapcase(char *result, const char *s, Py_ssize_t len); extern PyObject *_Py_bytes_find(const char *str, Py_ssize_t len, PyObject *sub, Py_ssize_t start, Py_ssize_t end); extern PyObject *_Py_bytes_index(const char *str, Py_ssize_t len, PyObject *sub, - Py_ssize_t start, Py_ssize_t end); + Py_ssize_t start, Py_ssize_t end, + const char *classname); extern PyObject *_Py_bytes_rfind(const char *str, Py_ssize_t len, PyObject *sub, Py_ssize_t start, Py_ssize_t end); extern PyObject *_Py_bytes_rindex(const char *str, Py_ssize_t len, PyObject *sub, - Py_ssize_t start, Py_ssize_t end); + Py_ssize_t start, Py_ssize_t end, + const char *classname); extern PyObject *_Py_bytes_count(const char *str, Py_ssize_t len, PyObject *sub, Py_ssize_t start, Py_ssize_t end); extern int _Py_bytes_contains(const char *str, Py_ssize_t len, PyObject *arg); diff --git a/Lib/operator.py b/Lib/operator.py index 1b765522f85949..9268ef35e7ea9a 100644 --- a/Lib/operator.py +++ b/Lib/operator.py @@ -184,7 +184,7 @@ def indexOf(a, b): if j is b or j == b: return i else: - raise ValueError('sequence.index(x): x not in sequence') + raise ValueError('value not in sequence') def setitem(a, b, c): "Same as a[b] = c." diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py index f65baa0cfae2ad..02b5b2b6c18657 100644 --- a/Lib/test/test_xml_etree.py +++ b/Lib/test/test_xml_etree.py @@ -371,7 +371,7 @@ def test_simpleops(self): element.remove(subelement) self.serialize_check(element, '') # 5 with self.assertRaisesRegex(ValueError, - r'Element\.remove\(.+\): element not found'): + r" not in "): element.remove(subelement) self.serialize_check(element, '') # 6 element[0:0] = [subelement, subelement, subelement] diff --git a/Lib/xml/etree/ElementTree.py b/Lib/xml/etree/ElementTree.py index dafe5b1b8a0c3f..f4306a3cafb11a 100644 --- a/Lib/xml/etree/ElementTree.py +++ b/Lib/xml/etree/ElementTree.py @@ -271,7 +271,7 @@ def remove(self, subelement): self._children.remove(subelement) except ValueError: # to align the error message with the C implementation - raise ValueError("Element.remove(x): element not found") from None + raise ValueError(f"{subelement!r} not in {self!r}") from None def find(self, path, namespaces=None): """Find first matching element by tag name or path. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-07-18-20-33.gh-issue-137508.Llt4Ei.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-07-18-20-33.gh-issue-137508.Llt4Ei.rst new file mode 100644 index 00000000000000..5458b17ffd941d --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-07-18-20-33.gh-issue-137508.Llt4Ei.rst @@ -0,0 +1,5 @@ +Unify error messages for the :meth:`!index` and :meth:`!remove` methods of +classes :class:`list`, :class:`tuple`, :class:`range`, :class:`memoryview`, +:class:`str`, :class:`bytes`, :class:`bytearray`, :class:`array.array`, and +:class:`collections.deque`, and the :func:`operator.indexOf` function. +Improve error message for :meth:`xml.etree.ElementTree.Element.remove`. diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c index 3ba48d5d9d3c64..dba7e8a3765a45 100644 --- a/Modules/_collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -1302,7 +1302,7 @@ deque_index_impl(dequeobject *deque, PyObject *v, Py_ssize_t start, index = 0; } } - PyErr_SetString(PyExc_ValueError, "deque.index(x): x not in deque"); + PyErr_SetString(PyExc_ValueError, "value not in deque"); return NULL; } @@ -1472,7 +1472,7 @@ deque_remove_impl(dequeobject *deque, PyObject *value) } } if (i == n) { - PyErr_SetString(PyExc_ValueError, "deque.remove(x): x not in deque"); + PyErr_SetString(PyExc_ValueError, "value not in deque"); return NULL; } rv = deque_del_item(deque, i); diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c index 9263f14b57f972..6923f2bcc05763 100644 --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -1679,8 +1679,7 @@ _elementtree_Element_remove_impl(ElementObject *self, PyObject *subelement) } if (rc == 0) { - PyErr_SetString(PyExc_ValueError, - "Element.remove(x): element not found"); + PyErr_Format(PyExc_ValueError, "%R not in %R", subelement, self); return NULL; } diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index d97cf7af767ca3..f0789b71eabae3 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -1233,7 +1233,7 @@ array_array_index_impl(arrayobject *self, PyObject *v, Py_ssize_t start, else if (cmp < 0) return NULL; } - PyErr_SetString(PyExc_ValueError, "array.index(x): x not in array"); + PyErr_SetString(PyExc_ValueError, "value not in array"); return NULL; } @@ -1285,7 +1285,7 @@ array_array_remove_impl(arrayobject *self, PyObject *v) else if (cmp < 0) return NULL; } - PyErr_SetString(PyExc_ValueError, "array.remove(x): x not in array"); + PyErr_SetString(PyExc_ValueError, "value not in array"); return NULL; } diff --git a/Objects/abstract.c b/Objects/abstract.c index 8adad8407d04d4..297193dcc44cb5 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -2206,8 +2206,7 @@ _PySequence_IterSearch(PyObject *seq, PyObject *obj, int operation) if (operation != PY_ITERSEARCH_INDEX) goto Done; - PyErr_SetString(PyExc_ValueError, - "sequence.index(x): x not in sequence"); + PyErr_SetString(PyExc_ValueError, "value not in sequence"); /* fall into failure code */ Fail: n = -1; diff --git a/Objects/bytearrayobject.c b/Objects/bytearrayobject.c index c519485c1cc74c..6f4b655da2f77a 100644 --- a/Objects/bytearrayobject.c +++ b/Objects/bytearrayobject.c @@ -1293,7 +1293,7 @@ bytearray_index_impl(PyByteArrayObject *self, PyObject *sub, /*[clinic end generated code: output=067a1e78efc672a7 input=c37f177cfee19fe4]*/ { return _Py_bytes_index(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), - sub, start, end); + sub, start, end, "bytearray"); } /*[clinic input] @@ -1331,7 +1331,7 @@ bytearray_rindex_impl(PyByteArrayObject *self, PyObject *sub, /*[clinic end generated code: output=38e1cf66bafb08b9 input=7d198b3d6b0a62ce]*/ { return _Py_bytes_rindex(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), - sub, start, end); + sub, start, end, "bytearray"); } static int @@ -2207,7 +2207,7 @@ bytearray_remove_impl(PyByteArrayObject *self, int value) where = stringlib_find_char(buf, n, value); if (where < 0) { - PyErr_SetString(PyExc_ValueError, "value not found in bytearray"); + PyErr_SetString(PyExc_ValueError, "value not in bytearray"); return NULL; } if (!_canresize(self)) diff --git a/Objects/bytes_methods.c b/Objects/bytes_methods.c index 56a461d0dd08a7..a2ee55c89bbe9f 100644 --- a/Objects/bytes_methods.c +++ b/Objects/bytes_methods.c @@ -521,14 +521,15 @@ _Py_bytes_find(const char *str, Py_ssize_t len, PyObject *sub, PyObject * _Py_bytes_index(const char *str, Py_ssize_t len, PyObject *sub, - Py_ssize_t start, Py_ssize_t end) + Py_ssize_t start, Py_ssize_t end, const char *classname) { Py_ssize_t result = find_internal(str, len, "index", sub, start, end, +1); if (result == -2) return NULL; if (result == -1) { - PyErr_SetString(PyExc_ValueError, - "subsection not found"); + PyErr_Format(PyExc_ValueError, "%s not in %s", + PyIndex_Check(sub) ? "value" : "subsection", + classname); return NULL; } return PyLong_FromSsize_t(result); @@ -546,14 +547,15 @@ _Py_bytes_rfind(const char *str, Py_ssize_t len, PyObject *sub, PyObject * _Py_bytes_rindex(const char *str, Py_ssize_t len, PyObject *sub, - Py_ssize_t start, Py_ssize_t end) + Py_ssize_t start, Py_ssize_t end, const char *classname) { Py_ssize_t result = find_internal(str, len, "rindex", sub, start, end, -1); if (result == -2) return NULL; if (result == -1) { - PyErr_SetString(PyExc_ValueError, - "subsection not found"); + PyErr_Format(PyExc_ValueError, "%s not in %s", + PyIndex_Check(sub) ? "value" : "subsection", + classname); return NULL; } return PyLong_FromSsize_t(result); diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index de8ab26db1e966..816210c3665c9e 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -1965,7 +1965,7 @@ bytes_index_impl(PyBytesObject *self, PyObject *sub, Py_ssize_t start, /*[clinic end generated code: output=0da25cc74683ba42 input=1cb45ce71456a269]*/ { return _Py_bytes_index(PyBytes_AS_STRING(self), PyBytes_GET_SIZE(self), - sub, start, end); + sub, start, end, "bytes"); } /*[clinic input] @@ -2001,7 +2001,7 @@ bytes_rindex_impl(PyBytesObject *self, PyObject *sub, Py_ssize_t start, /*[clinic end generated code: output=42bf674e0a0aabf6 input=bb5f473c64610c43]*/ { return _Py_bytes_rindex(PyBytes_AS_STRING(self), PyBytes_GET_SIZE(self), - sub, start, end); + sub, start, end, "bytes"); } diff --git a/Objects/listobject.c b/Objects/listobject.c index 5905a6d335b311..bbf8341b91dd80 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -3306,7 +3306,7 @@ list_index_impl(PyListObject *self, PyObject *value, Py_ssize_t start, else if (cmp < 0) return NULL; } - PyErr_SetString(PyExc_ValueError, "list.index(x): x not in list"); + PyErr_SetString(PyExc_ValueError, "value not in list"); return NULL; } @@ -3376,7 +3376,7 @@ list_remove_impl(PyListObject *self, PyObject *value) else if (cmp < 0) return NULL; } - PyErr_SetString(PyExc_ValueError, "list.remove(x): x not in list"); + PyErr_SetString(PyExc_ValueError, "value not in list"); return NULL; } diff --git a/Objects/memoryobject.c b/Objects/memoryobject.c index f1232f389210ea..26b3540ac3410a 100644 --- a/Objects/memoryobject.c +++ b/Objects/memoryobject.c @@ -2894,7 +2894,7 @@ memoryview_index_impl(PyMemoryViewObject *self, PyObject *value, } } - PyErr_SetString(PyExc_ValueError, "memoryview.index(x): x not found"); + PyErr_SetString(PyExc_ValueError, "value not in memoryview"); return NULL; } diff --git a/Objects/rangeobject.c b/Objects/rangeobject.c index f8cdfe68a6435e..0a6d21d52ce8cc 100644 --- a/Objects/rangeobject.c +++ b/Objects/rangeobject.c @@ -665,7 +665,7 @@ range_index(PyObject *self, PyObject *ob) } /* object is not in the range */ - PyErr_SetString(PyExc_ValueError, "range.index(x): x not in range"); + PyErr_SetString(PyExc_ValueError, "value not in range"); return NULL; } diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c index 9b31758485ca5e..dbcc802d41c047 100644 --- a/Objects/tupleobject.c +++ b/Objects/tupleobject.c @@ -585,7 +585,7 @@ tuple_index_impl(PyTupleObject *self, PyObject *value, Py_ssize_t start, else if (cmp < 0) return NULL; } - PyErr_SetString(PyExc_ValueError, "tuple.index(x): x not in tuple"); + PyErr_SetString(PyExc_ValueError, "value not in tuple"); return NULL; } From 109ec47e359ac6ff03a2ec67d879f25632ce9964 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 10 Oct 2025 15:24:00 +0300 Subject: [PATCH 2/2] Keep subsection not found for byte substring search. --- Objects/bytes_methods.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Objects/bytes_methods.c b/Objects/bytes_methods.c index a2ee55c89bbe9f..5c78c2cecab732 100644 --- a/Objects/bytes_methods.c +++ b/Objects/bytes_methods.c @@ -527,9 +527,12 @@ _Py_bytes_index(const char *str, Py_ssize_t len, PyObject *sub, if (result == -2) return NULL; if (result == -1) { - PyErr_Format(PyExc_ValueError, "%s not in %s", - PyIndex_Check(sub) ? "value" : "subsection", - classname); + if (PyIndex_Check(sub)) { + PyErr_Format(PyExc_ValueError, "value not in %s", classname); + } + else { + PyErr_SetString(PyExc_ValueError, "subsection not found"); + } return NULL; } return PyLong_FromSsize_t(result);