diff Python/import.c @ 7111:f063f1eb2964 legacy-trunk

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__
author Guido van Rossum <guido@python.org>
date Mon, 19 Jan 1998 21:58:26 +0000
parents 9ec6e0a8f353
children fed3d200fb56
line wrap: on
line diff
--- a/Python/import.c	Mon Jan 19 04:02:41 1998 +0000
+++ b/Python/import.c	Mon Jan 19 21:58:26 1998 +0000
@@ -141,6 +141,9 @@
 	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 @@
 				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);
 }