bpo-29741: Clean up C implementations of BytesIO and StringIO. (#606)

Some BytesIO methods now accept not just int subclasses but other int-like types.
diff --git a/Modules/_io/bytesio.c b/Modules/_io/bytesio.c
index 96be0f4..0a0e5e7 100644
--- a/Modules/_io/bytesio.c
+++ b/Modules/_io/bytesio.c
@@ -8,6 +8,13 @@
 [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 @@
 
 /*[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 @@
 [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 @@
 
 /*[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 @@
 [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 @@
 [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 @@
     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 656e7ec..60b7eaa 100644
--- a/Modules/_io/clinic/bytesio.c.h
+++ b/Modules/_io/clinic/bytesio.c.h
@@ -149,7 +149,7 @@
 }
 
 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 @@
     {"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 @@
     {"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 @@
 }
 
 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 @@
     {"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 @@
 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 af467d6..362d0b9 100644
--- a/Modules/_io/clinic/stringio.c.h
+++ b/Modules/_io/clinic/stringio.c.h
@@ -39,7 +39,7 @@
 }
 
 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 @@
     {"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 @@
     {"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 size);
 
 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 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_StringIO_readline_impl(self, arg);
+    return_value = _io_StringIO_readline_impl(self, size);
 
 exit:
     return return_value;
@@ -305,4 +303,4 @@
 {
     return _io_StringIO_seekable_impl(self);
 }
-/*[clinic end generated code: output=ce8018ec29def422 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=965fe9cb0d11511a input=a9049054013a1b77]*/
diff --git a/Modules/_io/stringio.c b/Modules/_io/stringio.c
index 93c8b47..788dcb1 100644
--- a/Modules/_io/stringio.c
+++ b/Modules/_io/stringio.c
@@ -17,6 +17,13 @@
 [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 @@
 
 /*[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 @@
 [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 @@
 
 /*[clinic input]
 _io.StringIO.readline
-    size as arg: object = None
+    size: io_ssize_t = -1
     /
 
 Read until newline or EOF.
@@ -397,26 +389,14 @@
 [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 size)
+/*[clinic end generated code: output=cabd6452f1b7e85d input=04de7535f732cb3d]*/
 {
-    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);
+    return _stringio_readline(self, size);
 }
 
 static PyObject *