Skip to content
Open
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
14 changes: 12 additions & 2 deletions Lib/_pyio.py
Original file line number Diff line number Diff line change
Expand Up @@ -1003,9 +1003,19 @@ def tell(self):
def peek(self, size=0):
if self.closed:
raise ValueError("peek on closed file")
try:
size_index = size.__index__

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

that the size is ignored previously is actually intentional, that's the behavior in the C as well:

/*[clinic input]
@critical_section
_io._Buffered.peek
size: Py_ssize_t = 0
/
[clinic start generated code]*/
static PyObject *
_io__Buffered_peek_impl(buffered *self, Py_ssize_t size)
/*[clinic end generated code: output=ba7a097ca230102b input=56733376f926d982]*/
{
PyObject *res = NULL;
CHECK_INITIALIZED(self)
CHECK_CLOSED(self, "peek of closed file")
if (!ENTER_BUFFERED(self))
return NULL;
if (self->writable) {
res = buffered_flush_and_rewind_unlocked(self);
if (res == NULL)
goto end;
Py_CLEAR(res);
}
res = _bufferedreader_peek_unlocked(self);
end:
LEAVE_BUFFERED(self)
return res;
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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.

except AttributeError:
raise TypeError(f"{size!r} is not an integer")
else:
size = size_index()

if size < 1:
return self._buffer[self._pos:self._pos + io.DEFAULT_BUFFER_SIZE]
return self._buffer[self._pos:self._pos + size]
size = io.DEFAULT_BUFFER_SIZE

with self._lock:
b = self._buffer[self._pos:self._pos + size]
return b.take_bytes()

def truncate(self, pos=None):
if self.closed:
Expand Down
5 changes: 5 additions & 0 deletions Lib/test/test_io/test_memoryio.py
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,11 @@ def test_peek(self):
buf = self.buftype("1234567890")
with self.ioclass(buf) as memio:
self.assertEqual(memio.tell(), 0)
# bytearray(b'1') == b'1', so the type has to be asserted separately.
self.assertIsInstance(memio.peek(), bytes)
self.assertIsInstance(memio.peek(1), bytes)
self.assertEqual(memio.peek(IntLike(3)), buf[:3])
self.assertRaises(TypeError, memio.peek, 1.5)
self.assertEqual(memio.peek(1), buf[:1])
self.assertEqual(memio.peek(1), buf[:1])
self.assertEqual(memio.peek(), buf)
Expand Down
Loading