Skip to content

Commit 186bf39

Browse files
erlend-aaslandkumaraditya303vstinner
authored
gh-101819: Isolate _io (#101948)
Co-authored-by: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com> Co-authored-by: Victor Stinner <vstinner@python.org>
1 parent 35bf091 commit 186bf39

File tree

13 files changed

+205
-362
lines changed

13 files changed

+205
-362
lines changed

Diff for: Lib/test/test_io.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -4242,6 +4242,7 @@ def test_warn_on_dealloc_fd(self):
42424242

42434243
def test_pickling(self):
42444244
# Pickling file objects is forbidden
4245+
msg = "cannot pickle"
42454246
for kwargs in [
42464247
{"mode": "w"},
42474248
{"mode": "wb"},
@@ -4256,8 +4257,10 @@ def test_pickling(self):
42564257
if "b" not in kwargs["mode"]:
42574258
kwargs["encoding"] = "utf-8"
42584259
for protocol in range(pickle.HIGHEST_PROTOCOL + 1):
4259-
with self.open(os_helper.TESTFN, **kwargs) as f:
4260-
self.assertRaises(TypeError, pickle.dumps, f, protocol)
4260+
with self.subTest(protocol=protocol, kwargs=kwargs):
4261+
with self.open(os_helper.TESTFN, **kwargs) as f:
4262+
with self.assertRaisesRegex(TypeError, msg):
4263+
pickle.dumps(f, protocol)
42614264

42624265
@unittest.skipIf(
42634266
support.is_emscripten, "fstat() of a pipe fd is not supported"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Isolate the :mod:`io` extension module by applying :pep:`687`. Patch by
2+
Kumar Aditya, Victor Stinner, and Erlend E. Aasland.

Diff for: Modules/_io/_iomodule.c

+41-97
Original file line numberDiff line numberDiff line change
@@ -561,25 +561,9 @@ PyNumber_AsOff_t(PyObject *item, PyObject *err)
561561
return result;
562562
}
563563

564-
_PyIO_State *
565-
_PyIO_get_module_state(void)
566-
{
567-
PyObject *mod = PyState_FindModule(&_PyIO_Module);
568-
_PyIO_State *state;
569-
if (mod == NULL || (state = get_io_state(mod)) == NULL) {
570-
PyErr_SetString(PyExc_RuntimeError,
571-
"could not find io module state "
572-
"(interpreter shutdown?)");
573-
return NULL;
574-
}
575-
return state;
576-
}
577-
578564
static int
579565
iomodule_traverse(PyObject *mod, visitproc visit, void *arg) {
580566
_PyIO_State *state = get_io_state(mod);
581-
if (!state->initialized)
582-
return 0;
583567
Py_VISIT(state->unsupported_operation);
584568

585569
Py_VISIT(state->PyIOBase_Type);
@@ -606,8 +590,6 @@ iomodule_traverse(PyObject *mod, visitproc visit, void *arg) {
606590
static int
607591
iomodule_clear(PyObject *mod) {
608592
_PyIO_State *state = get_io_state(mod);
609-
if (!state->initialized)
610-
return 0;
611593
Py_CLEAR(state->unsupported_operation);
612594

613595
Py_CLEAR(state->PyIOBase_Type);
@@ -652,115 +634,57 @@ static PyMethodDef module_methods[] = {
652634
{NULL, NULL}
653635
};
654636

655-
struct PyModuleDef _PyIO_Module = {
656-
PyModuleDef_HEAD_INIT,
657-
"io",
658-
module_doc,
659-
sizeof(_PyIO_State),
660-
module_methods,
661-
NULL,
662-
iomodule_traverse,
663-
iomodule_clear,
664-
(freefunc)iomodule_free,
665-
};
666-
667-
668-
static PyTypeObject* static_types[] = {
669-
// Base classes
670-
&PyIOBase_Type,
671-
672-
// PyIOBase_Type subclasses
673-
&PyBufferedIOBase_Type,
674-
&PyRawIOBase_Type,
675-
&PyTextIOBase_Type,
676-
};
677-
678-
679-
PyStatus
680-
_PyIO_InitTypes(PyInterpreterState *interp)
681-
{
682-
for (size_t i=0; i < Py_ARRAY_LENGTH(static_types); i++) {
683-
PyTypeObject *type = static_types[i];
684-
if (_PyStaticType_InitBuiltin(interp, type) < 0) {
685-
return _PyStatus_ERR("Can't initialize builtin type");
686-
}
687-
}
688-
689-
return _PyStatus_OK();
690-
}
691-
692-
void
693-
_PyIO_FiniTypes(PyInterpreterState *interp)
694-
{
695-
// Deallocate types in the reverse order to deallocate subclasses before
696-
// their base classes.
697-
for (Py_ssize_t i=Py_ARRAY_LENGTH(static_types) - 1; i >= 0; i--) {
698-
PyTypeObject *type = static_types[i];
699-
_PyStaticType_Dealloc(interp, type);
700-
}
701-
}
702-
703637
#define ADD_TYPE(module, type, spec, base) \
704638
do { \
705639
type = (PyTypeObject *)PyType_FromModuleAndSpec(module, spec, \
706640
(PyObject *)base); \
707641
if (type == NULL) { \
708-
goto fail; \
642+
return -1; \
709643
} \
710644
if (PyModule_AddType(module, type) < 0) { \
711-
goto fail; \
645+
return -1; \
712646
} \
713647
} while (0)
714648

715-
PyMODINIT_FUNC
716-
PyInit__io(void)
649+
static int
650+
iomodule_exec(PyObject *m)
717651
{
718-
PyObject *m = PyModule_Create(&_PyIO_Module);
719-
_PyIO_State *state = NULL;
720-
if (m == NULL)
721-
return NULL;
722-
state = get_io_state(m);
723-
state->initialized = 0;
652+
_PyIO_State *state = get_io_state(m);
724653

725654
/* DEFAULT_BUFFER_SIZE */
726655
if (PyModule_AddIntMacro(m, DEFAULT_BUFFER_SIZE) < 0)
727-
goto fail;
656+
return -1;
728657

729658
/* UnsupportedOperation inherits from ValueError and OSError */
730659
state->unsupported_operation = PyObject_CallFunction(
731660
(PyObject *)&PyType_Type, "s(OO){}",
732661
"UnsupportedOperation", PyExc_OSError, PyExc_ValueError);
733662
if (state->unsupported_operation == NULL)
734-
goto fail;
663+
return -1;
735664
if (PyModule_AddObjectRef(m, "UnsupportedOperation",
736665
state->unsupported_operation) < 0)
737666
{
738-
goto fail;
667+
return -1;
739668
}
740669

741670
/* BlockingIOError, for compatibility */
742671
if (PyModule_AddObjectRef(m, "BlockingIOError",
743672
(PyObject *) PyExc_BlockingIOError) < 0) {
744-
goto fail;
745-
}
746-
747-
// Add types
748-
for (size_t i=0; i < Py_ARRAY_LENGTH(static_types); i++) {
749-
PyTypeObject *type = static_types[i];
750-
if (PyModule_AddType(m, type) < 0) {
751-
goto fail;
752-
}
673+
return -1;
753674
}
754675

755676
// Base classes
756-
state->PyIOBase_Type = (PyTypeObject *)Py_NewRef(&PyIOBase_Type);
757677
ADD_TYPE(m, state->PyIncrementalNewlineDecoder_Type, &nldecoder_spec, NULL);
758678
ADD_TYPE(m, state->PyBytesIOBuffer_Type, &bytesiobuf_spec, NULL);
679+
ADD_TYPE(m, state->PyIOBase_Type, &iobase_spec, NULL);
759680

760681
// PyIOBase_Type subclasses
761-
state->PyRawIOBase_Type = (PyTypeObject *)Py_NewRef(&PyRawIOBase_Type);
762-
state->PyBufferedIOBase_Type = (PyTypeObject *)Py_NewRef(&PyBufferedIOBase_Type);
763-
state->PyTextIOBase_Type = (PyTypeObject *)Py_NewRef(&PyTextIOBase_Type);
682+
ADD_TYPE(m, state->PyTextIOBase_Type, &textiobase_spec,
683+
state->PyIOBase_Type);
684+
ADD_TYPE(m, state->PyBufferedIOBase_Type, &bufferediobase_spec,
685+
state->PyIOBase_Type);
686+
ADD_TYPE(m, state->PyRawIOBase_Type, &rawiobase_spec,
687+
state->PyIOBase_Type);
764688

765689
// PyBufferedIOBase_Type(PyIOBase_Type) subclasses
766690
ADD_TYPE(m, state->PyBytesIO_Type, &bytesio_spec, state->PyBufferedIOBase_Type);
@@ -775,6 +699,7 @@ PyInit__io(void)
775699

776700
// PyRawIOBase_Type(PyIOBase_Type) subclasses
777701
ADD_TYPE(m, state->PyFileIO_Type, &fileio_spec, state->PyRawIOBase_Type);
702+
778703
#ifdef HAVE_WINDOWS_CONSOLE_IO
779704
ADD_TYPE(m, state->PyWindowsConsoleIO_Type, &winconsoleio_spec,
780705
state->PyRawIOBase_Type);
@@ -785,11 +710,30 @@ PyInit__io(void)
785710
ADD_TYPE(m, state->PyTextIOWrapper_Type, &textiowrapper_spec,
786711
state->PyTextIOBase_Type);
787712

788-
state->initialized = 1;
713+
#undef ADD_TYPE
714+
return 0;
715+
}
789716

790-
return m;
717+
static struct PyModuleDef_Slot iomodule_slots[] = {
718+
{Py_mod_exec, iomodule_exec},
719+
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
720+
{0, NULL},
721+
};
791722

792-
fail:
793-
Py_DECREF(m);
794-
return NULL;
723+
struct PyModuleDef _PyIO_Module = {
724+
.m_base = PyModuleDef_HEAD_INIT,
725+
.m_name = "io",
726+
.m_doc = module_doc,
727+
.m_size = sizeof(_PyIO_State),
728+
.m_methods = module_methods,
729+
.m_traverse = iomodule_traverse,
730+
.m_clear = iomodule_clear,
731+
.m_free = iomodule_free,
732+
.m_slots = iomodule_slots,
733+
};
734+
735+
PyMODINIT_FUNC
736+
PyInit__io(void)
737+
{
738+
return PyModuleDef_Init(&_PyIO_Module);
795739
}

Diff for: Modules/_io/_iomodule.h

+5-10
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,20 @@
88
#include "pycore_typeobject.h" // _PyType_GetModuleState()
99
#include "structmember.h"
1010

11-
/* ABCs */
12-
extern PyTypeObject PyIOBase_Type;
13-
extern PyTypeObject PyRawIOBase_Type;
14-
extern PyTypeObject PyBufferedIOBase_Type;
15-
extern PyTypeObject PyTextIOBase_Type;
16-
1711
/* Type specs */
12+
extern PyType_Spec bufferediobase_spec;
1813
extern PyType_Spec bufferedrandom_spec;
1914
extern PyType_Spec bufferedreader_spec;
2015
extern PyType_Spec bufferedrwpair_spec;
2116
extern PyType_Spec bufferedwriter_spec;
2217
extern PyType_Spec bytesio_spec;
2318
extern PyType_Spec bytesiobuf_spec;
2419
extern PyType_Spec fileio_spec;
20+
extern PyType_Spec iobase_spec;
2521
extern PyType_Spec nldecoder_spec;
22+
extern PyType_Spec rawiobase_spec;
2623
extern PyType_Spec stringio_spec;
24+
extern PyType_Spec textiobase_spec;
2725
extern PyType_Spec textiowrapper_spec;
2826

2927
#ifdef HAVE_WINDOWS_CONSOLE_IO
@@ -168,9 +166,6 @@ struct _io_state {
168166
#endif
169167
};
170168

171-
#define IO_MOD_STATE(mod) ((_PyIO_State *)PyModule_GetState(mod))
172-
#define IO_STATE() _PyIO_get_module_state()
173-
174169
static inline _PyIO_State *
175170
get_io_state(PyObject *module)
176171
{
@@ -195,7 +190,7 @@ find_io_state_by_def(PyTypeObject *type)
195190
return get_io_state(mod);
196191
}
197192

198-
extern _PyIO_State *_PyIO_get_module_state(void);
193+
extern PyObject *_PyIOBase_cannot_pickle(PyObject *self, PyObject *args);
199194

200195
#ifdef HAVE_WINDOWS_CONSOLE_IO
201196
extern char _PyIO_get_console_type(PyObject *);

0 commit comments

Comments
 (0)