]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-101819: Isolate `_io` (#101948)
authorErlend E. Aasland <erlend.aasland@protonmail.com>
Mon, 15 May 2023 09:26:27 +0000 (11:26 +0200)
committerGitHub <noreply@github.com>
Mon, 15 May 2023 09:26:27 +0000 (09:26 +0000)
Co-authored-by: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com>
Co-authored-by: Victor Stinner <vstinner@python.org>
13 files changed:
Lib/test/test_io.py
Misc/NEWS.d/next/Library/2023-03-14-11-20-19.gh-issue-101819.0-h0it.rst [new file with mode: 0644]
Modules/_io/_iomodule.c
Modules/_io/_iomodule.h
Modules/_io/bufferedio.c
Modules/_io/bytesio.c
Modules/_io/fileio.c
Modules/_io/iobase.c
Modules/_io/stringio.c
Modules/_io/textio.c
Modules/_io/winconsoleio.c
Python/pylifecycle.c
Tools/c-analyzer/cpython/globals-to-fix.tsv

index 9bcc92a40c61df0604908d728277e407bc8ffa9d..cc16804fe2182921c699573a54778afefb8a86fe 100644 (file)
@@ -4242,6 +4242,7 @@ class MiscIOTest(unittest.TestCase):
 
     def test_pickling(self):
         # Pickling file objects is forbidden
+        msg = "cannot pickle"
         for kwargs in [
                 {"mode": "w"},
                 {"mode": "wb"},
@@ -4256,8 +4257,10 @@ class MiscIOTest(unittest.TestCase):
             if "b" not in kwargs["mode"]:
                 kwargs["encoding"] = "utf-8"
             for protocol in range(pickle.HIGHEST_PROTOCOL + 1):
-                with self.open(os_helper.TESTFN, **kwargs) as f:
-                    self.assertRaises(TypeError, pickle.dumps, f, protocol)
+                with self.subTest(protocol=protocol, kwargs=kwargs):
+                    with self.open(os_helper.TESTFN, **kwargs) as f:
+                        with self.assertRaisesRegex(TypeError, msg):
+                            pickle.dumps(f, protocol)
 
     @unittest.skipIf(
         support.is_emscripten, "fstat() of a pipe fd is not supported"
diff --git a/Misc/NEWS.d/next/Library/2023-03-14-11-20-19.gh-issue-101819.0-h0it.rst b/Misc/NEWS.d/next/Library/2023-03-14-11-20-19.gh-issue-101819.0-h0it.rst
new file mode 100644 (file)
index 0000000..4a73bbf
--- /dev/null
@@ -0,0 +1,2 @@
+Isolate the :mod:`io` extension module by applying :pep:`687`. Patch by
+Kumar Aditya, Victor Stinner, and Erlend E. Aasland.
index 2457cb124036fe147d11a350a2df9cfc9dbc4059..7b06c1bee5a83287662f9bca5e3e261db564bf86 100644 (file)
@@ -561,25 +561,9 @@ PyNumber_AsOff_t(PyObject *item, PyObject *err)
     return result;
 }
 
-_PyIO_State *
-_PyIO_get_module_state(void)
-{
-    PyObject *mod = PyState_FindModule(&_PyIO_Module);
-    _PyIO_State *state;
-    if (mod == NULL || (state = get_io_state(mod)) == NULL) {
-        PyErr_SetString(PyExc_RuntimeError,
-                        "could not find io module state "
-                        "(interpreter shutdown?)");
-        return NULL;
-    }
-    return state;
-}
-
 static int
 iomodule_traverse(PyObject *mod, visitproc visit, void *arg) {
     _PyIO_State *state = get_io_state(mod);
-    if (!state->initialized)
-        return 0;
     Py_VISIT(state->unsupported_operation);
 
     Py_VISIT(state->PyIOBase_Type);
@@ -606,8 +590,6 @@ iomodule_traverse(PyObject *mod, visitproc visit, void *arg) {
 static int
 iomodule_clear(PyObject *mod) {
     _PyIO_State *state = get_io_state(mod);
-    if (!state->initialized)
-        return 0;
     Py_CLEAR(state->unsupported_operation);
 
     Py_CLEAR(state->PyIOBase_Type);
@@ -652,115 +634,57 @@ static PyMethodDef module_methods[] = {
     {NULL, NULL}
 };
 
-struct PyModuleDef _PyIO_Module = {
-    PyModuleDef_HEAD_INIT,
-    "io",
-    module_doc,
-    sizeof(_PyIO_State),
-    module_methods,
-    NULL,
-    iomodule_traverse,
-    iomodule_clear,
-    (freefunc)iomodule_free,
-};
-
-
-static PyTypeObject* static_types[] = {
-    // Base classes
-    &PyIOBase_Type,
-
-    // PyIOBase_Type subclasses
-    &PyBufferedIOBase_Type,
-    &PyRawIOBase_Type,
-    &PyTextIOBase_Type,
-};
-
-
-PyStatus
-_PyIO_InitTypes(PyInterpreterState *interp)
-{
-    for (size_t i=0; i < Py_ARRAY_LENGTH(static_types); i++) {
-        PyTypeObject *type = static_types[i];
-        if (_PyStaticType_InitBuiltin(interp, type) < 0) {
-            return _PyStatus_ERR("Can't initialize builtin type");
-        }
-    }
-
-    return _PyStatus_OK();
-}
-
-void
-_PyIO_FiniTypes(PyInterpreterState *interp)
-{
-    // Deallocate types in the reverse order to deallocate subclasses before
-    // their base classes.
-    for (Py_ssize_t i=Py_ARRAY_LENGTH(static_types) - 1; i >= 0; i--) {
-        PyTypeObject *type = static_types[i];
-        _PyStaticType_Dealloc(interp, type);
-    }
-}
-
 #define ADD_TYPE(module, type, spec, base)                               \
 do {                                                                     \
     type = (PyTypeObject *)PyType_FromModuleAndSpec(module, spec,        \
                                                     (PyObject *)base);   \
     if (type == NULL) {                                                  \
-        goto fail;                                                       \
+        return -1;                                                       \
     }                                                                    \
     if (PyModule_AddType(module, type) < 0) {                            \
-        goto fail;                                                       \
+        return -1;                                                       \
     }                                                                    \
 } while (0)
 
-PyMODINIT_FUNC
-PyInit__io(void)
+static int
+iomodule_exec(PyObject *m)
 {
-    PyObject *m = PyModule_Create(&_PyIO_Module);
-    _PyIO_State *state = NULL;
-    if (m == NULL)
-        return NULL;
-    state = get_io_state(m);
-    state->initialized = 0;
+    _PyIO_State *state = get_io_state(m);
 
     /* DEFAULT_BUFFER_SIZE */
     if (PyModule_AddIntMacro(m, DEFAULT_BUFFER_SIZE) < 0)
-        goto fail;
+        return -1;
 
     /* UnsupportedOperation inherits from ValueError and OSError */
     state->unsupported_operation = PyObject_CallFunction(
         (PyObject *)&PyType_Type, "s(OO){}",
         "UnsupportedOperation", PyExc_OSError, PyExc_ValueError);
     if (state->unsupported_operation == NULL)
-        goto fail;
+        return -1;
     if (PyModule_AddObjectRef(m, "UnsupportedOperation",
                               state->unsupported_operation) < 0)
     {
-        goto fail;
+        return -1;
     }
 
     /* BlockingIOError, for compatibility */
     if (PyModule_AddObjectRef(m, "BlockingIOError",
                               (PyObject *) PyExc_BlockingIOError) < 0) {
-        goto fail;
-    }
-
-    // Add types
-    for (size_t i=0; i < Py_ARRAY_LENGTH(static_types); i++) {
-        PyTypeObject *type = static_types[i];
-        if (PyModule_AddType(m, type) < 0) {
-            goto fail;
-        }
+        return -1;
     }
 
     // Base classes
-    state->PyIOBase_Type = (PyTypeObject *)Py_NewRef(&PyIOBase_Type);
     ADD_TYPE(m, state->PyIncrementalNewlineDecoder_Type, &nldecoder_spec, NULL);
     ADD_TYPE(m, state->PyBytesIOBuffer_Type, &bytesiobuf_spec, NULL);
+    ADD_TYPE(m, state->PyIOBase_Type, &iobase_spec, NULL);
 
     // 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);
+    ADD_TYPE(m, state->PyTextIOBase_Type, &textiobase_spec,
+             state->PyIOBase_Type);
+    ADD_TYPE(m, state->PyBufferedIOBase_Type, &bufferediobase_spec,
+             state->PyIOBase_Type);
+    ADD_TYPE(m, state->PyRawIOBase_Type, &rawiobase_spec,
+             state->PyIOBase_Type);
 
     // PyBufferedIOBase_Type(PyIOBase_Type) subclasses
     ADD_TYPE(m, state->PyBytesIO_Type, &bytesio_spec, state->PyBufferedIOBase_Type);
@@ -775,6 +699,7 @@ PyInit__io(void)
 
     // PyRawIOBase_Type(PyIOBase_Type) subclasses
     ADD_TYPE(m, state->PyFileIO_Type, &fileio_spec, state->PyRawIOBase_Type);
+
 #ifdef HAVE_WINDOWS_CONSOLE_IO
     ADD_TYPE(m, state->PyWindowsConsoleIO_Type, &winconsoleio_spec,
              state->PyRawIOBase_Type);
@@ -785,11 +710,30 @@ PyInit__io(void)
     ADD_TYPE(m, state->PyTextIOWrapper_Type, &textiowrapper_spec,
              state->PyTextIOBase_Type);
 
-    state->initialized = 1;
+#undef ADD_TYPE
+    return 0;
+}
 
-    return m;
+static struct PyModuleDef_Slot iomodule_slots[] = {
+    {Py_mod_exec, iomodule_exec},
+    {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
+    {0, NULL},
+};
 
-  fail:
-    Py_DECREF(m);
-    return NULL;
+struct PyModuleDef _PyIO_Module = {
+    .m_base = PyModuleDef_HEAD_INIT,
+    .m_name = "io",
+    .m_doc = module_doc,
+    .m_size = sizeof(_PyIO_State),
+    .m_methods = module_methods,
+    .m_traverse = iomodule_traverse,
+    .m_clear = iomodule_clear,
+    .m_free = iomodule_free,
+    .m_slots = iomodule_slots,
+};
+
+PyMODINIT_FUNC
+PyInit__io(void)
+{
+    return PyModuleDef_Init(&_PyIO_Module);
 }
index ae06fecc48b4501756a5a8ea2501faa42233dd22..afd638a120ba085e44cadf310c75ed17bd2741e3 100644 (file)
@@ -8,13 +8,8 @@
 #include "pycore_typeobject.h"    // _PyType_GetModuleState()
 #include "structmember.h"
 
-/* ABCs */
-extern PyTypeObject PyIOBase_Type;
-extern PyTypeObject PyRawIOBase_Type;
-extern PyTypeObject PyBufferedIOBase_Type;
-extern PyTypeObject PyTextIOBase_Type;
-
 /* Type specs */
+extern PyType_Spec bufferediobase_spec;
 extern PyType_Spec bufferedrandom_spec;
 extern PyType_Spec bufferedreader_spec;
 extern PyType_Spec bufferedrwpair_spec;
@@ -22,8 +17,11 @@ extern PyType_Spec bufferedwriter_spec;
 extern PyType_Spec bytesio_spec;
 extern PyType_Spec bytesiobuf_spec;
 extern PyType_Spec fileio_spec;
+extern PyType_Spec iobase_spec;
 extern PyType_Spec nldecoder_spec;
+extern PyType_Spec rawiobase_spec;
 extern PyType_Spec stringio_spec;
+extern PyType_Spec textiobase_spec;
 extern PyType_Spec textiowrapper_spec;
 
 #ifdef HAVE_WINDOWS_CONSOLE_IO
@@ -168,9 +166,6 @@ struct _io_state {
 #endif
 };
 
-#define IO_MOD_STATE(mod) ((_PyIO_State *)PyModule_GetState(mod))
-#define IO_STATE() _PyIO_get_module_state()
-
 static inline _PyIO_State *
 get_io_state(PyObject *module)
 {
@@ -195,7 +190,7 @@ find_io_state_by_def(PyTypeObject *type)
     return get_io_state(mod);
 }
 
-extern _PyIO_State *_PyIO_get_module_state(void);
+extern PyObject *_PyIOBase_cannot_pickle(PyObject *self, PyObject *args);
 
 #ifdef HAVE_WINDOWS_CONSOLE_IO
 extern char _PyIO_get_console_type(PyObject *);
index 00e228bca4375b3def71f7eb8018d9adab7d0a2c..d5cc047bd8d563ecf299020024536283bfce4bf5 100644 (file)
 
 /*[clinic input]
 module _io
-class _io._BufferedIOBase "PyObject *" "&PyBufferedIOBase_Type"
-class _io._Buffered "buffered *" "&PyBufferedIOBase_Type"
+class _io._BufferedIOBase "PyObject *" "clinic_state()->PyBufferedIOBase_Type"
+class _io._Buffered "buffered *" "clinic_state()->PyBufferedIOBase_Type"
 class _io.BufferedReader "buffered *" "clinic_state()->PyBufferedReader_Type"
 class _io.BufferedWriter "buffered *" "clinic_state()->PyBufferedWriter_Type"
 class _io.BufferedRWPair "rwpair *" "clinic_state()->PyBufferedRWPair_Type"
 class _io.BufferedRandom "buffered *" "clinic_state()->PyBufferedRandom_Type"
 [clinic start generated code]*/
-/*[clinic end generated code: output=da39a3ee5e6b4b0d input=abd685b9d94b9888]*/
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=3b3ef9cbbbad4590]*/
 
 /*
  * BufferedIOBase class, inherits from IOBase.
@@ -128,7 +128,7 @@ static PyObject *
 _io__BufferedIOBase_detach_impl(PyObject *self, PyTypeObject *cls)
 /*[clinic end generated code: output=b87b135d67cd4448 input=0b61a7b4357c1ea7]*/
 {
-    _PyIO_State *state = IO_STATE();
+    _PyIO_State *state = get_io_state_by_cls(cls);
     return bufferediobase_unsupported(state, "detach");
 }
 
@@ -162,7 +162,7 @@ _io__BufferedIOBase_read_impl(PyObject *self, PyTypeObject *cls,
                               PyObject *args)
 /*[clinic end generated code: output=4521b30940fd7b67 input=390205758adc8510]*/
 {
-    _PyIO_State *state = IO_STATE();
+    _PyIO_State *state = get_io_state_by_cls(cls);
     return bufferediobase_unsupported(state, "read");
 }
 
@@ -184,7 +184,7 @@ _io__BufferedIOBase_read1_impl(PyObject *self, PyTypeObject *cls,
                                PyObject *args)
 /*[clinic end generated code: output=636fd241c21e050a input=ef546a1238c5b41c]*/
 {
-    _PyIO_State *state = IO_STATE();
+    _PyIO_State *state = get_io_state_by_cls(cls);
     return bufferediobase_unsupported(state, "read1");
 }
 
@@ -209,7 +209,7 @@ _io__BufferedIOBase_write_impl(PyObject *self, PyTypeObject *cls,
                                PyObject *args)
 /*[clinic end generated code: output=d51feea4bcac9892 input=f79b72c4dccb3dc2]*/
 {
-    _PyIO_State *state = IO_STATE();
+    _PyIO_State *state = get_io_state_by_cls(cls);
     return bufferediobase_unsupported(state, "write");
 }
 
@@ -394,6 +394,15 @@ _enter_buffered_busy(buffered *self)
         (self->buffer_size * (size / self->buffer_size)))
 
 
+static int
+buffered_clear(buffered *self)
+{
+    self->ok = 0;
+    Py_CLEAR(self->raw);
+    Py_CLEAR(self->dict);
+    return 0;
+}
+
 static void
 buffered_dealloc(buffered *self)
 {
@@ -405,7 +414,6 @@ buffered_dealloc(buffered *self)
     self->ok = 0;
     if (self->weakreflist != NULL)
         PyObject_ClearWeakRefs((PyObject *)self);
-    Py_CLEAR(self->raw);
     if (self->buffer) {
         PyMem_Free(self->buffer);
         self->buffer = NULL;
@@ -414,7 +422,7 @@ buffered_dealloc(buffered *self)
         PyThread_free_lock(self->lock);
         self->lock = NULL;
     }
-    Py_CLEAR(self->dict);
+    (void)buffered_clear(self);
     tp->tp_free((PyObject *)self);
     Py_DECREF(tp);
 }
@@ -443,15 +451,6 @@ buffered_traverse(buffered *self, visitproc visit, void *arg)
     return 0;
 }
 
-static int
-buffered_clear(buffered *self)
-{
-    self->ok = 0;
-    Py_CLEAR(self->raw);
-    Py_CLEAR(self->dict);
-    return 0;
-}
-
 /* Because this can call arbitrary code, it shouldn't be called when
    the refcount is 0 (that is, not directly from tp_dealloc unless
    the refcount has been temporarily re-incremented). */
@@ -2220,6 +2219,8 @@ bufferedrwpair_traverse(rwpair *self, visitproc visit, void *arg)
 {
     Py_VISIT(Py_TYPE(self));
     Py_VISIT(self->dict);
+    Py_VISIT(self->reader);
+    Py_VISIT(self->writer);
     return 0;
 }
 
@@ -2239,9 +2240,7 @@ bufferedrwpair_dealloc(rwpair *self)
     _PyObject_GC_UNTRACK(self);
     if (self->weakreflist != NULL)
         PyObject_ClearWeakRefs((PyObject *)self);
-    Py_CLEAR(self->reader);
-    Py_CLEAR(self->writer);
-    Py_CLEAR(self->dict);
+    (void)bufferedrwpair_clear(self);
     tp->tp_free((PyObject *) self);
     Py_DECREF(tp);
 }
@@ -2424,6 +2423,12 @@ _io_BufferedRandom___init___impl(buffered *self, PyObject *raw,
 #include "clinic/bufferedio.c.h"
 #undef clinic_state
 
+static int
+bufferediobase_traverse(PyObject *self, visitproc visit, void *arg)
+{
+    Py_VISIT(Py_TYPE(self));
+    return 0;
+}
 
 static PyMethodDef bufferediobase_methods[] = {
     _IO__BUFFEREDIOBASE_DETACH_METHODDEF
@@ -2435,57 +2440,19 @@ static PyMethodDef bufferediobase_methods[] = {
     {NULL, NULL}
 };
 
-PyTypeObject PyBufferedIOBase_Type = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    "_io._BufferedIOBase",      /*tp_name*/
-    0,                          /*tp_basicsize*/
-    0,                          /*tp_itemsize*/
-    0,                          /*tp_dealloc*/
-    0,                          /*tp_vectorcall_offset*/
-    0,                          /*tp_getattr*/
-    0,                          /*tp_setattr*/
-    0,                          /*tp_as_async*/
-    0,                          /*tp_repr*/
-    0,                          /*tp_as_number*/
-    0,                          /*tp_as_sequence*/
-    0,                          /*tp_as_mapping*/
-    0,                          /*tp_hash */
-    0,                          /*tp_call*/
-    0,                          /*tp_str*/
-    0,                          /*tp_getattro*/
-    0,                          /*tp_setattro*/
-    0,                          /*tp_as_buffer*/
-    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,  /*tp_flags*/
-    bufferediobase_doc,         /* tp_doc */
-    0,                          /* tp_traverse */
-    0,                          /* tp_clear */
-    0,                          /* tp_richcompare */
-    0,                          /* tp_weaklistoffset */
-    0,                          /* tp_iter */
-    0,                          /* tp_iternext */
-    bufferediobase_methods,     /* tp_methods */
-    0,                          /* tp_members */
-    0,                          /* tp_getset */
-    &PyIOBase_Type,             /* tp_base */
-    0,                          /* tp_dict */
-    0,                          /* tp_descr_get */
-    0,                          /* tp_descr_set */
-    0,                          /* tp_dictoffset */
-    0,                          /* tp_init */
-    0,                          /* tp_alloc */
-    0,                          /* tp_new */
-    0,                          /* tp_free */
-    0,                          /* tp_is_gc */
-    0,                          /* tp_bases */
-    0,                          /* tp_mro */
-    0,                          /* tp_cache */
-    0,                          /* tp_subclasses */
-    0,                          /* tp_weaklist */
-    0,                          /* tp_del */
-    0,                          /* tp_version_tag */
-    0,                          /* tp_finalize */
+static PyType_Slot bufferediobase_slots[] = {
+    {Py_tp_doc, (void *)bufferediobase_doc},
+    {Py_tp_methods, bufferediobase_methods},
+    {Py_tp_traverse, bufferediobase_traverse},
+    {0, NULL},
 };
 
+PyType_Spec bufferediobase_spec = {
+    .name = "_io._BufferedIOBase",
+    .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC |
+              Py_TPFLAGS_IMMUTABLETYPE),
+    .slots = bufferediobase_slots,
+};
 
 static PyMethodDef bufferedreader_methods[] = {
     /* BufferedIOMixin methods */
@@ -2508,6 +2475,9 @@ static PyMethodDef bufferedreader_methods[] = {
     _IO__BUFFERED_TELL_METHODDEF
     _IO__BUFFERED_TRUNCATE_METHODDEF
     _IO__BUFFERED___SIZEOF___METHODDEF
+
+    {"__reduce__", _PyIOBase_cannot_pickle, METH_VARARGS},
+    {"__reduce_ex__", _PyIOBase_cannot_pickle, METH_VARARGS},
     {NULL, NULL}
 };
 
@@ -2565,6 +2535,9 @@ static PyMethodDef bufferedwriter_methods[] = {
     _IO__BUFFERED_SEEK_METHODDEF
     _IO__BUFFERED_TELL_METHODDEF
     _IO__BUFFERED___SIZEOF___METHODDEF
+
+    {"__reduce__", _PyIOBase_cannot_pickle, METH_VARARGS},
+    {"__reduce_ex__", _PyIOBase_cannot_pickle, METH_VARARGS},
     {NULL, NULL}
 };
 
@@ -2680,6 +2653,9 @@ static PyMethodDef bufferedrandom_methods[] = {
     _IO__BUFFERED_PEEK_METHODDEF
     _IO_BUFFEREDWRITER_WRITE_METHODDEF
     _IO__BUFFERED___SIZEOF___METHODDEF
+
+    {"__reduce__", _PyIOBase_cannot_pickle, METH_VARARGS},
+    {"__reduce_ex__", _PyIOBase_cannot_pickle, METH_VARARGS},
     {NULL, NULL}
 };
 
index 3fddfc2ed0bc9cff6821aa672b0670b1bc8a2db8..80773058693259554706fdc9f705396c41db4ba6 100644 (file)
@@ -979,6 +979,7 @@ bytesio_traverse(bytesio *self, visitproc visit, void *arg)
 {
     Py_VISIT(Py_TYPE(self));
     Py_VISIT(self->dict);
+    Py_VISIT(self->buf);
     return 0;
 }
 
@@ -986,6 +987,7 @@ static int
 bytesio_clear(bytesio *self)
 {
     Py_CLEAR(self->dict);
+    Py_CLEAR(self->buf);
     return 0;
 }
 
index 473f0a8a6befcbbd54972e288b2190ed91b05136..30944fc56bf70eedcccd84e168128d6eff1b7648 100644 (file)
@@ -536,7 +536,7 @@ fileio_dealloc(fileio *self)
     _PyObject_GC_UNTRACK(self);
     if (self->weakreflist != NULL)
         PyObject_ClearWeakRefs((PyObject *) self);
-    Py_CLEAR(self->dict);
+    (void)fileio_clear(self);
     tp->tp_free((PyObject *)self);
     Py_DECREF(tp);
 }
@@ -1166,6 +1166,8 @@ static PyMethodDef fileio_methods[] = {
     _IO_FILEIO_FILENO_METHODDEF
     _IO_FILEIO_ISATTY_METHODDEF
     {"_dealloc_warn", (PyCFunction)fileio_dealloc_warn, METH_O, NULL},
+    {"__reduce__", _PyIOBase_cannot_pickle, METH_VARARGS},
+    {"__reduce_ex__", _PyIOBase_cannot_pickle, METH_VARARGS},
     {NULL,           NULL}             /* sentinel */
 };
 
index 26f2a3155bda62152819b9a7f476ba569c574f23..764c5fb332007284ad9887b96bed7b1bb650adb7 100644 (file)
 
 /*[clinic input]
 module _io
-class _io._IOBase "PyObject *" "&PyIOBase_Type"
-class _io._RawIOBase "PyObject *" "&PyRawIOBase_Type"
+class _io._IOBase "PyObject *" "clinic_state()->PyIOBase_Type"
+class _io._RawIOBase "PyObject *" "clinic_state()->PyRawIOBase_Type"
 [clinic start generated code]*/
-/*[clinic end generated code: output=da39a3ee5e6b4b0d input=d29a4d076c2b211c]*/
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=9006b7802ab8ea85]*/
 
 /*
  * IOBase class, an abstract class
@@ -101,7 +101,7 @@ static PyObject *
 _io__IOBase_seek_impl(PyObject *self, PyTypeObject *cls, PyObject *args)
 /*[clinic end generated code: output=1dd694ac9de260fa input=ebb5476eb22fc5d4]*/
 {
-    _PyIO_State *state = IO_STATE();
+    _PyIO_State *state = get_io_state_by_cls(cls);
     return iobase_unsupported(state, "seek");
 }
 
@@ -134,7 +134,7 @@ static PyObject *
 _io__IOBase_truncate_impl(PyObject *self, PyTypeObject *cls, PyObject *args)
 /*[clinic end generated code: output=b7eed4649cbe22c1 input=ad90582a1d8b5cc9]*/
 {
-    _PyIO_State *state = IO_STATE();
+    _PyIO_State *state = get_io_state_by_cls(cls);
     return iobase_unsupported(state, "truncate");
 }
 
@@ -220,24 +220,32 @@ _PyIOBase_check_closed(PyObject *self, PyObject *args)
 static PyObject *
 iobase_check_seekable(PyObject *self, PyObject *args)
 {
-    _PyIO_State *state = IO_STATE();
+    _PyIO_State *state = find_io_state_by_def(Py_TYPE(self));
     return _PyIOBase_check_seekable(state, self, args);
 }
 
 static PyObject *
 iobase_check_readable(PyObject *self, PyObject *args)
 {
-    _PyIO_State *state = IO_STATE();
+    _PyIO_State *state = find_io_state_by_def(Py_TYPE(self));
     return _PyIOBase_check_readable(state, self, args);
 }
 
 static PyObject *
 iobase_check_writable(PyObject *self, PyObject *args)
 {
-    _PyIO_State *state = IO_STATE();
+    _PyIO_State *state = find_io_state_by_def(Py_TYPE(self));
     return _PyIOBase_check_writable(state, self, args);
 }
 
+PyObject *
+_PyIOBase_cannot_pickle(PyObject *self, PyObject *args)
+{
+    PyErr_Format(PyExc_TypeError,
+        "cannot pickle '%.100s' instances", _PyType_Name(Py_TYPE(self)));
+    return NULL;
+}
+
 /* XXX: IOBase thinks it has to maintain its own internal state in
    `__IOBase_closed` and call flush() by itself, but it is redundant with
    whatever behaviour a non-trivial derived class will implement. */
@@ -351,6 +359,7 @@ _PyIOBase_finalize(PyObject *self)
 static int
 iobase_traverse(iobase *self, visitproc visit, void *arg)
 {
+    Py_VISIT(Py_TYPE(self));
     Py_VISIT(self->dict);
     return 0;
 }
@@ -380,11 +389,13 @@ iobase_dealloc(iobase *self)
         }
         return;
     }
+    PyTypeObject *tp = Py_TYPE(self);
     _PyObject_GC_UNTRACK(self);
     if (self->weakreflist != NULL)
         PyObject_ClearWeakRefs((PyObject *) self);
     Py_CLEAR(self->dict);
-    Py_TYPE(self)->tp_free((PyObject *) self);
+    tp->tp_free((PyObject *)self);
+    Py_DECREF(tp);
 }
 
 /* Inquiry methods */
@@ -523,7 +534,7 @@ static PyObject *
 _io__IOBase_fileno_impl(PyObject *self, PyTypeObject *cls)
 /*[clinic end generated code: output=7caaa32a6f4ada3d input=1927c8bea5c85099]*/
 {
-    _PyIO_State *state = IO_STATE();
+    _PyIO_State *state = get_io_state_by_cls(cls);
     return iobase_unsupported(state, "fileno");
 }
 
@@ -821,7 +832,9 @@ _io__IOBase_writelines(PyObject *self, PyObject *lines)
     Py_RETURN_NONE;
 }
 
+#define clinic_state() (find_io_state_by_def(Py_TYPE(self)))
 #include "clinic/iobase.c.h"
+#undef clinic_state
 
 static PyMethodDef iobase_methods[] = {
     _IO__IOBASE_SEEK_METHODDEF
@@ -858,59 +871,34 @@ static PyGetSetDef iobase_getset[] = {
     {NULL}
 };
 
+static struct PyMemberDef iobase_members[] = {
+    {"__weaklistoffset__", T_PYSSIZET, offsetof(iobase, weakreflist), READONLY},
+    {"__dictoffset__", T_PYSSIZET, offsetof(iobase, dict), READONLY},
+    {NULL},
+};
+
 
-PyTypeObject PyIOBase_Type = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    "_io._IOBase",              /*tp_name*/
-    sizeof(iobase),             /*tp_basicsize*/
-    0,                          /*tp_itemsize*/
-    (destructor)iobase_dealloc, /*tp_dealloc*/
-    0,                          /*tp_vectorcall_offset*/
-    0,                          /*tp_getattr*/
-    0,                          /*tp_setattr*/
-    0,                          /*tp_as_async*/
-    0,                          /*tp_repr*/
-    0,                          /*tp_as_number*/
-    0,                          /*tp_as_sequence*/
-    0,                          /*tp_as_mapping*/
-    0,                          /*tp_hash */
-    0,                          /*tp_call*/
-    0,                          /*tp_str*/
-    0,                          /*tp_getattro*/
-    0,                          /*tp_setattro*/
-    0,                          /*tp_as_buffer*/
-    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
-        | Py_TPFLAGS_HAVE_GC,   /*tp_flags*/
-    iobase_doc,                 /* tp_doc */
-    (traverseproc)iobase_traverse, /* tp_traverse */
-    (inquiry)iobase_clear,      /* tp_clear */
-    0,                          /* tp_richcompare */
-    offsetof(iobase, weakreflist), /* tp_weaklistoffset */
-    iobase_iter,                /* tp_iter */
-    iobase_iternext,            /* tp_iternext */
-    iobase_methods,             /* tp_methods */
-    0,                          /* tp_members */
-    iobase_getset,              /* tp_getset */
-    0,                          /* tp_base */
-    0,                          /* tp_dict */
-    0,                          /* tp_descr_get */
-    0,                          /* tp_descr_set */
-    offsetof(iobase, dict),     /* tp_dictoffset */
-    0,                          /* tp_init */
-    0,                          /* tp_alloc */
-    PyType_GenericNew,          /* tp_new */
-    0,                          /* tp_free */
-    0,                          /* tp_is_gc */
-    0,                          /* tp_bases */
-    0,                          /* tp_mro */
-    0,                          /* tp_cache */
-    0,                          /* tp_subclasses */
-    0,                          /* tp_weaklist */
-    0,                          /* tp_del */
-    0,                          /* tp_version_tag */
-    iobase_finalize,            /* tp_finalize */
+static PyType_Slot iobase_slots[] = {
+    {Py_tp_dealloc, iobase_dealloc},
+    {Py_tp_doc, (void *)iobase_doc},
+    {Py_tp_traverse, iobase_traverse},
+    {Py_tp_clear, iobase_clear},
+    {Py_tp_iter, iobase_iter},
+    {Py_tp_iternext, iobase_iternext},
+    {Py_tp_methods, iobase_methods},
+    {Py_tp_members, iobase_members},
+    {Py_tp_getset, iobase_getset},
+    {Py_tp_finalize, iobase_finalize},
+    {0, NULL},
 };
 
+PyType_Spec iobase_spec = {
+    .name = "_io._IOBase",
+    .basicsize = sizeof(iobase),
+    .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC |
+              Py_TPFLAGS_IMMUTABLETYPE),
+    .slots = iobase_slots,
+};
 
 /*
  * RawIOBase class, Inherits from IOBase.
@@ -1045,6 +1033,13 @@ rawiobase_write(PyObject *self, PyObject *args)
     return NULL;
 }
 
+static int
+rawiobase_traverse(PyObject *self, visitproc visit, void *arg)
+{
+    Py_VISIT(Py_TYPE(self));
+    return 0;
+}
+
 static PyMethodDef rawiobase_methods[] = {
     _IO__RAWIOBASE_READ_METHODDEF
     _IO__RAWIOBASE_READALL_METHODDEF
@@ -1053,53 +1048,16 @@ static PyMethodDef rawiobase_methods[] = {
     {NULL, NULL}
 };
 
-PyTypeObject PyRawIOBase_Type = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    "_io._RawIOBase",                /*tp_name*/
-    0,                          /*tp_basicsize*/
-    0,                          /*tp_itemsize*/
-    0,                          /*tp_dealloc*/
-    0,                          /*tp_vectorcall_offset*/
-    0,                          /*tp_getattr*/
-    0,                          /*tp_setattr*/
-    0,                          /*tp_as_async*/
-    0,                          /*tp_repr*/
-    0,                          /*tp_as_number*/
-    0,                          /*tp_as_sequence*/
-    0,                          /*tp_as_mapping*/
-    0,                          /*tp_hash */
-    0,                          /*tp_call*/
-    0,                          /*tp_str*/
-    0,                          /*tp_getattro*/
-    0,                          /*tp_setattro*/
-    0,                          /*tp_as_buffer*/
-    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,  /*tp_flags*/
-    rawiobase_doc,              /* tp_doc */
-    0,                          /* tp_traverse */
-    0,                          /* tp_clear */
-    0,                          /* tp_richcompare */
-    0,                          /* tp_weaklistoffset */
-    0,                          /* tp_iter */
-    0,                          /* tp_iternext */
-    rawiobase_methods,          /* tp_methods */
-    0,                          /* tp_members */
-    0,                          /* tp_getset */
-    &PyIOBase_Type,             /* tp_base */
-    0,                          /* tp_dict */
-    0,                          /* tp_descr_get */
-    0,                          /* tp_descr_set */
-    0,                          /* tp_dictoffset */
-    0,                          /* tp_init */
-    0,                          /* tp_alloc */
-    0,                          /* tp_new */
-    0,                          /* tp_free */
-    0,                          /* tp_is_gc */
-    0,                          /* tp_bases */
-    0,                          /* tp_mro */
-    0,                          /* tp_cache */
-    0,                          /* tp_subclasses */
-    0,                          /* tp_weaklist */
-    0,                          /* tp_del */
-    0,                          /* tp_version_tag */
-    0,                          /* tp_finalize */
+static PyType_Slot rawiobase_slots[] = {
+    {Py_tp_doc, (void *)rawiobase_doc},
+    {Py_tp_methods, rawiobase_methods},
+    {Py_tp_traverse, rawiobase_traverse},
+    {0, NULL},
+};
+
+PyType_Spec rawiobase_spec = {
+    .name = "_io._RawIOBase",
+    .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC |
+              Py_TPFLAGS_IMMUTABLETYPE),
+    .slots = rawiobase_slots,
 };
index 13d3b870b39a81492d9a02e769fd7278134d0124..3eb25704b4aa550374f09013e891d0f741649715 100644 (file)
@@ -583,6 +583,9 @@ static int
 stringio_traverse(stringio *self, visitproc visit, void *arg)
 {
     Py_VISIT(Py_TYPE(self));
+    Py_VISIT(self->readnl);
+    Py_VISIT(self->writenl);
+    Py_VISIT(self->decoder);
     Py_VISIT(self->dict);
     return 0;
 }
@@ -590,6 +593,9 @@ stringio_traverse(stringio *self, visitproc visit, void *arg)
 static int
 stringio_clear(stringio *self)
 {
+    Py_CLEAR(self->readnl);
+    Py_CLEAR(self->writenl);
+    Py_CLEAR(self->decoder);
     Py_CLEAR(self->dict);
     return 0;
 }
@@ -605,10 +611,7 @@ stringio_dealloc(stringio *self)
         self->buf = NULL;
     }
     _PyUnicodeWriter_Dealloc(&self->writer);
-    Py_CLEAR(self->readnl);
-    Py_CLEAR(self->writenl);
-    Py_CLEAR(self->decoder);
-    Py_CLEAR(self->dict);
+    (void)stringio_clear(self);
     if (self->weakreflist != NULL) {
         PyObject_ClearWeakRefs((PyObject *) self);
     }
index 3cc292cc35102efe2fb9684bd07916eebc3bea6d..81dd3bed005a6127e0aa994553b800ae1fcfe6ba 100644 (file)
@@ -62,7 +62,7 @@ static PyObject *
 _io__TextIOBase_detach_impl(PyObject *self, PyTypeObject *cls)
 /*[clinic end generated code: output=50915f40c609eaa4 input=987ca3640d0a3776]*/
 {
-    _PyIO_State *state = IO_STATE();
+    _PyIO_State *state = get_io_state_by_cls(cls);
     return _unsupported(state, "detach");
 }
 
@@ -82,7 +82,7 @@ static PyObject *
 _io__TextIOBase_read_impl(PyObject *self, PyTypeObject *cls, PyObject *args)
 /*[clinic end generated code: output=3adf28998831f461 input=cee1e84664a20de0]*/
 {
-    _PyIO_State *state = IO_STATE();
+    _PyIO_State *state = get_io_state_by_cls(cls);
     return _unsupported(state, "read");
 }
 
@@ -102,7 +102,7 @@ _io__TextIOBase_readline_impl(PyObject *self, PyTypeObject *cls,
                               PyObject *args)
 /*[clinic end generated code: output=3073a948d02319f3 input=58f801259f7ff3ef]*/
 {
-    _PyIO_State *state = IO_STATE();
+    _PyIO_State *state = get_io_state_by_cls(cls);
     return _unsupported(state, "readline");
 }
 
@@ -122,7 +122,7 @@ static PyObject *
 _io__TextIOBase_write_impl(PyObject *self, PyTypeObject *cls, PyObject *args)
 /*[clinic end generated code: output=5d985eb529472bc4 input=21b6961b5cba9496]*/
 {
-    _PyIO_State *state = IO_STATE();
+    _PyIO_State *state = get_io_state_by_cls(cls);
     return _unsupported(state, "write");
 }
 
@@ -164,6 +164,12 @@ textiobase_errors_get(PyObject *self, void *context)
     Py_RETURN_NONE;
 }
 
+static int
+textiobase_traverse(PyObject *self, visitproc visit, void *arg)
+{
+    Py_VISIT(Py_TYPE(self));
+    return 0;
+}
 
 static PyMethodDef textiobase_methods[] = {
     _IO__TEXTIOBASE_DETACH_METHODDEF
@@ -180,57 +186,20 @@ static PyGetSetDef textiobase_getset[] = {
     {NULL}
 };
 
-PyTypeObject PyTextIOBase_Type = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    "_io._TextIOBase",          /*tp_name*/
-    0,                          /*tp_basicsize*/
-    0,                          /*tp_itemsize*/
-    0,                          /*tp_dealloc*/
-    0,                          /*tp_vectorcall_offset*/
-    0,                          /*tp_getattr*/
-    0,                          /*tp_setattr*/
-    0,                          /*tp_as_async*/
-    0,                          /*tp_repr*/
-    0,                          /*tp_as_number*/
-    0,                          /*tp_as_sequence*/
-    0,                          /*tp_as_mapping*/
-    0,                          /*tp_hash */
-    0,                          /*tp_call*/
-    0,                          /*tp_str*/
-    0,                          /*tp_getattro*/
-    0,                          /*tp_setattro*/
-    0,                          /*tp_as_buffer*/
-    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,  /*tp_flags*/
-    textiobase_doc,             /* tp_doc */
-    0,                          /* tp_traverse */
-    0,                          /* tp_clear */
-    0,                          /* tp_richcompare */
-    0,                          /* tp_weaklistoffset */
-    0,                          /* tp_iter */
-    0,                          /* tp_iternext */
-    textiobase_methods,         /* tp_methods */
-    0,                          /* tp_members */
-    textiobase_getset,          /* tp_getset */
-    &PyIOBase_Type,             /* tp_base */
-    0,                          /* tp_dict */
-    0,                          /* tp_descr_get */
-    0,                          /* tp_descr_set */
-    0,                          /* tp_dictoffset */
-    0,                          /* tp_init */
-    0,                          /* tp_alloc */
-    0,                          /* tp_new */
-    0,                          /* tp_free */
-    0,                          /* tp_is_gc */
-    0,                          /* tp_bases */
-    0,                          /* tp_mro */
-    0,                          /* tp_cache */
-    0,                          /* tp_subclasses */
-    0,                          /* tp_weaklist */
-    0,                          /* tp_del */
-    0,                          /* tp_version_tag */
-    0,                          /* tp_finalize */
+static PyType_Slot textiobase_slots[] = {
+    {Py_tp_doc, (void *)textiobase_doc},
+    {Py_tp_methods, textiobase_methods},
+    {Py_tp_getset, textiobase_getset},
+    {Py_tp_traverse, textiobase_traverse},
+    {0, NULL},
 };
 
+PyType_Spec textiobase_spec = {
+    .name = "_io._TextIOBase",
+    .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC |
+              Py_TPFLAGS_IMMUTABLETYPE),
+    .slots = textiobase_slots,
+};
 
 /* IncrementalNewlineDecoder */
 
@@ -1456,7 +1425,7 @@ textiowrapper_dealloc(textio *self)
     _PyObject_GC_UNTRACK(self);
     if (self->weakreflist != NULL)
         PyObject_ClearWeakRefs((PyObject *)self);
-    textiowrapper_clear(self);
+    (void)textiowrapper_clear(self);
     tp->tp_free((PyObject *)self);
     Py_DECREF(tp);
 }
@@ -3267,6 +3236,9 @@ static PyMethodDef textiowrapper_methods[] = {
     _IO_TEXTIOWRAPPER_SEEK_METHODDEF
     _IO_TEXTIOWRAPPER_TELL_METHODDEF
     _IO_TEXTIOWRAPPER_TRUNCATE_METHODDEF
+
+    {"__reduce__", _PyIOBase_cannot_pickle, METH_VARARGS},
+    {"__reduce_ex__", _PyIOBase_cannot_pickle, METH_VARARGS},
     {NULL, NULL}
 };
 
index d65e247737a07105ed0797faca6ebb9cc1f7dbe0..15f3053957da612dcf9dae0097b5e1fd977f8c69 100644 (file)
@@ -1096,7 +1096,7 @@ _io__WindowsConsoleIO_isatty_impl(winconsoleio *self)
     Py_RETURN_TRUE;
 }
 
-#define clinic_state() (IO_STATE())
+#define clinic_state() (find_io_state_by_def(Py_TYPE(self)))
 #include "clinic/winconsoleio.c.h"
 #undef clinic_state
 
index 61f87c5eba60ed3e815404dac9084d8e190347ee..c5dc0f44a380684784adea1796c7c43cceec31cc 100644 (file)
@@ -29,9 +29,6 @@
 #include "pycore_unicodeobject.h" // _PyUnicode_InitTypes()
 #include "opcode.h"
 
-extern PyStatus _PyIO_InitTypes(PyInterpreterState *interp);
-extern void _PyIO_FiniTypes(PyInterpreterState *interp);
-
 #include <locale.h>               // setlocale()
 #include <stdlib.h>               // getenv()
 
@@ -706,11 +703,6 @@ pycore_init_types(PyInterpreterState *interp)
         return _PyStatus_ERR("failed to initialize an exception type");
     }
 
-    status = _PyIO_InitTypes(interp);
-    if (_PyStatus_EXCEPTION(status)) {
-        return status;
-    }
-
     status = _PyExc_InitGlobalObjects(interp);
     if (_PyStatus_EXCEPTION(status)) {
         return status;
@@ -1667,8 +1659,6 @@ flush_std_files(void)
 static void
 finalize_interp_types(PyInterpreterState *interp)
 {
-    _PyIO_FiniTypes(interp);
-
     _PyUnicode_FiniTypes(interp);
     _PySys_FiniTypes(interp);
     _PyExc_Fini(interp);
index 8afa92ef25d37642c791755c89fde95bb4debc44..9863acdade308b2f59e71b21703bb9ed1f7f9f54 100644 (file)
@@ -317,10 +317,6 @@ Python/instrumentation.c   -       _PyInstrumentation_MISSING      -
 ##-----------------------
 ## static types
 
-Modules/_io/bufferedio.c       -       PyBufferedIOBase_Type   -
-Modules/_io/iobase.c   -       PyIOBase_Type   -
-Modules/_io/iobase.c   -       PyRawIOBase_Type        -
-Modules/_io/textio.c   -       PyTextIOBase_Type       -
 Modules/_testcapi/vectorcall.c -       MethodDescriptorBase_Type       -
 Modules/_testcapi/vectorcall.c -       MethodDescriptorDerived_Type    -
 Modules/_testcapi/vectorcall.c -       MethodDescriptorNopGet_Type     -