8000 bpo-42208: Move _PyImport_Cleanup() to pylifecycle.c by vstinner · Pull Request #23040 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

bpo-42208: Move _PyImport_Cleanup() to pylifecycle.c #23040

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
227 changes: 0 additions & 227 deletions Python/import.c
10000 10000
Original file line number Diff line number Diff line change
Expand Up @@ -406,233 +406,6 @@ import_ensure_initialized(PyThreadState *tstate, PyObject *mod, PyObject *name)
}


/* List of names to clear in sys */
static const char * const sys_deletes[] = {
"path", "argv", "ps1", "ps2",
"last_type", "last_value", "last_traceback",
"path_hooks", "path_importer_cache", "meta_path",
"__interactivehook__",
NULL
};

static const char * const sys_files[] = {
"stdin", "__stdin__",
"stdout", "__stdout__",
"stderr", "__stderr__",
NULL
};

/* Un-initialize things, as good as we can */

void
_PyImport_Cleanup(PyThreadState *tstate)
{
PyInterpreterState *interp = tstate->interp;
PyObject *modules = interp->modules;
if (modules == NULL) {
/* Already done */
return;
}

/* Delete some special variables first. These are common
places where user values hide and people complain when their
destructors fail. Since the modules containing them are
deleted *last* of all, they would come too late in the normal
destruction order. Sigh. */

/* XXX Perhaps these precautions are obsolete. Who knows? */

int verbose = _PyInterpreterState_GetConfig(interp)->verbose;
if (verbose) {
PySys_WriteStderr("# clear builtins._\n");
}
if (PyDict_SetItemString(interp->builtins, "_", Py_None) < 0) {
PyErr_WriteUnraisable(NULL);
}

const char * const *p;
for (p = sys_deletes; *p != NULL; p++) {
if (verbose) {
PySys_WriteStderr("# clear sys.%s\n", *p);
}
if (PyDict_SetItemString(interp->sysdict, *p, Py_None) < 0) {
PyErr_WriteUnraisable(NULL);
}
}
for (p = sys_files; *p != NULL; p+=2) {
if (verbose) {
PySys_WriteStderr("# restore sys.%s\n", *p);
}
PyObject *value = _PyDict_GetItemStringWithError(interp->sysdict,
*(p+1));
if (value == NULL) {
if (_PyErr_Occurred(tstate)) {
PyErr_WriteUnraisable(NULL);
}
value = Py_None;
}
if (PyDict_SetItemString(interp->sysdict, *p, value) < 0) {
PyErr_WriteUnraisable(NULL);
}
}

/* We prepare a list which will receive (name, weakref) tuples of
modules when they are removed from sys.modules. The name is used
for diagnosis messages (in verbose mode), while the weakref helps
detect those modules which have been held alive. */
PyObject *weaklist = PyList_New(0);
if (weaklist == NULL) {
PyErr_WriteUnraisable(NULL);
}

#define STORE_MODULE_WEAKREF(name, mod) \
if (weaklist != NULL) { \
PyObject *wr = PyWeakref_NewRef(mod, NULL); \
if (wr) { \
PyObject *tup = PyTuple_Pack(2, name, wr); \
if (!tup || PyList_Append(weaklist, tup) < 0) { \
PyErr_WriteUnraisable(NULL); \
} \
Py_XDECREF(tup); \
Py_DECREF(wr); \
} \
else { \
PyErr_WriteUnraisable(NULL); \
} \
}
#define CLEAR_MODULE(name, mod) \
if (PyModule_Check(mod)) { \
if (verbose && PyUnicode_Check(name)) { \
PySys_FormatStderr("# cleanup[2] removing %U\n", name); \
} \
STORE_MODULE_WEAKREF(name, mod); \
if (PyObject_SetItem(modules, name, Py_None) < 0) { \
PyErr_WriteUnraisable(NULL); \
} \
}

/* Remove all modules from sys.modules, hoping that garbage collection
can reclaim most of them. */
if (PyDict_CheckExact(modules)) {
Py_ssize_t pos = 0;
PyObject *key, *value;
while (PyDict_Next(modules, &pos, &key, &value)) {
CLEAR_MODULE(key, value);
}
}
else {
PyObject *iterator = PyObject_GetIter(modules);
if (iterator == NULL) {
PyErr_WriteUnraisable(NULL);
}
else {
PyObject *key;
while ((key = PyIter_Next(iterator))) {
PyObject *value = PyObject_GetItem(modules, key);
if (value == NULL) {
PyErr_WriteUnraisable(NULL);
continue;
}
CLEAR_MODULE(key, value);
Py_DECREF(value);
Py_DECREF(key);
}
if (PyErr_Occurred()) {
PyErr_WriteUnraisable(NULL);
}
Py_DECREF(iterator);
}
}

/* Clear the modules dict. */
if (PyDict_CheckExact(modules)) {
PyDict_Clear(modules);
}
else {
_Py_IDENTIFIER(clear);
if (_PyObject_CallMethodIdNoArgs(modules, &PyId_clear) == NULL) {
PyErr_WriteUnraisable(NULL);
}
}
/* Restore the original builtins dict, to ensure that any
user data gets cleared. */
PyObject *dict = PyDict_Copy(interp->builtins);
if (dict == NULL) {
PyErr_WriteUnraisable(NULL);
}
PyDict_Clear(interp->builtins);
if (PyDict_Update(interp->builtins, interp->builtins_copy)) {
_PyErr_Clear(tstate);
}
Py_XDECREF(dict);
/* Collect references */
_PyGC_CollectNoFail(tstate);
/* Dump GC stats before it's too late, since it uses the warnings
machinery. */
_PyGC_DumpShutdownStats(tstate);

/* Now, if there are any modules left alive, clear their globals to
minimize potential leaks. All C extension modules actually end
up here, since they are kept alive in the interpreter state.

The special treatment of "builtins" here is because even
when it's not referenced as a module, its dictionary is
referenced by almost every module's __builtins__. Since
deleting a module clears its dictionary (even if there are
references left to it), we need to delete the "builtins"
module last. Likewise, we don't delete sys until the very
end because it is implicitly referenced (e.g. by print). */
if (weaklist != NULL) {
Py_ssize_t i;
/* Since dict is ordered in CPython 3.6+, modules are saved in
importing order. First clear modules imported later. */
for (i = PyList_GET_SIZE(weaklist) - 1; i >= 0; i--) {
PyObject *tup = PyList_GET_ITEM(weaklist, i);
PyObject *name = PyTuple_GET_ITEM(tup, 0);
PyObject *mod = PyWeakref_GET_OBJECT(PyTuple_GET_ITEM(tup, 1));
if (mod == Py_None)
continue;
assert(PyModule_Check(mod));
dict = PyModule_GetDict(mod);
if (dict == interp->builtins || dict == interp->sysdict)
continue;
Py_INCREF(mod);
if (verbose && PyUnicode_Check(name)) {
PySys_FormatStderr("# cleanup[3] wiping %U\n", name);
}
_PyModule_Clear(mod);
Py_DECREF(mod);
}
Py_DECREF(weaklist);
}

/* Next, delete sys and builtins (in that order) */
if (verbose) {
PySys_FormatStderr("# cleanup[3] wiping sys\n");
}
_PyModule_ClearDict(interp->sysdict);
if (verbose) {
PySys_FormatStderr("# cleanup[3] wiping builtins\n");
}
_PyModule_ClearDict(interp->builtins);

/* Clear module dict copies stored in the interpreter state */
_PyInterpreterState_ClearModules(interp);

/* Clear and delete the modules directory. Actual modules will
still be there only if imported during the execution of some
destructor. */
interp->modules = NULL;
Py_DECREF(modules);

/* Once more */
_PyGC_CollectNoFail(tstate);

#undef CLEAR_MODULE
#undef STORE_MODULE_WEAKREF
}


/* Helper for pythonrun.c -- return magic number and tag. */

long
Expand Down
Loading
0