mirror of https://github.com/python/cpython.git
Rewritten PyImport_Cleanup() and its helper, clear_carefully(). They
now implement the following finalization strategy. 1. Whenever this code deletes a module, its directory is cleared carefully, as follows: - set all names to None that begin with exactly one underscore - set all names to None that don't begin with two underscores - clear the directory 2. Modules are deleted in the following order: - modules with a reference count of 1, except __builtin__ or __sys__ - repeat until no more are found with a reference count of 1 - __main__ if it's still there - all remaining modules except __builtin__ or sys - sys _ __builtin__
This commit is contained in:
parent
8095ebfc4a
commit
758eec0172
110
Python/import.c
110
Python/import.c
|
@ -141,6 +141,9 @@ clear_carefully(d)
|
|||
int pos;
|
||||
PyObject *key, *value;
|
||||
|
||||
Py_INCREF(d); /* Prevent it from being deleted recursively */
|
||||
|
||||
/* First, clear only names starting with a single underscore */
|
||||
pos = 0;
|
||||
while (PyDict_Next(d, &pos, &key, &value)) {
|
||||
if (value != Py_None && PyString_Check(key)) {
|
||||
|
@ -149,31 +152,114 @@ clear_carefully(d)
|
|||
PyDict_SetItem(d, key, Py_None);
|
||||
}
|
||||
}
|
||||
|
||||
PyDict_Clear(d);
|
||||
|
||||
/* Next, clear all names except those starting with two underscores */
|
||||
pos = 0;
|
||||
while (PyDict_Next(d, &pos, &key, &value)) {
|
||||
if (value != Py_None && PyString_Check(key)) {
|
||||
char *s = PyString_AsString(key);
|
||||
if (s[0] != '_' || s[1] != '_')
|
||||
PyDict_SetItem(d, key, Py_None);
|
||||
}
|
||||
}
|
||||
|
||||
PyDict_Clear(d); /* Finally, clear all names */
|
||||
|
||||
Py_DECREF(d); /* Match INCREF at top */
|
||||
}
|
||||
|
||||
|
||||
/* Un-initialize things, as good as we can */
|
||||
|
||||
void
|
||||
PyImport_Cleanup()
|
||||
{
|
||||
int pos, ndone;
|
||||
char *name;
|
||||
PyObject *key, *value, *dict;
|
||||
PyInterpreterState *interp = PyThreadState_Get()->interp;
|
||||
PyObject *tmp = interp->modules;
|
||||
if (tmp != NULL) {
|
||||
int pos;
|
||||
PyObject *key, *value;
|
||||
interp->modules = NULL;
|
||||
PyObject *modules = interp->modules;
|
||||
|
||||
if (modules == NULL)
|
||||
return; /* Already done */
|
||||
|
||||
/* The special treatment of __builtin__ 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 __builtin__
|
||||
module last. Likewise, we don't delete sys until the very
|
||||
end because it is implicitly referenced (e.g. by print).
|
||||
|
||||
Also note that we 'delete' modules by replacing their entry
|
||||
in the modules dict with None, rather than really deleting
|
||||
them; this avoids a rehash of the modules dictionary and
|
||||
also marks them as "non existent" so they won't be
|
||||
re-imported. */
|
||||
|
||||
/* First, repeatedly delete modules with a reference count of
|
||||
one (skipping __builtin__ and sys) and delete them */
|
||||
do {
|
||||
ndone = 0;
|
||||
pos = 0;
|
||||
while (PyDict_Next(tmp, &pos, &key, &value)) {
|
||||
while (PyDict_Next(modules, &pos, &key, &value)) {
|
||||
if (value->ob_refcnt != 1)
|
||||
continue;
|
||||
if (PyModule_Check(value)) {
|
||||
PyObject *d = PyModule_GetDict(value);
|
||||
clear_carefully(d);
|
||||
name = PyString_AsString(key);
|
||||
dict = PyModule_GetDict(value);
|
||||
if (strcmp(name, "__builtin__") == 0)
|
||||
continue;
|
||||
if (strcmp(name, "sys") == 0)
|
||||
continue;
|
||||
clear_carefully(dict);
|
||||
PyDict_SetItem(modules, key, Py_None);
|
||||
ndone++;
|
||||
}
|
||||
}
|
||||
PyDict_Clear(tmp);
|
||||
Py_DECREF(tmp);
|
||||
} while (ndone > 0);
|
||||
|
||||
/* Next, delete __main__ if it's still there */
|
||||
value = PyDict_GetItemString(modules, "__main__");
|
||||
if (value != NULL && PyModule_Check(value)) {
|
||||
dict = PyModule_GetDict(value);
|
||||
clear_carefully(dict);
|
||||
PyDict_SetItemString(modules, "__main__", Py_None);
|
||||
}
|
||||
|
||||
/* Next, delete all modules (still skipping __builtin__ and sys) */
|
||||
pos = 0;
|
||||
while (PyDict_Next(modules, &pos, &key, &value)) {
|
||||
if (PyModule_Check(value)) {
|
||||
name = PyString_AsString(key);
|
||||
dict = PyModule_GetDict(value);
|
||||
if (strcmp(name, "__builtin__") == 0)
|
||||
continue;
|
||||
if (strcmp(name, "sys") == 0)
|
||||
continue;
|
||||
clear_carefully(dict);
|
||||
PyDict_SetItem(modules, key, Py_None);
|
||||
}
|
||||
}
|
||||
|
||||
/* Next, delete sys and __builtin__ (in that order) */
|
||||
value = PyDict_GetItemString(modules, "sys");
|
||||
if (value != NULL && PyModule_Check(value)) {
|
||||
dict = PyModule_GetDict(value);
|
||||
clear_carefully(dict);
|
||||
PyDict_SetItemString(modules, "sys", Py_None);
|
||||
}
|
||||
value = PyDict_GetItemString(modules, "__builtin__");
|
||||
if (value != NULL && PyModule_Check(value)) {
|
||||
dict = PyModule_GetDict(value);
|
||||
clear_carefully(dict);
|
||||
PyDict_SetItemString(modules, "__builtin__", Py_None);
|
||||
}
|
||||
|
||||
/* Finally, clear and delete the modules directory */
|
||||
PyDict_Clear(modules);
|
||||
interp->modules = NULL;
|
||||
Py_DECREF(modules);
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue