Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
assignee=Noneclosed_at=<Date2020-12-15.16:17:11.642>created_at=<Date2020-12-14.12:00:33.149>labels= ['library', '3.10']
title='Make atexit state per interpreter'updated_at=<Date2020-12-15.16:17:11.637>user='https://github.com/vstinner'
In Python 3.0, the atexit module was rewritten in C. A new private _Py_PyAtExit() function was added to set a new private global "pyexitfunc" variable: variable used by call_py_exitfuncs() called by Py_Finalize().
In Python 3.7, the global "pyexitfunc" variable was moved int _PyRuntimeState (commit 2ebc5ce), and then into PyInterpreterState (commit 776407f).
In Python 3.7, the atexit module was upgrade to the multiphase initialization API (PEP-489): PyInit_atexit() uses PyModuleDef_Init().
Since Python 2.7, the atexit module has a limitation: if a second instance is created, the new instance overrides the old one, and old registered callbacks are newer called.
One option is to disallow creating a second instance: see bpo-40600 and PR 23699 for that.
Another option is to move the atexit state (callbacks) into PyInterpreterState. Two atexit module instances would modify the same list of callbacks. In this issue, I propose to investigate this option.
Ok, the initial issue is now fixed. It's possible to have more than one atexit module instance, registered callbacks in one or the other module are all called as expected. I also wrote an unit test for that.
I also took the opportunity of this issue to modernize the C code base, fix a bug in test_atexit (no longer clear atexit callbacks, run tests in subprocesses), and enhance atexit._run_exitfuncs(): log all callback exceptions using sys.unraisablehook so it's possible to catch them using the hook.