[Duncan Booth]
No, Python doesn't run the garbage collector when it is exiting.
Actually, it does. What it doesn't do is call the garbage collector
twice when it exits, although it used to ;-)
What it does is to delete all the globals from each module in turn. So:
Yup. The code is in function Py_Finalize(). Here's the relevant snippet:
...
PyGC_Collect();
/* Destroy all modules */
PyImport_Cleanup();
/* Collect final garbage. This disposes of cycles created by
* new-style class definitions, for example.
* XXX This is disabled because it caused too many problems. If
* XXX a __del__ or weakref callback triggers here, Python code has
* XXX a hard time running, because even the sys module has been
* XXX cleared out (sys.stdout is gone, sys.excepthook is gone, etc).
* XXX One symptom is a sequence of information-free messages
* XXX coming from threads (if a __del__ or callback is invoked,
* XXX other threads can execute too, and any exception they encounter
* XXX triggers a comedy of errors as subsystem after subsystem
* XXX fails to find what it *expects* to find in sys to help report
* XXX the exception and consequent unexpected failures). I've also
* XXX seen segfaults then, after adding print statements to the
* XXX Python code getting called.
*/
#if 0
PyGC_Collect();
#endif
The first PyGC_Collect() runs, then what you described runs ("destroy
all modules"), and then PyGC_Collect() _doesn't_ run again. As the
comment says, it's the second run of PyGC_Collect() that _would_ get
rid of dead module-level new-style classes, were it to run.
Alas, as the XXX comments say, too much of the interpreter has been
destroyed by PyImport_Cleanup() for __del__ and weakref callbacks to
execute sanely, so we have to skip it. And, of course, module-level
objects _aren't_ trash before PyImport_Cleanup() runs. Therefore
module-level objects involved in reference cycles never trigger
__del__ or weakref callbacks as a side effect of Python exiting, and
new-style classes are (as you said) always involved in reference
cycles.