https://github.com/python/cpython/commit/b4be3a5804cbbecb9e1d2984241b455bf15065a8 commit: b4be3a5804cbbecb9e1d2984241b455bf15065a8 branch: main author: Victor Stinner <[email protected]> committer: vstinner <[email protected]> date: 2026-09-29T12:17:17+02:00 summary:
gh-158334: Fix bytes.translate() when the input is returned unchanged (#158344) Do no read output_start pointer after PyBytesWriter_Finish() since the pointer became a dangling pointer. Co-authored-by: Vyron Vasileiadis <[email protected]> files: M Lib/test/test_bytes.py M Objects/bytesobject.c diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index ad5091b54d7911..b55863256cc37c 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -1147,6 +1147,34 @@ def test_translate(self): c = b.translate(None, delete=b'e') self.assertEqual(c, b'hllo') + # short inputs starting with NUL bytes + table = bytes.maketrans(b'\x00', b'Z') + for data in b'\x00', b'\x00' * 8, b'\x00' * 8 + b'a' * 247: + c = self.type2test(data).translate(table) + self.assertEqual(c, data.replace(b'\x00', b'Z')) + + @support.cpython_only + def test_translate_unchanged(self): + if self.type2test != bytes: + self.skipTest("test specific bytes.translate()") + + # bytes.translate() returns the input string unchanged + # if no byte is modified + size = 1024 + b = b'hell' + b'o' * size + rosetta = bytearray(range(256)) + rosetta[ord('#')] = ord('?') + self.assertIs(b.translate(rosetta), b) + + # bytes.translate() always create a new object + # if the input string is a bytes subclass + class bytes_subclass(bytes): + pass + b = bytes_subclass(b) + result = b.translate(rosetta) + self.assertIsNot(result, b) + self.assertEqual(result, b) + def test_sq_item(self): _testlimitedcapi = import_helper.import_module('_testlimitedcapi') obj = self.type2test((42,)) diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index 683306fe724a5b..fcda380dbeb762 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -2324,15 +2324,19 @@ bytes_translate_impl(PyBytesObject *self, PyObject *table, c = Py_CHARMASK(*input++); *output++ = table_chars[c]; } - PyObject *result = PyBytesWriter_Finish(writer); /* Check if anything changed (for returning original object) */ /* We save this check until the end so that the compiler will */ /* unroll the loop above leading to MUCH faster code. */ - if (result != NULL && PyBytes_CheckExact(input_obj)) { - if (memcmp(PyBytes_AS_STRING(input_obj), output_start, inlen) == 0) { - Py_SETREF(result, Py_NewRef(input_obj)); - } + PyObject *result; + if (PyBytes_CheckExact(input_obj) + && memcmp(PyBytes_AS_STRING(input_obj), output_start, inlen) == 0) + { + PyBytesWriter_Discard(writer); + result = Py_NewRef(input_obj); + } + else { + result = PyBytesWriter_Finish(writer); } PyBuffer_Release(&del_table_view); _______________________________________________ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3//lists/python-checkins.python.org Member address: [email protected]
