Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions Lib/test/test_bytes.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import textwrap
import threading
import unittest
from _codecs import _unregister_error as _codecs_unregister_error

import test.support
from test import support
Expand Down Expand Up @@ -2195,6 +2196,30 @@ def delslice():
self.assertRaises(BufferError, delslice)
self.assertEqual(b, orig)

def test_decode_resize_forbidden(self):
# The storage is pinned while it is decoded, so an error handler
# cannot resize the bytearray.
b = bytearray(b'ab\xffcd')
errors = 'test.bytearray_decode_resize'
def handler(exc):
self.assertRaises(BufferError, b.clear)
self.assertRaises(BufferError, b.append, 0)
return ('?', exc.end)
self.addCleanup(_codecs_unregister_error, errors)
codecs.register_error(errors, handler)
for encoding in 'utf-8', 'utf-8-sig':
with self.subTest(encoding=encoding):
self.assertEqual(b.decode(encoding, errors), 'ab?cd')
self.assertEqual(b, b'ab\xffcd')
Comment thread
vstinner marked this conversation as resolved.

def test_decode_subclass_buffer(self):
# decode() decodes the buffer that the object exports.
class B(bytearray):
def __buffer__(self, flags):
return memoryview(b'other')
self.assertEqual(B(b'mine').decode(), 'other')
self.assertEqual(B(b'mine').decode('latin-1'), 'other')

@test.support.cpython_only
def test_obsolete_write_lock(self):
_testcapi = import_helper.import_module('_testcapi')
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
On Free Threading, speed up :meth:`bytearray.decode` by decoding the underlying
storage directly instead of going through the buffer protocol.
14 changes: 13 additions & 1 deletion Objects/bytearrayobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -2599,7 +2599,19 @@ bytearray_decode_impl(PyByteArrayObject *self, const char *encoding,
{
if (encoding == NULL)
encoding = PyUnicode_GetDefaultEncoding();
return PyUnicode_FromEncodedObject((PyObject*)self, encoding, errors);
if (Py_TYPE(self)->tp_as_buffer->bf_getbuffer != bytearray_getbuffer) {
/* A subclass may export a different buffer. */
return PyUnicode_FromEncodedObject((PyObject*)self, encoding, errors);
}

/* Decode the storage directly instead of exporting a buffer, which
would re-acquire the critical section we already hold. Increase
exports to prevent the storage from changing during the decode. */
self->ob_exports++;
PyObject *res = PyUnicode_Decode(PyByteArray_AS_STRING(self),
Py_SIZE(self), encoding, errors);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: I wanted to suggest replacing Py_SIZE() with PyByteArray_GET_SIZE() but I see that it uses an atomic operation on Free Threading, which is not needed in a critical section. Hum. PyByteArray_GET_SIZE() usage in this file is not really consistent anyway.

self->ob_exports--;
return res;
}

PyDoc_STRVAR(alloc_doc,
Expand Down
Loading