Skip to content

bpo-29741: make some BytesIO methods accept integer types #560

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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
52 changes: 38 additions & 14 deletions Lib/_pyio.py
Original file line number Diff line number Diff line change
Expand Up @@ -504,8 +504,13 @@ def nreadahead():
return 1
if size is None:
size = -1
elif not isinstance(size, int):
raise TypeError("size must be an integer")
else:
try:
size_index = size.__index__
except AttributeError:
raise TypeError(f"{size!r} is not an integer")
Copy link
Contributor Author

Choose a reason for hiding this comment

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

hope you are OK with this phrasing.. I chose it because even though 'int-like'
is intuitive, it seems that 'integer' is much more common in the docs and in
error messages (including in _pyio.py).

else:
size = size_index()
res = bytearray()
while size < 0 or len(res) < size:
b = self.read(nreadahead())
Expand Down Expand Up @@ -868,6 +873,13 @@ def read(self, size=-1):
raise ValueError("read from closed file")
if size is None:
size = -1
else:
try:
size_index = size.__index__
except AttributeError:
raise TypeError(f"{size!r} is not an integer")
else:
size = size_index()
if size < 0:
size = len(self._buffer)
if len(self._buffer) <= self._pos:
Expand Down Expand Up @@ -905,9 +917,11 @@ def seek(self, pos, whence=0):
if self.closed:
raise ValueError("seek on closed file")
try:
pos.__index__
except AttributeError as err:
raise TypeError("an integer is required") from err
pos_index = pos.__index__
except AttributeError:
raise TypeError(f"{pos!r} is not an integer")
else:
pos = pos_index()
if whence == 0:
if pos < 0:
raise ValueError("negative seek position %r" % (pos,))
Expand All @@ -932,9 +946,11 @@ def truncate(self, pos=None):
pos = self._pos
else:
try:
pos.__index__
except AttributeError as err:
raise TypeError("an integer is required") from err
pos_index = pos.__index__
except AttributeError:
raise TypeError(f"{pos!r} is not an integer")
else:
pos = pos_index()
if pos < 0:
raise ValueError("negative truncate position %r" % (pos,))
del self._buffer[pos:]
Expand Down Expand Up @@ -2378,11 +2394,14 @@ def read(self, size=None):
self._checkReadable()
if size is None:
size = -1
else:
try:
size_index = size.__index__
except AttributeError:
raise TypeError(f"{size!r} is not an integer")
else:
size = size_index()
decoder = self._decoder or self._get_decoder()
try:
size.__index__
except AttributeError as err:
raise TypeError("an integer is required") from err
if size < 0:
# Read everything.
result = (self._get_decoded_chars() +
Expand Down Expand Up @@ -2413,8 +2432,13 @@ def readline(self, size=None):
raise ValueError("read from closed file")
if size is None:
size = -1
elif not isinstance(size, int):
raise TypeError("size must be an integer")
else:
try:
size_index = size.__index__
except AttributeError:
raise TypeError(f"{size!r} is not an integer")
else:
size = size_index()

# Grab all the decoded text (we will rewind any extra bits later).
line = self._get_decoded_chars()
Expand Down
28 changes: 28 additions & 0 deletions Lib/test/test_memoryio.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@
import pickle
import sys

class IntLike:
def __init__(self, num):
self._num = num
def __index__(self):
return self._num
__int__ = __index__

class MemorySeekTestMixin:

def testInit(self):
Expand Down Expand Up @@ -116,7 +123,10 @@ def test_truncate(self):
memio = self.ioclass(buf)

self.assertRaises(ValueError, memio.truncate, -1)
self.assertRaises(ValueError, memio.truncate, IntLike(-1))
memio.seek(6)
self.assertEqual(memio.truncate(IntLike(8)), 8)
self.assertEqual(memio.getvalue(), buf[:8])
self.assertEqual(memio.truncate(), 6)
self.assertEqual(memio.getvalue(), buf[:6])
self.assertEqual(memio.truncate(4), 4)
Expand All @@ -131,6 +141,7 @@ def test_truncate(self):
self.assertRaises(TypeError, memio.truncate, '0')
memio.close()
self.assertRaises(ValueError, memio.truncate, 0)
self.assertRaises(ValueError, memio.truncate, IntLike(0))

def test_init(self):
buf = self.buftype("1234567890")
Expand All @@ -154,12 +165,19 @@ def test_read(self):
self.assertEqual(memio.read(900), buf[5:])
self.assertEqual(memio.read(), self.EOF)
memio.seek(0)
self.assertEqual(memio.read(IntLike(0)), self.EOF)
self.assertEqual(memio.read(IntLike(1)), buf[:1])
self.assertEqual(memio.read(IntLike(4)), buf[1:5])
self.assertEqual(memio.read(IntLike(900)), buf[5:])
memio.seek(0)
self.assertEqual(memio.read(), buf)
self.assertEqual(memio.read(), self.EOF)
self.assertEqual(memio.tell(), 10)
memio.seek(0)
self.assertEqual(memio.read(-1), buf)
memio.seek(0)
self.assertEqual(memio.read(IntLike(-1)), buf)
memio.seek(0)
self.assertEqual(type(memio.read()), type(buf))
memio.seek(100)
self.assertEqual(type(memio.read()), type(buf))
Expand All @@ -169,6 +187,8 @@ def test_read(self):
memio.seek(len(buf) + 1)
self.assertEqual(memio.read(1), self.EOF)
memio.seek(len(buf) + 1)
self.assertEqual(memio.read(IntLike(1)), self.EOF)
memio.seek(len(buf) + 1)
self.assertEqual(memio.read(), self.EOF)
memio.close()
self.assertRaises(ValueError, memio.read)
Expand All @@ -178,6 +198,7 @@ def test_readline(self):
memio = self.ioclass(buf * 2)

self.assertEqual(memio.readline(0), self.EOF)
self.assertEqual(memio.readline(IntLike(0)), self.EOF)
self.assertEqual(memio.readline(), buf)
self.assertEqual(memio.readline(), buf)
self.assertEqual(memio.readline(), self.EOF)
Expand All @@ -186,9 +207,16 @@ def test_readline(self):
self.assertEqual(memio.readline(5), buf[5:10])
self.assertEqual(memio.readline(5), buf[10:15])
memio.seek(0)
self.assertEqual(memio.readline(IntLike(5)), buf[:5])
self.assertEqual(memio.readline(IntLike(5)), buf[5:10])
self.assertEqual(memio.readline(IntLike(5)), buf[10:15])
memio.seek(0)
self.assertEqual(memio.readline(-1), buf)
memio.seek(0)
self.assertEqual(memio.readline(IntLike(-1)), buf)
memio.seek(0)
self.assertEqual(memio.readline(0), self.EOF)
self.assertEqual(memio.readline(IntLike(0)), self.EOF)
# Issue #24989: Buffer overread
memio.seek(len(buf) * 2 + 1)
self.assertEqual(memio.readline(), self.EOF)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Update some methods in the _pyio module to also accept integer types. Patch
by Oren Milman.