[Python-checkins] gh-101819: Prepare to modernize the _io extension (#104178)

vstinner webhook-mailer at python.org
Fri May 5 19:54:02 EDT 2023


https://github.com/python/cpython/commit/c84029179c3287f9c357ccac231fe78469c6f068
commit: c84029179c3287f9c357ccac231fe78469c6f068
branch: main
author: Victor Stinner <vstinner at python.org>
committer: vstinner <vstinner at python.org>
date: 2023-05-06T01:53:55+02:00
summary:

gh-101819: Prepare to modernize the _io extension (#104178)

* Add references to static types to _PyIO_State:

  * PyBufferedIOBase_Type
  * PyBytesIOBuffer_Type
  * PyIncrementalNewlineDecoder_Type
  * PyRawIOBase_Type
  * PyTextIOBase_Type

* Add the defining class to methods:

  * _io.BytesIO.getbuffer()
  * _io.FileIO.close()

* Add get_io_state_by_cls() function.
* Add state parameter to _textiowrapper_decode()
* _io_TextIOWrapper___init__() now sets self->state before calling
  _textiowrapper_set_decoder().

Co-authored-by: Erlend E. Aasland <erlend.aasland at protonmail.com>

files:
M Modules/_io/_iomodule.c
M Modules/_io/_iomodule.h
M Modules/_io/bufferedio.c
M Modules/_io/bytesio.c
M Modules/_io/clinic/bytesio.c.h
M Modules/_io/clinic/fileio.c.h
M Modules/_io/fileio.c
M Modules/_io/stringio.c
M Modules/_io/textio.c

diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c
index 8ec3a6081c98..403968af1b99 100644
--- a/Modules/_io/_iomodule.c
+++ b/Modules/_io/_iomodule.c
@@ -583,13 +583,18 @@ iomodule_traverse(PyObject *mod, visitproc visit, void *arg) {
     Py_VISIT(state->locale_module);
     Py_VISIT(state->unsupported_operation);
 
+    Py_VISIT(state->PyIncrementalNewlineDecoder_Type);
+    Py_VISIT(state->PyRawIOBase_Type);
+    Py_VISIT(state->PyBufferedIOBase_Type);
     Py_VISIT(state->PyBufferedRWPair_Type);
     Py_VISIT(state->PyBufferedRandom_Type);
     Py_VISIT(state->PyBufferedReader_Type);
     Py_VISIT(state->PyBufferedWriter_Type);
+    Py_VISIT(state->PyBytesIOBuffer_Type);
     Py_VISIT(state->PyBytesIO_Type);
     Py_VISIT(state->PyFileIO_Type);
     Py_VISIT(state->PyStringIO_Type);
+    Py_VISIT(state->PyTextIOBase_Type);
     Py_VISIT(state->PyTextIOWrapper_Type);
     return 0;
 }
@@ -604,13 +609,18 @@ iomodule_clear(PyObject *mod) {
         Py_CLEAR(state->locale_module);
     Py_CLEAR(state->unsupported_operation);
 
+    Py_CLEAR(state->PyIncrementalNewlineDecoder_Type);
+    Py_CLEAR(state->PyRawIOBase_Type);
+    Py_CLEAR(state->PyBufferedIOBase_Type);
     Py_CLEAR(state->PyBufferedRWPair_Type);
     Py_CLEAR(state->PyBufferedRandom_Type);
     Py_CLEAR(state->PyBufferedReader_Type);
     Py_CLEAR(state->PyBufferedWriter_Type);
+    Py_CLEAR(state->PyBytesIOBuffer_Type);
     Py_CLEAR(state->PyBytesIO_Type);
     Py_CLEAR(state->PyFileIO_Type);
     Py_CLEAR(state->PyStringIO_Type);
+    Py_CLEAR(state->PyTextIOBase_Type);
     Py_CLEAR(state->PyTextIOWrapper_Type);
     return 0;
 }
@@ -749,24 +759,33 @@ PyInit__io(void)
         }
     }
 
+    // Base classes
+    state->PyIncrementalNewlineDecoder_Type = (PyTypeObject *)Py_NewRef(&PyIncrementalNewlineDecoder_Type);
+
+    // PyIOBase_Type subclasses
+    state->PyRawIOBase_Type = (PyTypeObject *)Py_NewRef(&PyRawIOBase_Type);
+    state->PyBufferedIOBase_Type = (PyTypeObject *)Py_NewRef(&PyBufferedIOBase_Type);
+    state->PyTextIOBase_Type = (PyTypeObject *)Py_NewRef(&PyTextIOBase_Type);
+
     // PyBufferedIOBase_Type(PyIOBase_Type) subclasses
-    ADD_TYPE(m, state->PyBytesIO_Type, &bytesio_spec, &PyBufferedIOBase_Type);
+    ADD_TYPE(m, state->PyBytesIO_Type, &bytesio_spec, state->PyBufferedIOBase_Type);
     ADD_TYPE(m, state->PyBufferedWriter_Type, &bufferedwriter_spec,
-             &PyBufferedIOBase_Type);
+             state->PyBufferedIOBase_Type);
     ADD_TYPE(m, state->PyBufferedReader_Type, &bufferedreader_spec,
-             &PyBufferedIOBase_Type);
+             state->PyBufferedIOBase_Type);
     ADD_TYPE(m, state->PyBufferedRWPair_Type, &bufferedrwpair_spec,
-             &PyBufferedIOBase_Type);
+             state->PyBufferedIOBase_Type);
     ADD_TYPE(m, state->PyBufferedRandom_Type, &bufferedrandom_spec,
-             &PyBufferedIOBase_Type);
+             state->PyBufferedIOBase_Type);
 
     // PyRawIOBase_Type(PyIOBase_Type) subclasses
-    ADD_TYPE(m, state->PyFileIO_Type, &fileio_spec, &PyRawIOBase_Type);
+    state->PyBytesIOBuffer_Type = (PyTypeObject *)Py_NewRef(&_PyBytesIOBuffer_Type);
+    ADD_TYPE(m, state->PyFileIO_Type, &fileio_spec, state->PyRawIOBase_Type);
 
     // PyTextIOBase_Type(PyIOBase_Type) subclasses
-    ADD_TYPE(m, state->PyStringIO_Type, &stringio_spec, &PyTextIOBase_Type);
+    ADD_TYPE(m, state->PyStringIO_Type, &stringio_spec, state->PyTextIOBase_Type);
     ADD_TYPE(m, state->PyTextIOWrapper_Type, &textiowrapper_spec,
-             &PyTextIOBase_Type);
+             state->PyTextIOBase_Type);
 
     state->initialized = 1;
 
diff --git a/Modules/_io/_iomodule.h b/Modules/_io/_iomodule.h
index d7224e56f9a7..8a788fbb8185 100644
--- a/Modules/_io/_iomodule.h
+++ b/Modules/_io/_iomodule.h
@@ -5,6 +5,7 @@
 #include "exports.h"
 
 #include "pycore_moduleobject.h"  // _PyModule_GetState()
+#include "pycore_typeobject.h"    // _PyType_GetModuleState()
 #include "structmember.h"
 
 /* ABCs */
@@ -147,13 +148,18 @@ typedef struct {
     PyObject *unsupported_operation;
 
     /* Types */
+    PyTypeObject *PyIncrementalNewlineDecoder_Type;
+    PyTypeObject *PyRawIOBase_Type;
+    PyTypeObject *PyBufferedIOBase_Type;
     PyTypeObject *PyBufferedRWPair_Type;
     PyTypeObject *PyBufferedRandom_Type;
     PyTypeObject *PyBufferedReader_Type;
     PyTypeObject *PyBufferedWriter_Type;
+    PyTypeObject *PyBytesIOBuffer_Type;
     PyTypeObject *PyBytesIO_Type;
     PyTypeObject *PyFileIO_Type;
     PyTypeObject *PyStringIO_Type;
+    PyTypeObject *PyTextIOBase_Type;
     PyTypeObject *PyTextIOWrapper_Type;
 } _PyIO_State;
 
@@ -168,6 +174,14 @@ get_io_state(PyObject *module)
     return (_PyIO_State *)state;
 }
 
+static inline _PyIO_State *
+get_io_state_by_cls(PyTypeObject *cls)
+{
+    void *state = _PyType_GetModuleState(cls);
+    assert(state != NULL);
+    return (_PyIO_State *)state;
+}
+
 static inline _PyIO_State *
 find_io_state_by_def(PyTypeObject *type)
 {
diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c
index 2c71768be978..723d16b47fef 100644
--- a/Modules/_io/bufferedio.c
+++ b/Modules/_io/bufferedio.c
@@ -2231,7 +2231,7 @@ bufferedrwpair_close(rwpair *self, PyObject *Py_UNUSED(ignored))
     }
     else {
         Py_DECREF(ret);
-    } 
+    }
     ret = _forward_call(self->reader, &_Py_ID(close), NULL);
     if (exc != NULL) {
         _PyErr_ChainExceptions1(exc);
diff --git a/Modules/_io/bytesio.c b/Modules/_io/bytesio.c
index 7e9d28b3b965..9c7a28357987 100644
--- a/Modules/_io/bytesio.c
+++ b/Modules/_io/bytesio.c
@@ -308,14 +308,18 @@ _io_BytesIO_flush_impl(bytesio *self)
 /*[clinic input]
 _io.BytesIO.getbuffer
 
+    cls: defining_class
+    /
+
 Get a read-write view over the contents of the BytesIO object.
 [clinic start generated code]*/
 
 static PyObject *
-_io_BytesIO_getbuffer_impl(bytesio *self)
-/*[clinic end generated code: output=72cd7c6e13aa09ed input=8f738ef615865176]*/
+_io_BytesIO_getbuffer_impl(bytesio *self, PyTypeObject *cls)
+/*[clinic end generated code: output=045091d7ce87fe4e input=0668fbb48f95dffa]*/
 {
-    PyTypeObject *type = &_PyBytesIOBuffer_Type;
+    _PyIO_State *state = get_io_state_by_cls(cls);
+    PyTypeObject *type = state->PyBytesIOBuffer_Type;
     bytesiobuf *buf;
     PyObject *view;
 
diff --git a/Modules/_io/clinic/bytesio.c.h b/Modules/_io/clinic/bytesio.c.h
index 84b58db6c7a7..9550c8728c25 100644
--- a/Modules/_io/clinic/bytesio.c.h
+++ b/Modules/_io/clinic/bytesio.c.h
@@ -87,15 +87,19 @@ PyDoc_STRVAR(_io_BytesIO_getbuffer__doc__,
 "Get a read-write view over the contents of the BytesIO object.");
 
 #define _IO_BYTESIO_GETBUFFER_METHODDEF    \
-    {"getbuffer", (PyCFunction)_io_BytesIO_getbuffer, METH_NOARGS, _io_BytesIO_getbuffer__doc__},
+    {"getbuffer", _PyCFunction_CAST(_io_BytesIO_getbuffer), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _io_BytesIO_getbuffer__doc__},
 
 static PyObject *
-_io_BytesIO_getbuffer_impl(bytesio *self);
+_io_BytesIO_getbuffer_impl(bytesio *self, PyTypeObject *cls);
 
 static PyObject *
-_io_BytesIO_getbuffer(bytesio *self, PyObject *Py_UNUSED(ignored))
+_io_BytesIO_getbuffer(bytesio *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
 {
-    return _io_BytesIO_getbuffer_impl(self);
+    if (nargs) {
+        PyErr_SetString(PyExc_TypeError, "getbuffer() takes no arguments");
+        return NULL;
+    }
+    return _io_BytesIO_getbuffer_impl(self, cls);
 }
 
 PyDoc_STRVAR(_io_BytesIO_getvalue__doc__,
@@ -534,4 +538,4 @@ _io_BytesIO___init__(PyObject *self, PyObject *args, PyObject *kwargs)
 exit:
     return return_value;
 }
-/*[clinic end generated code: output=a44770efbaeb80dd input=a9049054013a1b77]*/
+/*[clinic end generated code: output=098584d485420b65 input=a9049054013a1b77]*/
diff --git a/Modules/_io/clinic/fileio.c.h b/Modules/_io/clinic/fileio.c.h
index b6e9bd5b65a0..dfad8a58c472 100644
--- a/Modules/_io/clinic/fileio.c.h
+++ b/Modules/_io/clinic/fileio.c.h
@@ -18,15 +18,19 @@ PyDoc_STRVAR(_io_FileIO_close__doc__,
 "called more than once without error.");
 
 #define _IO_FILEIO_CLOSE_METHODDEF    \
-    {"close", (PyCFunction)_io_FileIO_close, METH_NOARGS, _io_FileIO_close__doc__},
+    {"close", _PyCFunction_CAST(_io_FileIO_close), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _io_FileIO_close__doc__},
 
 static PyObject *
-_io_FileIO_close_impl(fileio *self);
+_io_FileIO_close_impl(fileio *self, PyTypeObject *cls);
 
 static PyObject *
-_io_FileIO_close(fileio *self, PyObject *Py_UNUSED(ignored))
+_io_FileIO_close(fileio *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
 {
-    return _io_FileIO_close_impl(self);
+    if (nargs) {
+        PyErr_SetString(PyExc_TypeError, "close() takes no arguments");
+        return NULL;
+    }
+    return _io_FileIO_close_impl(self, cls);
 }
 
 PyDoc_STRVAR(_io_FileIO___init____doc__,
@@ -466,4 +470,4 @@ _io_FileIO_isatty(fileio *self, PyObject *Py_UNUSED(ignored))
 #ifndef _IO_FILEIO_TRUNCATE_METHODDEF
     #define _IO_FILEIO_TRUNCATE_METHODDEF
 #endif /* !defined(_IO_FILEIO_TRUNCATE_METHODDEF) */
-/*[clinic end generated code: output=27f883807a6c29ae input=a9049054013a1b77]*/
+/*[clinic end generated code: output=29ed2ae6c451c139 input=a9049054013a1b77]*/
diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c
index 1118d86e6c9a..cc0e7307b9da 100644
--- a/Modules/_io/fileio.c
+++ b/Modules/_io/fileio.c
@@ -130,6 +130,9 @@ internal_close(fileio *self)
 /*[clinic input]
 _io.FileIO.close
 
+    cls: defining_class
+    /
+
 Close the file.
 
 A closed file cannot be used for further I/O operations.  close() may be
@@ -137,18 +140,20 @@ called more than once without error.
 [clinic start generated code]*/
 
 static PyObject *
-_io_FileIO_close_impl(fileio *self)
-/*[clinic end generated code: output=7737a319ef3bad0b input=f35231760d54a522]*/
+_io_FileIO_close_impl(fileio *self, PyTypeObject *cls)
+/*[clinic end generated code: output=c30cbe9d1f23ca58 input=70da49e63db7c64d]*/
 {
     PyObject *res;
-    PyObject *exc;
     int rc;
-    res = PyObject_CallMethodOneArg((PyObject*)&PyRawIOBase_Type,
+    _PyIO_State *state = get_io_state_by_cls(cls);
+    res = PyObject_CallMethodOneArg((PyObject*)state->PyRawIOBase_Type,
                                      &_Py_ID(close), (PyObject *)self);
     if (!self->closefd) {
         self->fd = -1;
         return res;
     }
+
+    PyObject *exc;
     if (res == NULL) {
         exc = PyErr_GetRaisedException();
     }
diff --git a/Modules/_io/stringio.c b/Modules/_io/stringio.c
index 54c050f0be46..13d3b870b39a 100644
--- a/Modules/_io/stringio.c
+++ b/Modules/_io/stringio.c
@@ -716,9 +716,10 @@ _io_StringIO___init___impl(stringio *self, PyObject *value,
         self->writenl = Py_NewRef(self->readnl);
     }
 
+    _PyIO_State *module_state = find_io_state_by_def(Py_TYPE(self));
     if (self->readuniversal) {
         self->decoder = PyObject_CallFunctionObjArgs(
-            (PyObject *)&PyIncrementalNewlineDecoder_Type,
+            (PyObject *)module_state->PyIncrementalNewlineDecoder_Type,
             Py_None, self->readtranslate ? Py_True : Py_False, NULL);
         if (self->decoder == NULL)
             return -1;
@@ -750,7 +751,7 @@ _io_StringIO___init___impl(stringio *self, PyObject *value,
         self->state = STATE_ACCUMULATING;
     }
     self->pos = 0;
-    self->module_state = find_io_state_by_def(Py_TYPE(self));
+    self->module_state = module_state;
     self->closed = 0;
     self->ok = 1;
     return 0;
diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c
index 3ff84cb623af..2dba382f4f8f 100644
--- a/Modules/_io/textio.c
+++ b/Modules/_io/textio.c
@@ -18,10 +18,10 @@
 
 /*[clinic input]
 module _io
-class _io.IncrementalNewlineDecoder "nldecoder_object *" "&PyIncrementalNewlineDecoder_Type"
+class _io.IncrementalNewlineDecoder "nldecoder_object *" "clinic_state()->PyIncrementalNewlineDecoder_Type"
 class _io.TextIOWrapper "textio *" "clinic_state()->TextIOWrapper_Type"
 [clinic start generated code]*/
-/*[clinic end generated code: output=da39a3ee5e6b4b0d input=d3f032e90f74c8f2]*/
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=81f67cf54eaa6001]*/
 
 /* TextIOBase */
 
@@ -872,8 +872,9 @@ _textiowrapper_set_decoder(textio *self, PyObject *codec_info,
         return -1;
 
     if (self->readuniversal) {
+        _PyIO_State *state = self->state;
         PyObject *incrementalDecoder = PyObject_CallFunctionObjArgs(
-            (PyObject *)&PyIncrementalNewlineDecoder_Type,
+            (PyObject *)state->PyIncrementalNewlineDecoder_Type,
             self->decoder, self->readtranslate ? Py_True : Py_False, NULL);
         if (incrementalDecoder == NULL)
             return -1;
@@ -884,11 +885,12 @@ _textiowrapper_set_decoder(textio *self, PyObject *codec_info,
 }
 
 static PyObject*
-_textiowrapper_decode(PyObject *decoder, PyObject *bytes, int eof)
+_textiowrapper_decode(_PyIO_State *state, PyObject *decoder, PyObject *bytes,
+                      int eof)
 {
     PyObject *chars;
 
-    if (Py_IS_TYPE(decoder, &PyIncrementalNewlineDecoder_Type))
+    if (Py_IS_TYPE(decoder, state->PyIncrementalNewlineDecoder_Type))
         chars = _PyIncrementalNewlineDecoder_decode(decoder, bytes, eof);
     else
         chars = PyObject_CallMethodObjArgs(decoder, &_Py_ID(decode), bytes,
@@ -1167,6 +1169,8 @@ _io_TextIOWrapper___init___impl(textio *self, PyObject *buffer,
     self->buffer = Py_NewRef(buffer);
 
     /* Build the decoder object */
+    _PyIO_State *state = find_io_state_by_def(Py_TYPE(self));
+    self->state = state;
     if (_textiowrapper_set_decoder(self, codec_info, PyUnicode_AsUTF8(errors)) != 0)
         goto error;
 
@@ -1177,7 +1181,6 @@ _io_TextIOWrapper___init___impl(textio *self, PyObject *buffer,
     /* Finished sorting out the codec details */
     Py_CLEAR(codec_info);
 
-    _PyIO_State *state = find_io_state_by_def(Py_TYPE(self));
     if (Py_IS_TYPE(buffer, state->PyBufferedReader_Type) ||
         Py_IS_TYPE(buffer, state->PyBufferedWriter_Type) ||
         Py_IS_TYPE(buffer, state->PyBufferedRandom_Type))
@@ -1214,7 +1217,6 @@ _io_TextIOWrapper___init___impl(textio *self, PyObject *buffer,
         goto error;
     }
 
-    self->state = state;
     self->ok = 1;
     return 0;
 
@@ -1843,7 +1845,8 @@ textiowrapper_read_chunk(textio *self, Py_ssize_t size_hint)
     nbytes = input_chunk_buf.len;
     eof = (nbytes == 0);
 
-    decoded_chars = _textiowrapper_decode(self->decoder, input_chunk, eof);
+    decoded_chars = _textiowrapper_decode(self->state, self->decoder,
+                                          input_chunk, eof);
     PyBuffer_Release(&input_chunk_buf);
     if (decoded_chars == NULL)
         goto fail;
@@ -1913,7 +1916,8 @@ _io_TextIOWrapper_read_impl(textio *self, Py_ssize_t n)
         if (bytes == NULL)
             goto fail;
 
-        if (Py_IS_TYPE(self->decoder, &PyIncrementalNewlineDecoder_Type))
+        _PyIO_State *state = self->state;
+        if (Py_IS_TYPE(self->decoder, state->PyIncrementalNewlineDecoder_Type))
             decoded = _PyIncrementalNewlineDecoder_decode(self->decoder,
                                                           bytes, 1);
         else



More information about the Python-checkins mailing list