Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit c00faf7

Browse filesBrowse files
gh-101819: Adapt _io types to heap types, batch 1 (GH-101949)
Adapt StringIO, TextIOWrapper, FileIO, Buffered*, and BytesIO types. Automerge-Triggered-By: GH:erlend-aasland
1 parent 2713631 commit c00faf7
Copy full SHA for c00faf7

File tree

10 files changed

+463
-478
lines changed
Filter options

10 files changed

+463
-478
lines changed

‎Lib/test/test_io.py

Copy file name to clipboardExpand all lines: Lib/test/test_io.py
+90-1Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1042,6 +1042,95 @@ def close(self):
10421042
support.gc_collect()
10431043
self.assertIsNone(wr(), wr)
10441044

1045+
@support.cpython_only
1046+
class TestIOCTypes(unittest.TestCase):
1047+
def setUp(self):
1048+
_io = import_helper.import_module("_io")
1049+
self.types = [
1050+
_io.BufferedRWPair,
1051+
_io.BufferedRandom,
1052+
_io.BufferedReader,
1053+
_io.BufferedWriter,
1054+
_io.BytesIO,
1055+
_io.FileIO,
1056+
_io.IncrementalNewlineDecoder,
1057+
_io.StringIO,
1058+
_io.TextIOWrapper,
1059+
_io._BufferedIOBase,
1060+
_io._BytesIOBuffer,
1061+
_io._IOBase,
1062+
_io._RawIOBase,
1063+
_io._TextIOBase,
1064+
]
1065+
if sys.platform == "win32":
1066+
self.types.append(_io._WindowsConsoleIO)
1067+
self._io = _io
1068+
1069+
def test_immutable_types(self):
1070+
for tp in self.types:
1071+
with self.subTest(tp=tp):
1072+
with self.assertRaisesRegex(TypeError, "immutable"):
1073+
tp.foo = "bar"
1074+
1075+
def test_class_hierarchy(self):
1076+
def check_subs(types, base):
1077+
for tp in types:
1078+
with self.subTest(tp=tp, base=base):
1079+
self.assertTrue(issubclass(tp, base))
1080+
1081+
def recursive_check(d):
1082+
for k, v in d.items():
1083+
if isinstance(v, dict):
1084+
recursive_check(v)
1085+
elif isinstance(v, set):
1086+
check_subs(v, k)
1087+
else:
1088+
self.fail("corrupt test dataset")
1089+
1090+
_io = self._io
1091+
hierarchy = {
1092+
_io._IOBase: {
1093+
_io._BufferedIOBase: {
1094+
_io.BufferedRWPair,
1095+
_io.BufferedRandom,
1096+
_io.BufferedReader,
1097+
_io.BufferedWriter,
1098+
_io.BytesIO,
1099+
},
1100+
_io._RawIOBase: {
1101+
_io.FileIO,
1102+
},
1103+
_io._TextIOBase: {
1104+
_io.StringIO,
1105+
_io.TextIOWrapper,
1106+
},
1107+
},
1108+
}
1109+
if sys.platform == "win32":
1110+
hierarchy[_io._IOBase][_io._RawIOBase].add(_io._WindowsConsoleIO)
1111+
1112+
recursive_check(hierarchy)
1113+
1114+
def test_subclassing(self):
1115+
_io = self._io
1116+
dataset = {k: True for k in self.types}
1117+
dataset[_io._BytesIOBuffer] = False
1118+
1119+
for tp, is_basetype in dataset.items():
1120+
with self.subTest(tp=tp, is_basetype=is_basetype):
1121+
name = f"{tp.__name__}_subclass"
1122+
bases = (tp,)
1123+
if is_basetype:
1124+
_ = type(name, bases, {})
1125+
else:
1126+
msg = "not an acceptable base type"
1127+
with self.assertRaisesRegex(TypeError, msg):
1128+
_ = type(name, bases, {})
1129+
1130+
def test_disallow_instantiation(self):
1131+
_io = self._io
1132+
support.check_disallow_instantiation(self, _io._BytesIOBuffer)
1133+
10451134
class PyIOTest(IOTest):
10461135
pass
10471136

@@ -4671,7 +4760,7 @@ def load_tests(loader, tests, pattern):
46714760
CIncrementalNewlineDecoderTest, PyIncrementalNewlineDecoderTest,
46724761
CTextIOWrapperTest, PyTextIOWrapperTest,
46734762
CMiscIOTest, PyMiscIOTest,
4674-
CSignalsTest, PySignalsTest,
4763+
CSignalsTest, PySignalsTest, TestIOCTypes,
46754764
)
46764765

46774766
# Put the namespaces of the IO module we are testing and some useful mock

‎Modules/_io/_iomodule.c

Copy file name to clipboardExpand all lines: Modules/_io/_iomodule.c
+62-37Lines changed: 62 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
#define PY_SSIZE_T_CLEAN
1111
#include "Python.h"
1212
#include "_iomodule.h"
13-
#include "pycore_moduleobject.h" // _PyModule_GetState()
1413
#include "pycore_pystate.h" // _PyInterpreterState_GET()
1514

1615
#ifdef HAVE_SYS_TYPES_H
@@ -315,8 +314,9 @@ _io_open_impl(PyObject *module, PyObject *file, const char *mode,
315314
}
316315

317316
/* Create the Raw file stream */
317+
_PyIO_State *state = get_io_state(module);
318318
{
319-
PyObject *RawIO_class = (PyObject *)&PyFileIO_Type;
319+
PyObject *RawIO_class = (PyObject *)state->PyFileIO_Type;
320320
#ifdef MS_WINDOWS
321321
const PyConfig *config = _Py_GetConfig();
322322
if (!config->legacy_windows_stdio && _PyIO_get_console_type(path_or_fd) != '\0') {
@@ -390,12 +390,15 @@ _io_open_impl(PyObject *module, PyObject *file, const char *mode,
390390
{
391391
PyObject *Buffered_class;
392392

393-
if (updating)
394-
Buffered_class = (PyObject *)&PyBufferedRandom_Type;
395-
else if (creating || writing || appending)
396-
Buffered_class = (PyObject *)&PyBufferedWriter_Type;
397-
else if (reading)
398-
Buffered_class = (PyObject *)&PyBufferedReader_Type;
393+
if (updating) {
394+
Buffered_class = (PyObject *)state->PyBufferedRandom_Type;
395+
}
396+
else if (creating || writing || appending) {
397+
Buffered_class = (PyObject *)state->PyBufferedWriter_Type;
398+
}
399+
else if (reading) {
400+
Buffered_class = (PyObject *)state->PyBufferedReader_Type;
401+
}
399402
else {
400403
PyErr_Format(PyExc_ValueError,
401404
"unknown mode: '%s'", mode);
@@ -417,7 +420,7 @@ _io_open_impl(PyObject *module, PyObject *file, const char *mode,
417420
}
418421

419422
/* wraps into a TextIOWrapper */
420-
wrapper = PyObject_CallFunction((PyObject *)&PyTextIOWrapper_Type,
423+
wrapper = PyObject_CallFunction((PyObject *)state->PyTextIOWrapper_Type,
421424
"OsssO",
422425
buffer,
423426
encoding, errors, newline,
@@ -558,14 +561,6 @@ PyNumber_AsOff_t(PyObject *item, PyObject *err)
558561
return result;
559562
}
560563

561-
static inline _PyIO_State*
562-
get_io_state(PyObject *module)
563-
{
564-
void *state = _PyModule_GetState(module);
565-
assert(state != NULL);
566-
return (_PyIO_State *)state;
567-
}
568-
569564
_PyIO_State *
570565
_PyIO_get_module_state(void)
571566
{
@@ -587,6 +582,15 @@ iomodule_traverse(PyObject *mod, visitproc visit, void *arg) {
587582
return 0;
588583
Py_VISIT(state->locale_module);
589584
Py_VISIT(state->unsupported_operation);
585+
586+
Py_VISIT(state->PyBufferedRWPair_Type);
587+
Py_VISIT(state->PyBufferedRandom_Type);
588+
Py_VISIT(state->PyBufferedReader_Type);
589+
Py_VISIT(state->PyBufferedWriter_Type);
590+
Py_VISIT(state->PyBytesIO_Type);
591+
Py_VISIT(state->PyFileIO_Type);
592+
Py_VISIT(state->PyStringIO_Type);
593+
Py_VISIT(state->PyTextIOWrapper_Type);
590594
return 0;
591595
}
592596

@@ -599,6 +603,15 @@ iomodule_clear(PyObject *mod) {
599603
if (state->locale_module != NULL)
600604
Py_CLEAR(state->locale_module);
601605
Py_CLEAR(state->unsupported_operation);
606+
607+
Py_CLEAR(state->PyBufferedRWPair_Type);
608+
Py_CLEAR(state->PyBufferedRandom_Type);
609+
Py_CLEAR(state->PyBufferedReader_Type);
610+
Py_CLEAR(state->PyBufferedWriter_Type);
611+
Py_CLEAR(state->PyBytesIO_Type);
612+
Py_CLEAR(state->PyFileIO_Type);
613+
Py_CLEAR(state->PyStringIO_Type);
614+
Py_CLEAR(state->PyTextIOWrapper_Type);
602615
return 0;
603616
}
604617

@@ -612,7 +625,9 @@ iomodule_free(PyObject *mod) {
612625
* Module definition
613626
*/
614627

628+
#define clinic_state() (get_io_state(module))
615629
#include "clinic/_iomodule.c.h"
630+
#undef clinic_state
616631

617632
static PyMethodDef module_methods[] = {
618633
_IO_OPEN_METHODDEF
@@ -644,23 +659,11 @@ static PyTypeObject* static_types[] = {
644659
&PyRawIOBase_Type,
645660
&PyTextIOBase_Type,
646661

647-
// PyBufferedIOBase_Type(PyIOBase_Type) subclasses
648-
&PyBytesIO_Type,
649-
&PyBufferedReader_Type,
650-
&PyBufferedWriter_Type,
651-
&PyBufferedRWPair_Type,
652-
&PyBufferedRandom_Type,
653-
654662
// PyRawIOBase_Type(PyIOBase_Type) subclasses
655-
&PyFileIO_Type,
656663
&_PyBytesIOBuffer_Type,
657664
#ifdef MS_WINDOWS
658665
&PyWindowsConsoleIO_Type,
659666
#endif
660-
661-
// PyTextIOBase_Type(PyIOBase_Type) subclasses
662-
&PyStringIO_Type,
663-
&PyTextIOWrapper_Type,
664667
};
665668

666669

@@ -673,6 +676,17 @@ _PyIO_Fini(void)
673676
}
674677
}
675678

679+
#define ADD_TYPE(module, type, spec, base) \
680+
do { \
681+
type = (PyTypeObject *)PyType_FromModuleAndSpec(module, spec, \
682+
(PyObject *)base); \
683+
if (type == NULL) { \
684+
goto fail; \
685+
} \
686+
if (PyModule_AddType(module, type) < 0) { \
687+
goto fail; \
688+
} \
689+
} while (0)
676690

677691
PyMODINIT_FUNC
678692
PyInit__io(void)
@@ -705,17 +719,9 @@ PyInit__io(void)
705719
}
706720

707721
// Set type base classes
708-
PyFileIO_Type.tp_base = &PyRawIOBase_Type;
709-
PyBytesIO_Type.tp_base = &PyBufferedIOBase_Type;
710-
PyStringIO_Type.tp_base = &PyTextIOBase_Type;
711722
#ifdef MS_WINDOWS
712723
PyWindowsConsoleIO_Type.tp_base = &PyRawIOBase_Type;
713724
#endif
714-
PyBufferedReader_Type.tp_base = &PyBufferedIOBase_Type;
715-
PyBufferedWriter_Type.tp_base = &PyBufferedIOBase_Type;
716-
PyBufferedRWPair_Type.tp_base = &PyBufferedIOBase_Type;
717-
PyBufferedRandom_Type.tp_base = &PyBufferedIOBase_Type;
718-
PyTextIOWrapper_Type.tp_base = &PyTextIOBase_Type;
719725

720726
// Add types
721727
for (size_t i=0; i < Py_ARRAY_LENGTH(static_types); i++) {
@@ -725,6 +731,25 @@ PyInit__io(void)
725731
}
726732
}
727733

734+
// PyBufferedIOBase_Type(PyIOBase_Type) subclasses
735+
ADD_TYPE(m, state->PyBytesIO_Type, &bytesio_spec, &PyBufferedIOBase_Type);
736+
ADD_TYPE(m, state->PyBufferedWriter_Type, &bufferedwriter_spec,
737+
&PyBufferedIOBase_Type);
738+
ADD_TYPE(m, state->PyBufferedReader_Type, &bufferedreader_spec,
739+
&PyBufferedIOBase_Type);
740+
ADD_TYPE(m, state->PyBufferedRWPair_Type, &bufferedrwpair_spec,
741+
&PyBufferedIOBase_Type);
742+
ADD_TYPE(m, state->PyBufferedRandom_Type, &bufferedrandom_spec,
743+
&PyBufferedIOBase_Type);
744+
745+
// PyRawIOBase_Type(PyIOBase_Type) subclasses
746+
ADD_TYPE(m, state->PyFileIO_Type, &fileio_spec, &PyRawIOBase_Type);
747+
748+
// PyTextIOBase_Type(PyIOBase_Type) subclasses
749+
ADD_TYPE(m, state->PyStringIO_Type, &stringio_spec, &PyTextIOBase_Type);
750+
ADD_TYPE(m, state->PyTextIOWrapper_Type, &textiowrapper_spec,
751+
&PyTextIOBase_Type);
752+
728753
state->initialized = 1;
729754

730755
return m;

‎Modules/_io/_iomodule.h

Copy file name to clipboardExpand all lines: Modules/_io/_iomodule.h
+39-8Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,28 @@
44

55
#include "exports.h"
66

7+
#include "pycore_moduleobject.h" // _PyModule_GetState()
8+
#include "structmember.h"
9+
710
/* ABCs */
811
extern PyTypeObject PyIOBase_Type;
912
extern PyTypeObject PyRawIOBase_Type;
1013
extern PyTypeObject PyBufferedIOBase_Type;
1114
extern PyTypeObject PyTextIOBase_Type;
1215

1316
/* Concrete classes */
14-
extern PyTypeObject PyFileIO_Type;
15-
extern PyTypeObject PyBytesIO_Type;
16-
extern PyTypeObject PyStringIO_Type;
17-
extern PyTypeObject PyBufferedReader_Type;
18-
extern PyTypeObject PyBufferedWriter_Type;
19-
extern PyTypeObject PyBufferedRWPair_Type;
20-
extern PyTypeObject PyBufferedRandom_Type;
21-
extern PyTypeObject PyTextIOWrapper_Type;
2217
extern PyTypeObject PyIncrementalNewlineDecoder_Type;
2318

19+
/* Type specs */
20+
extern PyType_Spec bufferedrandom_spec;
21+
extern PyType_Spec bufferedreader_spec;
22+
extern PyType_Spec bufferedrwpair_spec;
23+
extern PyType_Spec bufferedwriter_spec;
24+
extern PyType_Spec bytesio_spec;
25+
extern PyType_Spec fileio_spec;
26+
extern PyType_Spec stringio_spec;
27+
extern PyType_Spec textiowrapper_spec;
28+
2429
#ifdef MS_WINDOWS
2530
extern PyTypeObject PyWindowsConsoleIO_Type;
2631
#endif /* MS_WINDOWS */
@@ -140,11 +145,37 @@ typedef struct {
140145
PyObject *locale_module;
141146

142147
PyObject *unsupported_operation;
148+
149+
/* Types */
150+
PyTypeObject *PyBufferedRWPair_Type;
151+
PyTypeObject *PyBufferedRandom_Type;
152+
PyTypeObject *PyBufferedReader_Type;
153+
PyTypeObject *PyBufferedWriter_Type;
154+
PyTypeObject *PyBytesIO_Type;
155+
PyTypeObject *PyFileIO_Type;
156+
PyTypeObject *PyStringIO_Type;
157+
PyTypeObject *PyTextIOWrapper_Type;
143158
} _PyIO_State;
144159

145160
#define IO_MOD_STATE(mod) ((_PyIO_State *)PyModule_GetState(mod))
146161
#define IO_STATE() _PyIO_get_module_state()
147162

163+
static inline _PyIO_State *
164+
get_io_state(PyObject *module)
165+
{
166+
void *state = _PyModule_GetState(module);
167+
assert(state != NULL);
168+
return (_PyIO_State *)state;
169+
}
170+
171+
static inline _PyIO_State *
172+
find_io_state_by_def(PyTypeObject *type)
173+
{
174+
PyObject *mod = PyType_GetModuleByDef(type, &_PyIO_Module);
175+
assert(mod != NULL);
176+
return get_io_state(mod);
177+
}
178+
148179
extern _PyIO_State *_PyIO_get_module_state(void);
149180

150181
#ifdef MS_WINDOWS

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.