mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +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 @@ | ||||||
| 
 | 
 | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
|  | import atexit | ||||||
| import builtins | import builtins | ||||||
| import __main__ | import __main__ | ||||||
| 
 | 
 | ||||||
|  | @ -158,3 +159,8 @@ def get_class_members(klass): | ||||||
|     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 @@ | ||||||
| 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_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 __call__(self, code=None): | ||||||
|             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 @@ def __call__(self): | ||||||
| 
 | 
 | ||||||
| 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 @@ def __call__(self, *args, **kwds): | ||||||
|         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 @@ def test_reinit(self): | ||||||
|                "__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 @@ def test_module_repr_source(self): | ||||||
|         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 @@ def get_gen(): yield 1 | ||||||
|         # 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
	
	 Antoine Pitrou
						Antoine Pitrou