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]

Reply via email to