Skip to content

Commit a833e2b

Browse files
fix: ensure BodyPartReader.read() returns bytes, not bytearray
BodyPartReader.read() accumulated data in a bytearray buffer and returned it directly, violating the documented return type of bytes. Both the plain and decode=True code paths were affected. Convert the internal bytearray to bytes before returning so the API contract matches the documentation and type hints. Fixes #12404
1 parent b0601d6 commit a833e2b

3 files changed

Lines changed: 24 additions & 2 deletions

File tree

CHANGES/12404.bugfix.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fixed ``BodyPartReader.read()`` and ``BodyPartReader.read(decode=True)`` returning
2+
``bytearray`` instead of ``bytes``, violating the documented API contract.
3+
-- by :user:`CrepuscularIRIS`.

aiohttp/multipart.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -322,8 +322,8 @@ async def read(self, *, decode: bool = False) -> bytes:
322322
decoded_data.extend(d)
323323
if len(decoded_data) > self._client_max_size:
324324
raise self._max_size_error_cls(self._client_max_size)
325-
return decoded_data
326-
return data
325+
return bytes(decoded_data)
326+
return bytes(data)
327327

328328
async def read_chunk(self, size: int = chunk_size) -> bytes:
329329
"""Reads body part content chunk of the specified size.

tests/test_multipart.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,25 @@ async def test_read(self) -> None:
185185
assert b"Hello, world!" == result
186186
assert obj.at_eof()
187187

188+
async def test_read_returns_bytes_not_bytearray(self) -> None:
189+
# Regression test for https://github.com/aio-libs/aiohttp/issues/12404
190+
# read() must return bytes (not bytearray) to honour the documented API contract.
191+
with Stream(b"Hello, world!\r\n--:") as stream:
192+
d = CIMultiDictProxy[str](CIMultiDict())
193+
obj = aiohttp.BodyPartReader(BOUNDARY, d, stream)
194+
result = await obj.read()
195+
assert isinstance(result, bytes), f"Expected bytes, got {type(result).__name__}"
196+
197+
async def test_read_decode_returns_bytes_not_bytearray(self) -> None:
198+
# Regression test for https://github.com/aio-libs/aiohttp/issues/12404
199+
# read(decode=True) must return bytes (not bytearray) even when no
200+
# Content-Transfer-Encoding / Content-Encoding header is present.
201+
with Stream(b"Hello, world!\r\n--:") as stream:
202+
d = CIMultiDictProxy[str](CIMultiDict())
203+
obj = aiohttp.BodyPartReader(BOUNDARY, d, stream)
204+
result = await obj.read(decode=True)
205+
assert isinstance(result, bytes), f"Expected bytes, got {type(result).__name__}"
206+
188207
async def test_read_chunk_at_eof(self) -> None:
189208
with Stream(b"--:") as stream:
190209
d = CIMultiDictProxy[str](CIMultiDict())

0 commit comments

Comments
 (0)