Uh oh!
There was an error while loading. Please reload this page.
gh-155389: Return bytes from _pyio.BytesIO.peek() - #155390
Conversation
peek() returned a slice of the internal bytearray, where read() converts with take_bytes(). It also did not coerce its size through __index__ and did not hold the lock while slicing, both of which read() and the C implementation do.
cmaloney
commented
Aug 8, 2026
| size = size_index() | ||
| with self._lock: | ||
| if size < 1: |
There was a problem hiding this comment.
I don't think the size piece here needs to be in the lock
There was a problem hiding this comment.
Done, thanks. Only the slicing is under the lock now.
| return self._buffer[self._pos:self._pos + io.DEFAULT_BUFFER_SIZE] | ||
| return self._buffer[self._pos:self._pos + size] | ||
| try: | ||
| size_index = size.__index__ |
There was a problem hiding this comment.
that the size is ignored previously is actually intentional, that's the behavior in the C as well:
cpython/Modules/_io/bufferedio.c
Lines 951 to 982 in 998b890
There was a problem hiding this comment.
I think that one is _io._Buffered.peek (BufferedReader), which does ignore size. BytesIO.peek is in bytesio.c and uses it: size < 1 becomes DEFAULT_BUFFER_SIZE, then it clamps to what is left in the buffer.
io.BytesIO.peek(2) -> b'ab'
BufferedReader.peek(2) -> b'bcdef'
So the Python fallback should honour it too, which is what the __index__ coercion is for.
aisk
commented
Aug 8, 2026
It's better to add a test to check if |
The size normalisation touches no shared state, so it does not need the lock.
_pyio.BytesIO.peek()returned a slice of the internalbytearray, so it gave abytearraywhere the C version and the docs saybytes. It also did not accept an__index__-able size, and did not take the lock while slicing.It now does what
read()does: coerce the size, take the lock, convert withtake_bytes().test_peekonly usedassertEqual, andbytearray(b'1') == b'1', so it passed either way. It now checks the type and an__index__size, and fails without the change.BytesIO.peek()is new in 3.16, so there is no NEWS entry._pyio.BytesIO.peek()returns bytearray instead of bytes #155389