Utilities for interfacing with the standard library’s atexit module.#

class sage.cpython.atexit.restore_atexit[source]#

Bases: object

Context manager that restores the state of the atexit module to its previous state when exiting the context.

INPUT:

  • run (bool, default: False) – if True, when exiting the context (but before restoring the old exit functions), run all atexit functions which were added inside the context.

  • clear (bool, default: equal to run) – if True, clear already registered atexit handlers upon entering the context.

Warning

The combination run=True and clear=False will cause already-registered exit functions to be run twice: once when exiting the context and again when exiting Python.

EXAMPLES:

For this example we will wrap the entire example with restore_atexit(clear=True) so as to start with a fresh atexit module state for the sake of the example.

Note that the function atexit._run_exitfuncs() runs all registered handlers, and then clears the list of handlers, so we can use it to test manipulation of the atexit state:

sage: import atexit
sage: from sage.cpython.atexit import restore_atexit
sage: def handler(*args, **kwargs):
....:     import sys
....:     # see https://github.com/sagemath/sage/issues/25270#comment:56
....:     sys.stdout.write(str((args, kwargs)))
....:     sys.stdout.write('\n')
sage: atexit.register(handler, 1, 2, c=3)
<function handler at 0x...>
sage: atexit.register(handler, 4, 5, d=6)
<function handler at 0x...>
sage: with restore_atexit(clear=True):
....:     atexit._run_exitfuncs()  # Should be none registered
....:     atexit.register(handler, 1, 2, c=3)
....:     with restore_atexit():
....:         atexit._run_exitfuncs()  # Run just registered handler
....:     atexit._run_exitfuncs()  # Handler should be run again
<function handler at 0x...>
((1, 2), {'c': 3})
((1, 2), {'c': 3})
>>> from sage.all import *
>>> import atexit
>>> from sage.cpython.atexit import restore_atexit
>>> def handler(*args, **kwargs):
...     import sys
...     # see https://github.com/sagemath/sage/issues/25270#comment:56
...     sys.stdout.write(str((args, kwargs)))
...     sys.stdout.write('\n')
>>> atexit.register(handler, Integer(1), Integer(2), c=Integer(3))
<function handler at 0x...>
>>> atexit.register(handler, Integer(4), Integer(5), d=Integer(6))
<function handler at 0x...>
>>> with restore_atexit(clear=True):
...     atexit._run_exitfuncs()  # Should be none registered
...     atexit.register(handler, Integer(1), Integer(2), c=Integer(3))
...     with restore_atexit():
...         atexit._run_exitfuncs()  # Run just registered handler
...     atexit._run_exitfuncs()  # Handler should be run again
<function handler at 0x...>
((1, 2), {'c': 3})
((1, 2), {'c': 3})

We test the run option:

sage: with restore_atexit(run=True):
....:     # this handler is run when exiting the context
....:     _ = atexit.register(handler, 7, 8, e=9)
((7, 8), {'e': 9})
sage: with restore_atexit(clear=False, run=True):
....:     # original handlers are run when exiting the context
....:     pass
((4, 5), {'d': 6})
((1, 2), {'c': 3})
>>> from sage.all import *
>>> with restore_atexit(run=True):
...     # this handler is run when exiting the context
...     _ = atexit.register(handler, Integer(7), Integer(8), e=Integer(9))
((7, 8), {'e': 9})
>>> with restore_atexit(clear=False, run=True):
...     # original handlers are run when exiting the context
...     pass
((4, 5), {'d': 6})
((1, 2), {'c': 3})

The original handlers are still in place:

sage: atexit._run_exitfuncs()
((4, 5), {'d': 6})
((1, 2), {'c': 3})
>>> from sage.all import *
>>> atexit._run_exitfuncs()
((4, 5), {'d': 6})
((1, 2), {'c': 3})