Skip to content

Commit

Permalink
gh-101819: Isolate _io (#101948)
Browse files Browse the repository at this point in the history
Co-authored-by: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com>
Co-authored-by: Victor Stinner <vstinner@python.org>
  • Loading branch information
3 people authored May 15, 2023
1 parent 35bf091 commit 186bf39
Show file tree
Hide file tree
Showing 13 changed files with 205 additions and 362 deletions.
7 changes: 5 additions & 2 deletions Lib/test/test_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -4242,6 +4242,7 @@ def test_warn_on_dealloc_fd(self):

def test_pickling(self):
# Pickling file objects is forbidden
msg = "cannot pickle"
for kwargs in [
{"mode": "w"},
{"mode": "wb"},
Expand All @@ -4256,8 +4257,10 @@ def test_pickling(self):
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"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Isolate the :mod:`io` extension module by applying :pep:`687`. Patch by
Kumar Aditya, Victor Stinner, and Erlend E. Aasland.
138 changes: 41 additions & 97 deletions Modules/_io/_iomodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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);
}
15 changes: 5 additions & 10 deletions Modules/_io/_iomodule.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,20 @@
#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;
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
Expand Down Expand Up @@ -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)
{
Expand All @@ -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 *);
Expand Down
Loading

0 comments on commit 186bf39

Please sign in to comment.