mirror of
https://github.com/python/cpython.git
synced 2025-08-04 00:48:58 +00:00
Issue #18214: Improve finalization of Python modules to avoid setting their globals to None, in most cases.
This commit is contained in:
parent
c27cd71cd7
commit
dcedaf6e53
9 changed files with 178 additions and 109 deletions
|
@ -29,6 +29,7 @@ Notes:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import atexit
|
||||||
import builtins
|
import builtins
|
||||||
import __main__
|
import __main__
|
||||||
|
|
||||||
|
@ -158,3 +159,8 @@ except ImportError:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
readline.set_completer(Completer().complete)
|
readline.set_completer(Completer().complete)
|
||||||
|
# Release references early at shutdown (the readline module's
|
||||||
|
# contents are quasi-immortal, and the completer function holds a
|
||||||
|
# reference to globals).
|
||||||
|
atexit.register(lambda: readline.set_completer(None))
|
||||||
|
|
||||||
|
|
39
Lib/site.py
39
Lib/site.py
|
@ -68,6 +68,7 @@ site-specific customizations. If this import fails with an
|
||||||
ImportError exception, it is silently ignored.
|
ImportError exception, it is silently ignored.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import atexit
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
@ -86,6 +87,25 @@ USER_SITE = None
|
||||||
USER_BASE = None
|
USER_BASE = None
|
||||||
|
|
||||||
|
|
||||||
|
_no_builtin = object()
|
||||||
|
|
||||||
|
def _patch_builtins(**items):
|
||||||
|
# When patching builtins, we make some objects almost immortal
|
||||||
|
# (builtins are only reclaimed at the very end of the interpreter
|
||||||
|
# shutdown sequence). To avoid keeping to many references alive,
|
||||||
|
# we register callbacks to undo our builtins additions.
|
||||||
|
old_items = {k: getattr(builtins, k, _no_builtin) for k in items}
|
||||||
|
def unpatch(old_items=old_items):
|
||||||
|
for k, v in old_items.items():
|
||||||
|
if v is _no_builtin:
|
||||||
|
delattr(builtins, k)
|
||||||
|
else:
|
||||||
|
setattr(builtins, k, v)
|
||||||
|
for k, v in items.items():
|
||||||
|
setattr(builtins, k, v)
|
||||||
|
atexit.register(unpatch)
|
||||||
|
|
||||||
|
|
||||||
def makepath(*paths):
|
def makepath(*paths):
|
||||||
dir = os.path.join(*paths)
|
dir = os.path.join(*paths)
|
||||||
try:
|
try:
|
||||||
|
@ -357,8 +377,7 @@ def setquit():
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
raise SystemExit(code)
|
raise SystemExit(code)
|
||||||
builtins.quit = Quitter('quit')
|
_patch_builtins(quit=Quitter('quit'), exit=Quitter('exit'))
|
||||||
builtins.exit = Quitter('exit')
|
|
||||||
|
|
||||||
|
|
||||||
class _Printer(object):
|
class _Printer(object):
|
||||||
|
@ -423,20 +442,20 @@ class _Printer(object):
|
||||||
|
|
||||||
def setcopyright():
|
def setcopyright():
|
||||||
"""Set 'copyright' and 'credits' in builtins"""
|
"""Set 'copyright' and 'credits' in builtins"""
|
||||||
builtins.copyright = _Printer("copyright", sys.copyright)
|
_patch_builtins(copyright=_Printer("copyright", sys.copyright))
|
||||||
if sys.platform[:4] == 'java':
|
if sys.platform[:4] == 'java':
|
||||||
builtins.credits = _Printer(
|
_patch_builtins(credits=_Printer(
|
||||||
"credits",
|
"credits",
|
||||||
"Jython is maintained by the Jython developers (www.jython.org).")
|
"Jython is maintained by the Jython developers (www.jython.org)."))
|
||||||
else:
|
else:
|
||||||
builtins.credits = _Printer("credits", """\
|
_patch_builtins(credits=_Printer("credits", """\
|
||||||
Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands
|
Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands
|
||||||
for supporting Python development. See www.python.org for more information.""")
|
for supporting Python development. See www.python.org for more information."""))
|
||||||
here = os.path.dirname(os.__file__)
|
here = os.path.dirname(os.__file__)
|
||||||
builtins.license = _Printer(
|
_patch_builtins(license=_Printer(
|
||||||
"license", "See http://www.python.org/%.3s/license.html" % sys.version,
|
"license", "See http://www.python.org/%.3s/license.html" % sys.version,
|
||||||
["LICENSE.txt", "LICENSE"],
|
["LICENSE.txt", "LICENSE"],
|
||||||
[os.path.join(here, os.pardir), here, os.curdir])
|
[os.path.join(here, os.pardir), here, os.curdir]))
|
||||||
|
|
||||||
|
|
||||||
class _Helper(object):
|
class _Helper(object):
|
||||||
|
@ -453,7 +472,7 @@ class _Helper(object):
|
||||||
return pydoc.help(*args, **kwds)
|
return pydoc.help(*args, **kwds)
|
||||||
|
|
||||||
def sethelper():
|
def sethelper():
|
||||||
builtins.help = _Helper()
|
_patch_builtins(help=_Helper())
|
||||||
|
|
||||||
def enablerlcompleter():
|
def enablerlcompleter():
|
||||||
"""Enable default readline configuration on interactive prompts, by
|
"""Enable default readline configuration on interactive prompts, by
|
||||||
|
|
19
Lib/test/final_a.py
Normal file
19
Lib/test/final_a.py
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
"""
|
||||||
|
Fodder for module finalization tests in test_module.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import shutil
|
||||||
|
import test.final_b
|
||||||
|
|
||||||
|
x = 'a'
|
||||||
|
|
||||||
|
class C:
|
||||||
|
def __del__(self):
|
||||||
|
# Inspect module globals and builtins
|
||||||
|
print("x =", x)
|
||||||
|
print("final_b.x =", test.final_b.x)
|
||||||
|
print("shutil.rmtree =", getattr(shutil.rmtree, '__name__', None))
|
||||||
|
print("len =", getattr(len, '__name__', None))
|
||||||
|
|
||||||
|
c = C()
|
||||||
|
_underscored = C()
|
19
Lib/test/final_b.py
Normal file
19
Lib/test/final_b.py
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
"""
|
||||||
|
Fodder for module finalization tests in test_module.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import shutil
|
||||||
|
import test.final_a
|
||||||
|
|
||||||
|
x = 'b'
|
||||||
|
|
||||||
|
class C:
|
||||||
|
def __del__(self):
|
||||||
|
# Inspect module globals and builtins
|
||||||
|
print("x =", x)
|
||||||
|
print("final_a.x =", test.final_a.x)
|
||||||
|
print("shutil.rmtree =", getattr(shutil.rmtree, '__name__', None))
|
||||||
|
print("len =", getattr(len, '__name__', None))
|
||||||
|
|
||||||
|
c = C()
|
||||||
|
_underscored = C()
|
|
@ -1,6 +1,7 @@
|
||||||
# Test the module type
|
# Test the module type
|
||||||
import unittest
|
import unittest
|
||||||
from test.support import run_unittest, gc_collect
|
from test.support import run_unittest, gc_collect
|
||||||
|
from test.script_helper import assert_python_ok
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
ModuleType = type(sys)
|
ModuleType = type(sys)
|
||||||
|
@ -70,7 +71,6 @@ class ModuleTests(unittest.TestCase):
|
||||||
"__loader__": None, "__package__": None})
|
"__loader__": None, "__package__": None})
|
||||||
self.assertTrue(foo.__dict__ is d)
|
self.assertTrue(foo.__dict__ is d)
|
||||||
|
|
||||||
@unittest.expectedFailure
|
|
||||||
def test_dont_clear_dict(self):
|
def test_dont_clear_dict(self):
|
||||||
# See issue 7140.
|
# See issue 7140.
|
||||||
def f():
|
def f():
|
||||||
|
@ -181,6 +181,19 @@ a = A(destroyed)"""
|
||||||
self.assertEqual(r[:25], "<module 'unittest' from '")
|
self.assertEqual(r[:25], "<module 'unittest' from '")
|
||||||
self.assertEqual(r[-13:], "__init__.py'>")
|
self.assertEqual(r[-13:], "__init__.py'>")
|
||||||
|
|
||||||
|
def test_module_finalization_at_shutdown(self):
|
||||||
|
# Module globals and builtins should still be available during shutdown
|
||||||
|
rc, out, err = assert_python_ok("-c", "from test import final_a")
|
||||||
|
self.assertFalse(err)
|
||||||
|
lines = out.splitlines()
|
||||||
|
self.assertEqual(set(lines), {
|
||||||
|
b"x = a",
|
||||||
|
b"x = b",
|
||||||
|
b"final_a.x = a",
|
||||||
|
b"final_b.x = b",
|
||||||
|
b"len = len",
|
||||||
|
b"shutil.rmtree = rmtree"})
|
||||||
|
|
||||||
# frozen and namespace module reprs are tested in importlib.
|
# frozen and namespace module reprs are tested in importlib.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -810,7 +810,7 @@ class SizeofTest(unittest.TestCase):
|
||||||
# memoryview
|
# memoryview
|
||||||
check(memoryview(b''), size('Pnin 2P2n2i5P 3cPn'))
|
check(memoryview(b''), size('Pnin 2P2n2i5P 3cPn'))
|
||||||
# module
|
# module
|
||||||
check(unittest, size('PnP'))
|
check(unittest, size('PnPPP'))
|
||||||
# None
|
# None
|
||||||
check(None, size(''))
|
check(None, size(''))
|
||||||
# NotImplementedType
|
# NotImplementedType
|
||||||
|
|
|
@ -10,6 +10,9 @@ What's New in Python 3.4.0 Alpha 1?
|
||||||
Core and Builtins
|
Core and Builtins
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- Issue #18214: Improve finalization of Python modules to avoid setting
|
||||||
|
their globals to None, in most cases.
|
||||||
|
|
||||||
- Issue #18112: PEP 442 implementation (safe object finalization).
|
- Issue #18112: PEP 442 implementation (safe object finalization).
|
||||||
|
|
||||||
- Issue #18552: Check return value of PyArena_AddPyObject() in
|
- Issue #18552: Check return value of PyArena_AddPyObject() in
|
||||||
|
|
|
@ -11,6 +11,8 @@ typedef struct {
|
||||||
PyObject *md_dict;
|
PyObject *md_dict;
|
||||||
struct PyModuleDef *md_def;
|
struct PyModuleDef *md_def;
|
||||||
void *md_state;
|
void *md_state;
|
||||||
|
PyObject *md_weaklist;
|
||||||
|
PyObject *md_name; /* for logging purposes after md_dict is cleared */
|
||||||
} PyModuleObject;
|
} PyModuleObject;
|
||||||
|
|
||||||
static PyMemberDef module_members[] = {
|
static PyMemberDef module_members[] = {
|
||||||
|
@ -27,7 +29,8 @@ static PyTypeObject moduledef_type = {
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
module_init_dict(PyObject *md_dict, PyObject *name, PyObject *doc)
|
module_init_dict(PyModuleObject *mod, PyObject *md_dict,
|
||||||
|
PyObject *name, PyObject *doc)
|
||||||
{
|
{
|
||||||
if (md_dict == NULL)
|
if (md_dict == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -42,6 +45,11 @@ module_init_dict(PyObject *md_dict, PyObject *name, PyObject *doc)
|
||||||
return -1;
|
return -1;
|
||||||
if (PyDict_SetItemString(md_dict, "__loader__", Py_None) != 0)
|
if (PyDict_SetItemString(md_dict, "__loader__", Py_None) != 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
if (PyUnicode_CheckExact(name)) {
|
||||||
|
Py_INCREF(name);
|
||||||
|
Py_XDECREF(mod->md_name);
|
||||||
|
mod->md_name = name;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -56,8 +64,10 @@ PyModule_NewObject(PyObject *name)
|
||||||
return NULL;
|
return NULL;
|
||||||
m->md_def = NULL;
|
m->md_def = NULL;
|
||||||
m->md_state = NULL;
|
m->md_state = NULL;
|
||||||
|
m->md_weaklist = NULL;
|
||||||
|
m->md_name = NULL;
|
||||||
m->md_dict = PyDict_New();
|
m->md_dict = PyDict_New();
|
||||||
if (module_init_dict(m->md_dict, name, NULL) != 0)
|
if (module_init_dict(m, m->md_dict, name, NULL) != 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
PyObject_GC_Track(m);
|
PyObject_GC_Track(m);
|
||||||
return (PyObject *)m;
|
return (PyObject *)m;
|
||||||
|
@ -362,7 +372,7 @@ module_init(PyModuleObject *m, PyObject *args, PyObject *kwds)
|
||||||
return -1;
|
return -1;
|
||||||
m->md_dict = dict;
|
m->md_dict = dict;
|
||||||
}
|
}
|
||||||
if (module_init_dict(dict, name, doc) < 0)
|
if (module_init_dict(m, dict, name, doc) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -371,12 +381,15 @@ static void
|
||||||
module_dealloc(PyModuleObject *m)
|
module_dealloc(PyModuleObject *m)
|
||||||
{
|
{
|
||||||
PyObject_GC_UnTrack(m);
|
PyObject_GC_UnTrack(m);
|
||||||
|
if (Py_VerboseFlag && m->md_name) {
|
||||||
|
PySys_FormatStderr("# destroy %S\n", m->md_name);
|
||||||
|
}
|
||||||
|
if (m->md_weaklist != NULL)
|
||||||
|
PyObject_ClearWeakRefs((PyObject *) m);
|
||||||
if (m->md_def && m->md_def->m_free)
|
if (m->md_def && m->md_def->m_free)
|
||||||
m->md_def->m_free(m);
|
m->md_def->m_free(m);
|
||||||
if (m->md_dict != NULL) {
|
Py_XDECREF(m->md_dict);
|
||||||
_PyModule_Clear((PyObject *)m);
|
Py_XDECREF(m->md_name);
|
||||||
Py_DECREF(m->md_dict);
|
|
||||||
}
|
|
||||||
if (m->md_state != NULL)
|
if (m->md_state != NULL)
|
||||||
PyMem_FREE(m->md_state);
|
PyMem_FREE(m->md_state);
|
||||||
Py_TYPE(m)->tp_free((PyObject *)m);
|
Py_TYPE(m)->tp_free((PyObject *)m);
|
||||||
|
@ -522,7 +535,7 @@ PyTypeObject PyModule_Type = {
|
||||||
(traverseproc)module_traverse, /* tp_traverse */
|
(traverseproc)module_traverse, /* tp_traverse */
|
||||||
(inquiry)module_clear, /* tp_clear */
|
(inquiry)module_clear, /* tp_clear */
|
||||||
0, /* tp_richcompare */
|
0, /* tp_richcompare */
|
||||||
0, /* tp_weaklistoffset */
|
offsetof(PyModuleObject, md_weaklist), /* tp_weaklistoffset */
|
||||||
0, /* tp_iter */
|
0, /* tp_iter */
|
||||||
0, /* tp_iternext */
|
0, /* tp_iternext */
|
||||||
module_methods, /* tp_methods */
|
module_methods, /* tp_methods */
|
||||||
|
|
155
Python/import.c
155
Python/import.c
|
@ -277,6 +277,7 @@ static char* sys_deletes[] = {
|
||||||
"path", "argv", "ps1", "ps2",
|
"path", "argv", "ps1", "ps2",
|
||||||
"last_type", "last_value", "last_traceback",
|
"last_type", "last_value", "last_traceback",
|
||||||
"path_hooks", "path_importer_cache", "meta_path",
|
"path_hooks", "path_importer_cache", "meta_path",
|
||||||
|
"__interactivehook__",
|
||||||
/* misc stuff */
|
/* misc stuff */
|
||||||
"flags", "float_info",
|
"flags", "float_info",
|
||||||
NULL
|
NULL
|
||||||
|
@ -289,40 +290,17 @@ static char* sys_files[] = {
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
static int
|
|
||||||
is_essential_module(PyObject *name)
|
|
||||||
{
|
|
||||||
Py_ssize_t name_len;
|
|
||||||
char *name_str = PyUnicode_AsUTF8AndSize(name, &name_len);
|
|
||||||
|
|
||||||
if (name_str == NULL) {
|
|
||||||
PyErr_Clear();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (strcmp(name_str, "builtins") == 0)
|
|
||||||
return 1;
|
|
||||||
if (strcmp(name_str, "sys") == 0)
|
|
||||||
return 1;
|
|
||||||
/* These are all needed for stderr to still function */
|
|
||||||
if (strcmp(name_str, "codecs") == 0)
|
|
||||||
return 1;
|
|
||||||
if (strcmp(name_str, "_codecs") == 0)
|
|
||||||
return 1;
|
|
||||||
if (strncmp(name_str, "encodings.", 10) == 0)
|
|
||||||
return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Un-initialize things, as good as we can */
|
/* Un-initialize things, as good as we can */
|
||||||
|
|
||||||
void
|
void
|
||||||
PyImport_Cleanup(void)
|
PyImport_Cleanup(void)
|
||||||
{
|
{
|
||||||
Py_ssize_t pos, ndone;
|
Py_ssize_t pos;
|
||||||
PyObject *key, *value, *dict;
|
PyObject *key, *value, *dict;
|
||||||
PyInterpreterState *interp = PyThreadState_GET()->interp;
|
PyInterpreterState *interp = PyThreadState_GET()->interp;
|
||||||
PyObject *modules = interp->modules;
|
PyObject *modules = interp->modules;
|
||||||
|
PyObject *builtins = interp->builtins;
|
||||||
|
PyObject *weaklist = NULL;
|
||||||
|
|
||||||
if (modules == NULL)
|
if (modules == NULL)
|
||||||
return; /* Already done */
|
return; /* Already done */
|
||||||
|
@ -333,6 +311,8 @@ PyImport_Cleanup(void)
|
||||||
deleted *last* of all, they would come too late in the normal
|
deleted *last* of all, they would come too late in the normal
|
||||||
destruction order. Sigh. */
|
destruction order. Sigh. */
|
||||||
|
|
||||||
|
/* XXX Perhaps these precautions are obsolete. Who knows? */
|
||||||
|
|
||||||
value = PyDict_GetItemString(modules, "builtins");
|
value = PyDict_GetItemString(modules, "builtins");
|
||||||
if (value != NULL && PyModule_Check(value)) {
|
if (value != NULL && PyModule_Check(value)) {
|
||||||
dict = PyModule_GetDict(value);
|
dict = PyModule_GetDict(value);
|
||||||
|
@ -360,87 +340,84 @@ PyImport_Cleanup(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* First, delete __main__ */
|
/* We prepare a list which will receive (name, weakref) tuples of
|
||||||
value = PyDict_GetItemString(modules, "__main__");
|
modules when they are removed from sys.modules. The name is used
|
||||||
if (value != NULL && PyModule_Check(value)) {
|
for diagnosis messages (in verbose mode), while the weakref helps
|
||||||
if (Py_VerboseFlag)
|
detect those modules which have been held alive. */
|
||||||
PySys_WriteStderr("# cleanup __main__\n");
|
weaklist = PyList_New(0);
|
||||||
_PyModule_Clear(value);
|
|
||||||
PyDict_SetItemString(modules, "__main__", Py_None);
|
#define STORE_MODULE_WEAKREF(mod) \
|
||||||
|
if (weaklist != NULL) { \
|
||||||
|
PyObject *name = PyModule_GetNameObject(mod); \
|
||||||
|
PyObject *wr = PyWeakref_NewRef(mod, NULL); \
|
||||||
|
if (name && wr) { \
|
||||||
|
PyObject *tup = PyTuple_Pack(2, name, wr); \
|
||||||
|
PyList_Append(weaklist, tup); \
|
||||||
|
Py_XDECREF(tup); \
|
||||||
|
} \
|
||||||
|
Py_XDECREF(name); \
|
||||||
|
Py_XDECREF(wr); \
|
||||||
|
if (PyErr_Occurred()) \
|
||||||
|
PyErr_Clear(); \
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The special treatment of "builtins" here is because even
|
/* Remove all modules from sys.modules, hoping that garbage collection
|
||||||
when it's not referenced as a module, its dictionary is
|
can reclaim most of them. */
|
||||||
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).
|
|
||||||
|
|
||||||
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. */
|
|
||||||
|
|
||||||
/* Next, repeatedly delete modules with a reference count of
|
|
||||||
one (skipping builtins and sys) and delete them */
|
|
||||||
do {
|
|
||||||
ndone = 0;
|
|
||||||
pos = 0;
|
|
||||||
while (PyDict_Next(modules, &pos, &key, &value)) {
|
|
||||||
if (value->ob_refcnt != 1)
|
|
||||||
continue;
|
|
||||||
if (PyUnicode_Check(key) && PyModule_Check(value)) {
|
|
||||||
if (is_essential_module(key))
|
|
||||||
continue;
|
|
||||||
if (Py_VerboseFlag)
|
|
||||||
PySys_FormatStderr(
|
|
||||||
"# cleanup[1] %U\n", key);
|
|
||||||
_PyModule_Clear(value);
|
|
||||||
PyDict_SetItem(modules, key, Py_None);
|
|
||||||
ndone++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while (ndone > 0);
|
|
||||||
|
|
||||||
/* Next, delete all modules (still skipping builtins and sys) */
|
|
||||||
pos = 0;
|
pos = 0;
|
||||||
while (PyDict_Next(modules, &pos, &key, &value)) {
|
while (PyDict_Next(modules, &pos, &key, &value)) {
|
||||||
if (PyUnicode_Check(key) && PyModule_Check(value)) {
|
if (PyModule_Check(value)) {
|
||||||
if (is_essential_module(key))
|
if (Py_VerboseFlag && PyUnicode_Check(key))
|
||||||
continue;
|
PySys_FormatStderr("# cleanup[2] removing %U\n", key, value);
|
||||||
if (Py_VerboseFlag)
|
STORE_MODULE_WEAKREF(value);
|
||||||
PySys_FormatStderr("# cleanup[2] %U\n", key);
|
|
||||||
_PyModule_Clear(value);
|
|
||||||
PyDict_SetItem(modules, key, Py_None);
|
PyDict_SetItem(modules, key, Py_None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Collect garbage remaining after deleting the modules. Mostly
|
/* Clear the modules dict. */
|
||||||
reference cycles created by classes. */
|
PyDict_Clear(modules);
|
||||||
PyGC_Collect();
|
/* Replace the interpreter's reference to builtins with an empty dict
|
||||||
|
(module globals still have a reference to the original builtins). */
|
||||||
|
builtins = interp->builtins;
|
||||||
|
interp->builtins = PyDict_New();
|
||||||
|
Py_DECREF(builtins);
|
||||||
|
/* Collect references */
|
||||||
|
_PyGC_CollectNoFail();
|
||||||
/* Dump GC stats before it's too late, since it uses the warnings
|
/* Dump GC stats before it's too late, since it uses the warnings
|
||||||
machinery. */
|
machinery. */
|
||||||
_PyGC_DumpShutdownStats();
|
_PyGC_DumpShutdownStats();
|
||||||
|
|
||||||
/* Next, delete all remaining modules */
|
/* Now, if there are any modules left alive, clear their globals to
|
||||||
pos = 0;
|
minimize potential leaks. All C extension modules actually end
|
||||||
while (PyDict_Next(modules, &pos, &key, &value)) {
|
up here, since they are kept alive in the interpreter state. */
|
||||||
if (PyUnicode_Check(key) && PyModule_Check(value)) {
|
if (weaklist != NULL) {
|
||||||
|
Py_ssize_t i, n;
|
||||||
|
n = PyList_GET_SIZE(weaklist);
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
PyObject *tup = PyList_GET_ITEM(weaklist, i);
|
||||||
|
PyObject *mod = PyWeakref_GET_OBJECT(PyTuple_GET_ITEM(tup, 1));
|
||||||
|
if (mod == Py_None)
|
||||||
|
continue;
|
||||||
|
Py_INCREF(mod);
|
||||||
|
assert(PyModule_Check(mod));
|
||||||
if (Py_VerboseFlag)
|
if (Py_VerboseFlag)
|
||||||
PySys_FormatStderr("# cleanup[3] %U\n", key);
|
PySys_FormatStderr("# cleanup[3] wiping %U\n",
|
||||||
_PyModule_Clear(value);
|
PyTuple_GET_ITEM(tup, 0), mod);
|
||||||
PyDict_SetItem(modules, key, Py_None);
|
_PyModule_Clear(mod);
|
||||||
|
Py_DECREF(mod);
|
||||||
}
|
}
|
||||||
|
Py_DECREF(weaklist);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Finally, clear and delete the modules directory */
|
/* Clear and delete the modules directory. Actual modules will
|
||||||
PyDict_Clear(modules);
|
still be there only if imported during the execution of some
|
||||||
_PyGC_CollectNoFail();
|
destructor. */
|
||||||
interp->modules = NULL;
|
interp->modules = NULL;
|
||||||
Py_DECREF(modules);
|
Py_DECREF(modules);
|
||||||
|
|
||||||
|
/* Once more */
|
||||||
|
_PyGC_CollectNoFail();
|
||||||
|
|
||||||
|
#undef STORE_MODULE_WEAKREF
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue