Skip to content

Commit

Permalink
gh-101819: Prepare to modernize the _io extension
Browse files Browse the repository at this point in the history
* 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 <[email protected]>
  • Loading branch information
vstinner and erlend-aasland committed May 5, 2023
1 parent 81fc135 commit dfc1e9e
Show file tree
Hide file tree
Showing 9 changed files with 92 additions and 37 deletions.
35 changes: 27 additions & 8 deletions Modules/_io/_iomodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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;
}
Expand Down Expand Up @@ -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;

Expand Down
14 changes: 14 additions & 0 deletions Modules/_io/_iomodule.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "exports.h"

#include "pycore_moduleobject.h" // _PyModule_GetState()
#include "pycore_typeobject.h" // _PyType_GetModuleState()
#include "structmember.h"

/* ABCs */
Expand Down Expand Up @@ -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;

Expand All @@ -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)
{
Expand Down
2 changes: 1 addition & 1 deletion Modules/_io/bufferedio.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
10 changes: 7 additions & 3 deletions Modules/_io/bytesio.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
14 changes: 9 additions & 5 deletions Modules/_io/clinic/bytesio.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 9 additions & 5 deletions Modules/_io/clinic/fileio.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 9 additions & 4 deletions Modules/_io/fileio.c
Original file line number Diff line number Diff line change
Expand Up @@ -130,25 +130,30 @@ 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
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();
}
Expand Down
5 changes: 3 additions & 2 deletions Modules/_io/stringio.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
22 changes: 13 additions & 9 deletions Modules/_io/textio.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */

Expand Down Expand Up @@ -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;
Expand All @@ -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,
Expand Down Expand Up @@ -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;

Expand All @@ -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))
Expand Down Expand Up @@ -1214,7 +1217,6 @@ _io_TextIOWrapper___init___impl(textio *self, PyObject *buffer,
goto error;
}

self->state = state;
self->ok = 1;
return 0;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit dfc1e9e

Please sign in to comment.