diff --git a/CHANGES/12404.bugfix.rst b/CHANGES/12404.bugfix.rst new file mode 100644 index 00000000000..1c5e82f8d3a --- /dev/null +++ b/CHANGES/12404.bugfix.rst @@ -0,0 +1,3 @@ +Fixed ``BodyPartReader.read()`` and ``BodyPartReader.read(decode=True)`` returning +:class:`bytearray` instead of :class:`bytes`, violating the documented API contract. +-- by :user:`CrepuscularIRIS`. diff --git a/aiohttp/multipart.py b/aiohttp/multipart.py index 9d5e5d27b84..4d4eee7887f 100644 --- a/aiohttp/multipart.py +++ b/aiohttp/multipart.py @@ -322,8 +322,8 @@ async def read(self, *, decode: bool = False) -> bytes: decoded_data.extend(d) if len(decoded_data) > self._client_max_size: raise self._max_size_error_cls(self._client_max_size) - return decoded_data - return data + return bytes(decoded_data) + return bytes(data) async def read_chunk(self, size: int = chunk_size) -> bytes: """Reads body part content chunk of the specified size. diff --git a/docs/multipart_reference.rst b/docs/multipart_reference.rst index 41c5942c7d7..fb0c4f2e064 100644 --- a/docs/multipart_reference.rst +++ b/docs/multipart_reference.rst @@ -44,7 +44,7 @@ Multipart reference from ``Content-Encoding`` header. If it missed data remains untouched - :rtype: bytearray + :rtype: bytes .. method:: read_chunk(size=chunk_size) :async: diff --git a/tests/test_multipart.py b/tests/test_multipart.py index 83046ccc034..db288a97d07 100644 --- a/tests/test_multipart.py +++ b/tests/test_multipart.py @@ -185,6 +185,17 @@ async def test_read(self) -> None: assert b"Hello, world!" == result assert obj.at_eof() + @pytest.mark.parametrize("kwargs", [{}, {"decode": True}]) + async def test_read_returns_bytes_not_bytearray( + self, kwargs: dict[str, bool] + ) -> None: + # Regression test for https://github.com/aio-libs/aiohttp/issues/12404 + with Stream(b"Hello, world!\r\n--:") as stream: + d = CIMultiDictProxy[str](CIMultiDict()) + obj = aiohttp.BodyPartReader(BOUNDARY, d, stream) + result = await obj.read(**kwargs) + assert isinstance(result, bytes) + async def test_read_chunk_at_eof(self) -> None: with Stream(b"--:") as stream: d = CIMultiDictProxy[str](CIMultiDict())