Skip to content

Commit f1dc061

Browse files
committed
Add internal routine _PyModule_Clear(), which does approximately what
clear_carefully() used to do in import.c. Differences: leave only __builtins__ alone in the 2nd pass; and don't clear the dictionary (on the theory that as long as there are references left to the dictionary, those might be destructors that might expect __builtins__ to be alive when they run; and __builtins__ can't normally be part of a cycle).
1 parent c83db33 commit f1dc061

2 files changed

Lines changed: 51 additions & 1 deletion

File tree

Include/moduleobject.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ extern DL_IMPORT(PyTypeObject) PyModule_Type;
4444
extern PyObject *PyModule_New Py_PROTO((char *));
4545
extern PyObject *PyModule_GetDict Py_PROTO((PyObject *));
4646
extern char *PyModule_GetName Py_PROTO((PyObject *));
47+
extern void _PyModule_Clear Py_PROTO((PyObject *));
4748

4849
#ifdef __cplusplus
4950
}

Objects/moduleobject.c

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,14 +93,63 @@ PyModule_GetName(m)
9393
return PyString_AsString(nameobj);
9494
}
9595

96+
void
97+
_PyModule_Clear(m)
98+
PyObject *m;
99+
{
100+
/* To make the execution order of destructors for global
101+
objects a bit more predictable, we first zap all objects
102+
whose name starts with a single underscore, before we clear
103+
the entire dictionary. We zap them by replacing them with
104+
None, rather than deleting them from the dictionary, to
105+
avoid rehashing the dictionary (to some extent). */
106+
107+
int pos;
108+
PyObject *key, *value;
109+
PyObject *d;
110+
111+
d = ((PyModuleObject *)m)->md_dict;
112+
113+
/* First, clear only names starting with a single underscore */
114+
pos = 0;
115+
while (PyDict_Next(d, &pos, &key, &value)) {
116+
if (value != Py_None && PyString_Check(key)) {
117+
char *s = PyString_AsString(key);
118+
if (s[0] == '_' && s[1] != '_') {
119+
if (Py_VerboseFlag > 1)
120+
fprintf(stderr, "# clear[1] %s\n", s);
121+
PyDict_SetItem(d, key, Py_None);
122+
}
123+
}
124+
}
125+
126+
/* Next, clear all names except for __builtins__ */
127+
pos = 0;
128+
while (PyDict_Next(d, &pos, &key, &value)) {
129+
if (value != Py_None && PyString_Check(key)) {
130+
char *s = PyString_AsString(key);
131+
if (s[0] != '_' || strcmp(s, "__builtins__") != 0) {
132+
if (Py_VerboseFlag > 1)
133+
fprintf(stderr, "# clear[2] %s\n", s);
134+
PyDict_SetItem(d, key, Py_None);
135+
}
136+
}
137+
}
138+
139+
/* Note: we leave __builtins__ in place, so that destructors
140+
of non-global objects defined in this module can still use
141+
builtins, in particularly 'None'. */
142+
143+
}
144+
96145
/* Methods */
97146

98147
static void
99148
module_dealloc(m)
100149
PyModuleObject *m;
101150
{
102151
if (m->md_dict != NULL) {
103-
PyDict_Clear(m->md_dict);
152+
_PyModule_Clear((PyObject *)m);
104153
Py_DECREF(m->md_dict);
105154
}
106155
free((char *)m);

0 commit comments

Comments
 (0)