diff --git a/Lib/_pyio.py b/Lib/_pyio.py index 8f9397660b..e44ce499aa 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -504,8 +504,11 @@ class IOBase(metaclass=abc.ABCMeta): return 1 if size is None: size = -1 - elif not isinstance(size, int): - raise TypeError("size must be an integer") + else: + try: + size = size.__index__() + except AttributeError as err: + raise TypeError("an integer is required") from err res = bytearray() while size < 0 or len(res) < size: b = self.read(nreadahead()) @@ -868,6 +871,11 @@ class BytesIO(BufferedIOBase): raise ValueError("read from closed file") if size is None: size = -1 + else: + try: + size = size.__index__() + except AttributeError as err: + raise TypeError("an integer is required") from err if size < 0: size = len(self._buffer) if len(self._buffer) <= self._pos: @@ -905,7 +913,7 @@ class BytesIO(BufferedIOBase): if self.closed: raise ValueError("seek on closed file") try: - pos.__index__ + pos = pos.__index__() except AttributeError as err: raise TypeError("an integer is required") from err if whence == 0: @@ -932,7 +940,7 @@ class BytesIO(BufferedIOBase): pos = self._pos else: try: - pos.__index__ + pos = pos.__index__() except AttributeError as err: raise TypeError("an integer is required") from err if pos < 0: @@ -2357,11 +2365,12 @@ class TextIOWrapper(TextIOBase): self._checkReadable() if size is None: size = -1 + else: + try: + size = size.__index__() + except AttributeError as err: + raise TypeError("an integer is required") from err 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() + @@ -2392,8 +2401,11 @@ class TextIOWrapper(TextIOBase): 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 = size.__index__() + except AttributeError as err: + raise TypeError("an integer is required") from err # Grab all the decoded text (we will rewind any extra bits later). line = self._get_decoded_chars() diff --git a/Lib/test/test_memoryio.py b/Lib/test/test_memoryio.py index 80055ce1e7..f8c3b08ee1 100644 --- a/Lib/test/test_memoryio.py +++ b/Lib/test/test_memoryio.py @@ -11,6 +11,13 @@ import _pyio as pyio 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): @@ -116,7 +123,10 @@ class MemoryTestMixin: 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) @@ -131,6 +141,7 @@ class MemoryTestMixin: 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") @@ -154,12 +165,19 @@ class MemoryTestMixin: 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)) @@ -169,6 +187,8 @@ class MemoryTestMixin: 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) @@ -178,6 +198,7 @@ class MemoryTestMixin: 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) @@ -186,9 +207,16 @@ class MemoryTestMixin: 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) diff --git a/Modules/_io/bytesio.c b/Modules/_io/bytesio.c index 96be0f4541..0a0e5e72d7 100644 --- a/Modules/_io/bytesio.c +++ b/Modules/_io/bytesio.c @@ -8,6 +8,13 @@ class _io.BytesIO "bytesio *" "&PyBytesIO_Type" [clinic start generated code]*/ /*[clinic end generated code: output=da39a3ee5e6b4b0d input=7f50ec034f5c0b26]*/ +/*[python input] +class io_ssize_t_converter(CConverter): + type = 'Py_ssize_t' + converter = '_PyIO_ConvertSsize_t' +[python start generated code]*/ +/*[python end generated code: output=da39a3ee5e6b4b0d input=d0a811d3cbfd1b33]*/ + typedef struct { PyObject_HEAD PyObject *buf; @@ -374,7 +381,7 @@ read_bytes(bytesio *self, Py_ssize_t size) /*[clinic input] _io.BytesIO.read - size as arg: object = None + size: io_ssize_t = -1 / Read at most size bytes, returned as a bytes object. @@ -384,28 +391,13 @@ Return an empty bytes object at EOF. [clinic start generated code]*/ static PyObject * -_io_BytesIO_read_impl(bytesio *self, PyObject *arg) -/*[clinic end generated code: output=85dacb535c1e1781 input=cc7ba4a797bb1555]*/ +_io_BytesIO_read_impl(bytesio *self, Py_ssize_t size) +/*[clinic end generated code: output=9cc025f21c75bdd2 input=c81ec53b8f2cc3cf]*/ { - Py_ssize_t size, n; + Py_ssize_t n; CHECK_CLOSED(self); - if (PyLong_Check(arg)) { - size = PyLong_AsSsize_t(arg); - if (size == -1 && PyErr_Occurred()) - return NULL; - } - else if (arg == Py_None) { - /* Read until EOF is reached, by default. */ - size = -1; - } - else { - PyErr_Format(PyExc_TypeError, "integer argument expected, got '%s'", - Py_TYPE(arg)->tp_name); - return NULL; - } - /* adjust invalid sizes */ n = self->string_size - self->pos; if (size < 0 || size > n) { @@ -420,7 +412,7 @@ _io_BytesIO_read_impl(bytesio *self, PyObject *arg) /*[clinic input] _io.BytesIO.read1 - size: object(c_default="Py_None") = -1 + size: io_ssize_t = -1 / Read at most size bytes, returned as a bytes object. @@ -430,15 +422,15 @@ Return an empty bytes object at EOF. [clinic start generated code]*/ static PyObject * -_io_BytesIO_read1_impl(bytesio *self, PyObject *size) -/*[clinic end generated code: output=a60d80c84c81a6b8 input=0951874bafee8e80]*/ +_io_BytesIO_read1_impl(bytesio *self, Py_ssize_t size) +/*[clinic end generated code: output=d0f843285aa95f1c input=67cf18b142111664]*/ { return _io_BytesIO_read_impl(self, size); } /*[clinic input] _io.BytesIO.readline - size as arg: object = None + size: io_ssize_t = -1 / Next line from the file, as a bytes object. @@ -449,28 +441,13 @@ Return an empty bytes object at EOF. [clinic start generated code]*/ static PyObject * -_io_BytesIO_readline_impl(bytesio *self, PyObject *arg) -/*[clinic end generated code: output=1c2115534a4f9276 input=ca31f06de6eab257]*/ +_io_BytesIO_readline_impl(bytesio *self, Py_ssize_t size) +/*[clinic end generated code: output=4bff3c251df8ffcd input=7c95bd3f9e9d1646]*/ { - Py_ssize_t size, n; + Py_ssize_t n; CHECK_CLOSED(self); - if (PyLong_Check(arg)) { - size = PyLong_AsSsize_t(arg); - if (size == -1 && PyErr_Occurred()) - return NULL; - } - else if (arg == Py_None) { - /* No size limit, by default. */ - size = -1; - } - else { - PyErr_Format(PyExc_TypeError, "integer argument expected, got '%s'", - Py_TYPE(arg)->tp_name); - return NULL; - } - n = scan_eol(self, size); return read_bytes(self, n); @@ -597,19 +574,15 @@ _io_BytesIO_truncate_impl(bytesio *self, PyObject *arg) CHECK_CLOSED(self); CHECK_EXPORTS(self); - if (PyLong_Check(arg)) { - size = PyLong_AsSsize_t(arg); - if (size == -1 && PyErr_Occurred()) - return NULL; - } - else if (arg == Py_None) { + if (arg == Py_None) { /* Truncate to current position if no argument is passed. */ size = self->pos; } else { - PyErr_Format(PyExc_TypeError, "integer argument expected, got '%s'", - Py_TYPE(arg)->tp_name); - return NULL; + size = PyNumber_AsSsize_t(arg, PyExc_OverflowError); + if (size == -1 && PyErr_Occurred()) { + return NULL; + } } if (size < 0) { diff --git a/Modules/_io/clinic/bytesio.c.h b/Modules/_io/clinic/bytesio.c.h index 656e7ecd13..60b7eaadd0 100644 --- a/Modules/_io/clinic/bytesio.c.h +++ b/Modules/_io/clinic/bytesio.c.h @@ -149,7 +149,7 @@ _io_BytesIO_tell(bytesio *self, PyObject *Py_UNUSED(ignored)) } PyDoc_STRVAR(_io_BytesIO_read__doc__, -"read($self, size=None, /)\n" +"read($self, size=-1, /)\n" "--\n" "\n" "Read at most size bytes, returned as a bytes object.\n" @@ -161,24 +161,23 @@ PyDoc_STRVAR(_io_BytesIO_read__doc__, {"read", (PyCFunction)_io_BytesIO_read, METH_FASTCALL, _io_BytesIO_read__doc__}, static PyObject * -_io_BytesIO_read_impl(bytesio *self, PyObject *arg); +_io_BytesIO_read_impl(bytesio *self, Py_ssize_t size); static PyObject * _io_BytesIO_read(bytesio *self, PyObject **args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; - PyObject *arg = Py_None; + Py_ssize_t size = -1; - if (!_PyArg_UnpackStack(args, nargs, "read", - 0, 1, - &arg)) { + if (!_PyArg_ParseStack(args, nargs, "|O&:read", + _PyIO_ConvertSsize_t, &size)) { goto exit; } if (!_PyArg_NoStackKeywords("read", kwnames)) { goto exit; } - return_value = _io_BytesIO_read_impl(self, arg); + return_value = _io_BytesIO_read_impl(self, size); exit: return return_value; @@ -197,17 +196,16 @@ PyDoc_STRVAR(_io_BytesIO_read1__doc__, {"read1", (PyCFunction)_io_BytesIO_read1, METH_FASTCALL, _io_BytesIO_read1__doc__}, static PyObject * -_io_BytesIO_read1_impl(bytesio *self, PyObject *size); +_io_BytesIO_read1_impl(bytesio *self, Py_ssize_t size); static PyObject * _io_BytesIO_read1(bytesio *self, PyObject **args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; - PyObject *size = Py_None; + Py_ssize_t size = -1; - if (!_PyArg_UnpackStack(args, nargs, "read1", - 0, 1, - &size)) { + if (!_PyArg_ParseStack(args, nargs, "|O&:read1", + _PyIO_ConvertSsize_t, &size)) { goto exit; } @@ -221,7 +219,7 @@ exit: } PyDoc_STRVAR(_io_BytesIO_readline__doc__, -"readline($self, size=None, /)\n" +"readline($self, size=-1, /)\n" "--\n" "\n" "Next line from the file, as a bytes object.\n" @@ -234,24 +232,23 @@ PyDoc_STRVAR(_io_BytesIO_readline__doc__, {"readline", (PyCFunction)_io_BytesIO_readline, METH_FASTCALL, _io_BytesIO_readline__doc__}, static PyObject * -_io_BytesIO_readline_impl(bytesio *self, PyObject *arg); +_io_BytesIO_readline_impl(bytesio *self, Py_ssize_t size); static PyObject * _io_BytesIO_readline(bytesio *self, PyObject **args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; - PyObject *arg = Py_None; + Py_ssize_t size = -1; - if (!_PyArg_UnpackStack(args, nargs, "readline", - 0, 1, - &arg)) { + if (!_PyArg_ParseStack(args, nargs, "|O&:readline", + _PyIO_ConvertSsize_t, &size)) { goto exit; } if (!_PyArg_NoStackKeywords("readline", kwnames)) { goto exit; } - return_value = _io_BytesIO_readline_impl(self, arg); + return_value = _io_BytesIO_readline_impl(self, size); exit: return return_value; @@ -472,4 +469,4 @@ _io_BytesIO___init__(PyObject *self, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=138ee6ad6951bc84 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=74a856733a5d55b0 input=a9049054013a1b77]*/ diff --git a/Modules/_io/clinic/stringio.c.h b/Modules/_io/clinic/stringio.c.h index af467d687f..60227afe4b 100644 --- a/Modules/_io/clinic/stringio.c.h +++ b/Modules/_io/clinic/stringio.c.h @@ -39,7 +39,7 @@ _io_StringIO_tell(stringio *self, PyObject *Py_UNUSED(ignored)) } PyDoc_STRVAR(_io_StringIO_read__doc__, -"read($self, size=None, /)\n" +"read($self, size=-1, /)\n" "--\n" "\n" "Read at most size characters, returned as a string.\n" @@ -51,31 +51,30 @@ PyDoc_STRVAR(_io_StringIO_read__doc__, {"read", (PyCFunction)_io_StringIO_read, METH_FASTCALL, _io_StringIO_read__doc__}, static PyObject * -_io_StringIO_read_impl(stringio *self, PyObject *arg); +_io_StringIO_read_impl(stringio *self, Py_ssize_t size); static PyObject * _io_StringIO_read(stringio *self, PyObject **args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; - PyObject *arg = Py_None; + Py_ssize_t size = -1; - if (!_PyArg_UnpackStack(args, nargs, "read", - 0, 1, - &arg)) { + if (!_PyArg_ParseStack(args, nargs, "|O&:read", + _PyIO_ConvertSsize_t, &size)) { goto exit; } if (!_PyArg_NoStackKeywords("read", kwnames)) { goto exit; } - return_value = _io_StringIO_read_impl(self, arg); + return_value = _io_StringIO_read_impl(self, size); exit: return return_value; } PyDoc_STRVAR(_io_StringIO_readline__doc__, -"readline($self, size=None, /)\n" +"readline($self, size=-1, /)\n" "--\n" "\n" "Read until newline or EOF.\n" @@ -86,24 +85,23 @@ PyDoc_STRVAR(_io_StringIO_readline__doc__, {"readline", (PyCFunction)_io_StringIO_readline, METH_FASTCALL, _io_StringIO_readline__doc__}, static PyObject * -_io_StringIO_readline_impl(stringio *self, PyObject *arg); +_io_StringIO_readline_impl(stringio *self, Py_ssize_t limit); static PyObject * _io_StringIO_readline(stringio *self, PyObject **args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; - PyObject *arg = Py_None; + Py_ssize_t limit = -1; - if (!_PyArg_UnpackStack(args, nargs, "readline", - 0, 1, - &arg)) { + if (!_PyArg_ParseStack(args, nargs, "|O&:readline", + _PyIO_ConvertSsize_t, &limit)) { goto exit; } if (!_PyArg_NoStackKeywords("readline", kwnames)) { goto exit; } - return_value = _io_StringIO_readline_impl(self, arg); + return_value = _io_StringIO_readline_impl(self, limit); exit: return return_value; @@ -305,4 +303,4 @@ _io_StringIO_seekable(stringio *self, PyObject *Py_UNUSED(ignored)) { return _io_StringIO_seekable_impl(self); } -/*[clinic end generated code: output=ce8018ec29def422 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=39fd76198351aab3 input=a9049054013a1b77]*/ diff --git a/Modules/_io/stringio.c b/Modules/_io/stringio.c index 93c8b47de3..e520b05635 100644 --- a/Modules/_io/stringio.c +++ b/Modules/_io/stringio.c @@ -17,6 +17,13 @@ class _io.StringIO "stringio *" "&PyStringIO_Type" [clinic start generated code]*/ /*[clinic end generated code: output=da39a3ee5e6b4b0d input=c17bc0f42165cd7d]*/ +/*[python input] +class io_ssize_t_converter(CConverter): + type = 'Py_ssize_t' + converter = '_PyIO_ConvertSsize_t' +[python start generated code]*/ +/*[python end generated code: output=da39a3ee5e6b4b0d input=d0a811d3cbfd1b33]*/ + typedef struct { PyObject_HEAD Py_UCS4 *buf; @@ -301,7 +308,7 @@ _io_StringIO_tell_impl(stringio *self) /*[clinic input] _io.StringIO.read - size as arg: object = None + size: io_ssize_t = -1 / Read at most size characters, returned as a string. @@ -311,30 +318,15 @@ is reached. Return an empty string at EOF. [clinic start generated code]*/ static PyObject * -_io_StringIO_read_impl(stringio *self, PyObject *arg) -/*[clinic end generated code: output=3676864773746f68 input=9a319015f6f3965c]*/ +_io_StringIO_read_impl(stringio *self, Py_ssize_t size) +/*[clinic end generated code: output=ae8cf6002f71626c input=bbd84248eb4ab957]*/ { - Py_ssize_t size, n; + Py_ssize_t n; Py_UCS4 *output; CHECK_INITIALIZED(self); CHECK_CLOSED(self); - if (PyNumber_Check(arg)) { - size = PyNumber_AsSsize_t(arg, PyExc_OverflowError); - if (size == -1 && PyErr_Occurred()) - return NULL; - } - else if (arg == Py_None) { - /* Read until EOF is reached, by default. */ - size = -1; - } - else { - PyErr_Format(PyExc_TypeError, "integer argument expected, got '%s'", - Py_TYPE(arg)->tp_name); - return NULL; - } - /* adjust invalid sizes */ n = self->string_size - self->pos; if (size < 0 || size > n) { @@ -388,7 +380,7 @@ _stringio_readline(stringio *self, Py_ssize_t limit) /*[clinic input] _io.StringIO.readline - size as arg: object = None + size as limit: io_ssize_t = -1 / Read until newline or EOF. @@ -397,25 +389,13 @@ Returns an empty string if EOF is hit immediately. [clinic start generated code]*/ static PyObject * -_io_StringIO_readline_impl(stringio *self, PyObject *arg) -/*[clinic end generated code: output=99fdcac03a3dee81 input=e0e0ed4042040176]*/ +_io_StringIO_readline_impl(stringio *self, Py_ssize_t limit) +/*[clinic end generated code: output=6be3c36f6a89f7e3 input=a1b64cd6d94f7abd]*/ { - Py_ssize_t limit = -1; - CHECK_INITIALIZED(self); CHECK_CLOSED(self); ENSURE_REALIZED(self); - if (PyNumber_Check(arg)) { - limit = PyNumber_AsSsize_t(arg, PyExc_OverflowError); - if (limit == -1 && PyErr_Occurred()) - return NULL; - } - else if (arg != Py_None) { - PyErr_Format(PyExc_TypeError, "integer argument expected, got '%s'", - Py_TYPE(arg)->tp_name); - return NULL; - } return _stringio_readline(self, limit); }