Skip to content

Commit 9c8e490

Browse files
authored
bpo-46417: Clear _io module static objects at exit (GH-30807)
Add _PyIO_Fini() function, called by finalize_interp_clear(). It clears static objects used by the _io extension module.
1 parent 1626bf4 commit 9c8e490

File tree

2 files changed

+99
-44
lines changed

2 files changed

+99
-44
lines changed

Modules/_io/_iomodule.c

Lines changed: 93 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,82 @@ struct PyModuleDef _PyIO_Module = {
666666
(freefunc)iomodule_free,
667667
};
668668

669+
670+
static PyTypeObject* static_types[] = {
671+
// Base classes
672+
&PyIOBase_Type,
673+
&PyIncrementalNewlineDecoder_Type,
674+
675+
// PyIOBase_Type subclasses
676+
&PyBufferedIOBase_Type,
677+
&PyRawIOBase_Type,
678+
&PyTextIOBase_Type,
679+
680+
// PyBufferedIOBase_Type(PyIOBase_Type) subclasses
681+
&PyBytesIO_Type,
682+
&PyBufferedReader_Type,
683+
&PyBufferedWriter_Type,
684+
&PyBufferedRWPair_Type,
685+
&PyBufferedRandom_Type,
686+
687+
// PyRawIOBase_Type(PyIOBase_Type) subclasses
688+
&PyFileIO_Type,
689+
&_PyBytesIOBuffer_Type,
690+
#ifdef MS_WINDOWS
691+
&PyWindowsConsoleIO_Type,
692+
#endif
693+
694+
// PyTextIOBase_Type(PyIOBase_Type) subclasses
695+
&PyStringIO_Type,
696+
&PyTextIOWrapper_Type,
697+
};
698+
699+
700+
void
701+
_PyIO_Fini(void)
702+
{
703+
for (Py_ssize_t i=Py_ARRAY_LENGTH(static_types) - 1; i >= 0; i--) {
704+
PyTypeObject *exc = static_types[i];
705+
_PyStaticType_Dealloc(exc);
706+
}
707+
708+
/* Interned strings */
709+
#define CLEAR_INTERNED(name) \
710+
Py_CLEAR(_PyIO_str_ ## name)
711+
712+
CLEAR_INTERNED(close);
713+
CLEAR_INTERNED(closed);
714+
CLEAR_INTERNED(decode);
715+
CLEAR_INTERNED(encode);
716+
CLEAR_INTERNED(fileno);
717+
CLEAR_INTERNED(flush);
718+
CLEAR_INTERNED(getstate);
719+
CLEAR_INTERNED(isatty);
720+
CLEAR_INTERNED(locale);
721+
CLEAR_INTERNED(newlines);
722+
CLEAR_INTERNED(peek);
723+
CLEAR_INTERNED(read);
724+
CLEAR_INTERNED(read1);
725+
CLEAR_INTERNED(readable);
726+
CLEAR_INTERNED(readall);
727+
CLEAR_INTERNED(readinto);
728+
CLEAR_INTERNED(readline);
729+
CLEAR_INTERNED(reset);
730+
CLEAR_INTERNED(seek);
731+
CLEAR_INTERNED(seekable);
732+
CLEAR_INTERNED(setstate);
733+
CLEAR_INTERNED(tell);
734+
CLEAR_INTERNED(truncate);
735+
CLEAR_INTERNED(write);
736+
CLEAR_INTERNED(writable);
737+
#undef CLEAR_INTERNED
738+
739+
Py_CLEAR(_PyIO_str_nl);
740+
Py_CLEAR(_PyIO_empty_str);
741+
Py_CLEAR(_PyIO_empty_bytes);
742+
}
743+
744+
669745
PyMODINIT_FUNC
670746
PyInit__io(void)
671747
{
@@ -676,11 +752,6 @@ PyInit__io(void)
676752
state = get_io_state(m);
677753
state->initialized = 0;
678754

679-
#define ADD_TYPE(type) \
680-
if (PyModule_AddType(m, type) < 0) { \
681-
goto fail; \
682-
}
683-
684755
/* DEFAULT_BUFFER_SIZE */
685756
if (PyModule_AddIntMacro(m, DEFAULT_BUFFER_SIZE) < 0)
686757
goto fail;
@@ -702,57 +773,34 @@ PyInit__io(void)
702773
(PyObject *) PyExc_BlockingIOError) < 0)
703774
goto fail;
704775

705-
/* Concrete base types of the IO ABCs.
706-
(the ABCs themselves are declared through inheritance in io.py)
707-
*/
708-
ADD_TYPE(&PyIOBase_Type);
709-
ADD_TYPE(&PyRawIOBase_Type);
710-
ADD_TYPE(&PyBufferedIOBase_Type);
711-
ADD_TYPE(&PyTextIOBase_Type);
712-
713-
/* Implementation of concrete IO objects. */
714-
/* FileIO */
776+
// Set type base classes
715777
PyFileIO_Type.tp_base = &PyRawIOBase_Type;
716-
ADD_TYPE(&PyFileIO_Type);
717-
718-
/* BytesIO */
719778
PyBytesIO_Type.tp_base = &PyBufferedIOBase_Type;
720-
ADD_TYPE(&PyBytesIO_Type);
721-
if (PyType_Ready(&_PyBytesIOBuffer_Type) < 0)
722-
goto fail;
723-
724-
/* StringIO */
725779
PyStringIO_Type.tp_base = &PyTextIOBase_Type;
726-
ADD_TYPE(&PyStringIO_Type);
727-
728780
#ifdef MS_WINDOWS
729-
/* WindowsConsoleIO */
730781
PyWindowsConsoleIO_Type.tp_base = &PyRawIOBase_Type;
731-
ADD_TYPE(&PyWindowsConsoleIO_Type);
732782
#endif
733-
734-
/* BufferedReader */
735783
PyBufferedReader_Type.tp_base = &PyBufferedIOBase_Type;
736-
ADD_TYPE(&PyBufferedReader_Type);
737-
738-
/* BufferedWriter */
739784
PyBufferedWriter_Type.tp_base = &PyBufferedIOBase_Type;
740-
ADD_TYPE(&PyBufferedWriter_Type);
741-
742-
/* BufferedRWPair */
743785
PyBufferedRWPair_Type.tp_base = &PyBufferedIOBase_Type;
744-
ADD_TYPE(&PyBufferedRWPair_Type);
745-
746-
/* BufferedRandom */
747786
PyBufferedRandom_Type.tp_base = &PyBufferedIOBase_Type;
748-
ADD_TYPE(&PyBufferedRandom_Type);
749-
750-
/* TextIOWrapper */
751787
PyTextIOWrapper_Type.tp_base = &PyTextIOBase_Type;
752-
ADD_TYPE(&PyTextIOWrapper_Type);
753788

754-
/* IncrementalNewlineDecoder */
755-
ADD_TYPE(&PyIncrementalNewlineDecoder_Type);
789+
// Add types
790+
for (size_t i=0; i < Py_ARRAY_LENGTH(static_types); i++) {
791+
PyTypeObject *type = static_types[i];
792+
// Private type not exposed in the _io module
793+
if (type == &_PyBytesIOBuffer_Type) {
794+
if (PyType_Ready(type) < 0) {
795+
goto fail;
796+
}
797+
}
798+
else {
799+
if (PyModule_AddType(m, type) < 0) {
800+
goto fail;
801+
}
802+
}
803+
}
756804

757805
/* Interned strings */
758806
#define ADD_INTERNED(name) \
@@ -785,6 +833,7 @@ PyInit__io(void)
785833
ADD_INTERNED(truncate)
786834
ADD_INTERNED(write)
787835
ADD_INTERNED(writable)
836+
#undef ADD_INTERNED
788837

789838
if (!_PyIO_str_nl &&
790839
!(_PyIO_str_nl = PyUnicode_InternFromString("\n")))

Python/pylifecycle.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
#include "pycore_typeobject.h" // _PyTypes_InitTypes()
3030
#include "pycore_unicodeobject.h" // _PyUnicode_InitTypes()
3131

32+
extern void _PyIO_Fini(void);
33+
3234
#include <locale.h> // setlocale()
3335
#include <stdlib.h> // getenv()
3436

@@ -1702,6 +1704,10 @@ finalize_interp_clear(PyThreadState *tstate)
17021704
/* Clear interpreter state and all thread states */
17031705
_PyInterpreterState_Clear(tstate);
17041706

1707+
if (is_main_interp) {
1708+
_PyIO_Fini();
1709+
}
1710+
17051711
/* Clear all loghooks */
17061712
/* Both _PySys_Audit function and users still need PyObject, such as tuple.
17071713
Call _PySys_ClearAuditHooks when PyObject available. */

0 commit comments

Comments
 (0)