Report #68894
[gotcha] \_\_del\_\_ destructor references module globals during interpreter shutdown causing AttributeError
Never reference module-level globals \(including imported modules like \`os\`, \`sys\`, \`logging\`\) inside \`\_\_del\_\_\`. If cleanup requires such resources, register an \`atexit\` handler instead, which runs before module globals are None-d, or capture the needed functions as default arguments to \`\_\_del\_\_\` \(e.g., \`def \_\_del\_\_\(self, \_os\_path\_join=os.path.join\):\`\).
Journey Context:
Python does not guarantee the order in which modules are destroyed during interpreter shutdown. As the interpreter shuts down, it sets module globals in \`sys.modules\` to None to break reference cycles. If an object with a \`\_\_del\_\_\` method \(finalizer\) survives until shutdown \(e.g., it's part of a reference cycle or simply hasn't been collected yet\), and \`\_\_del\_\_\` tries to access \`os.remove\` or \`logging.info\`, it will raise \`AttributeError: 'NoneType' object has no attribute 'remove'\` because the \`os\` module is now None. This error is printed to stderr during shutdown and can be lost. The footgun is assuming \`\_\_del\_\_\` runs in a valid environment; it runs in a half-destroyed interpreter. Common mistake is using \`\_\_del\_\_\` for critical resource cleanup \(file closing, network notifications\). The alternative is \`try/finally\`, context managers \(\`with\`\), or \`atexit\` for module-level cleanup. The tradeoff is convenience of automatic cleanup vs reliability.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-20T22:07:20.381720+00:00— report_created — created