run testatexit py in a subprocess since it calls atexit clear actually test the shutdown mechanism in a subprocess res scripthelper assertpythonokc code self assertequalres out decode splitlines two one self assertfalseres err def testatexitinstancesself bpo42639 it is safe to have more than one atexit instance code textwrap dedent import sys import atexit as atexit1 del sys modules atexit import atexit as atexit2 del sys modules atexit assert atexit2 is not atexit1 atexit1 registerprint atexit1 atexit2 registerprint atexit2 this test shows a leak in refleak mode if atexit doesn t take care to free callbacks in its persubinterpreter module state ret support runinsubinterpcode self assertequalret 0 self assertequalatexit ncallbacks n def testcallbacksleakrefcycleself similar to the above but with a refcycle through the atexit module n atexit ncallbacks code textwrap dedentr import atexit def f pass atexit registerf atexit atexit atexit this tests if a callback is called on subinterpreter teardown formatw ret support runinsubinterpcode os closew self assertequalos readr lenexpected expected os closer if name main unittest main run _test_atexit py in a subprocess since it calls atexit _clear actually test the shutdown mechanism in a subprocess import atexit def f msg print msg atexit register f one atexit register f two bpo 42639 it is safe to have more than one atexit instance import sys import atexit as atexit1 del sys modules atexit import atexit as atexit2 del sys modules atexit assert atexit2 is not atexit1 atexit1 register print atexit1 atexit2 register print atexit2 this test shows a leak in refleak mode if atexit doesn t take care to free callbacks in its per subinterpreter module state import atexit def f pass atexit register f del atexit similar to the above but with a refcycle through the atexit module import atexit def f pass atexit register f atexit __atexit atexit this tests if a callback is called on subinterpreter teardown import os import atexit def callback os write d b the test has passed atexit register callback
import atexit
import os
import textwrap
import unittest
from test import support
from test.support import script_helper


class GeneralTest(unittest.TestCase):
    def test_general(self):
        
        script = support.findfile("_test_atexit.py")
        script_helper.run_test_script(script)

class FunctionalTest(unittest.TestCase):
    def test_shutdown(self):
        
        code = textwrap.dedent()
        res = script_helper.assert_python_ok("-c", code)
        self.assertEqual(res.out.decode().splitlines(), ["two", "one"])
        self.assertFalse(res.err)

    def test_atexit_instances(self):
        
        code = textwrap.dedent()
        res = script_helper.assert_python_ok("-c", code)
        self.assertEqual(res.out.decode().splitlines(), ["atexit2", "atexit1"])
        self.assertFalse(res.err)


@support.cpython_only
class SubinterpreterTest(unittest.TestCase):

    def test_callbacks_leak(self):
        
        
        
        n = atexit._ncallbacks()
        code = textwrap.dedent(r)
        ret = support.run_in_subinterp(code)
        self.assertEqual(ret, 0)
        self.assertEqual(atexit._ncallbacks(), n)

    def test_callbacks_leak_refcycle(self):
        
        
        n = atexit._ncallbacks()
        code = textwrap.dedent(r)
        ret = support.run_in_subinterp(code)
        self.assertEqual(ret, 0)
        self.assertEqual(atexit._ncallbacks(), n)

    @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()")
    def test_callback_on_subinterpreter_teardown(self):
        
        
        expected = b"The test has passed!"
        r, w = os.pipe()

        code = textwrap.dedent(r.format(w))
        ret = support.run_in_subinterp(code)
        os.close(w)
        self.assertEqual(os.read(r, len(expected)), expected)
        os.close(r)


if __name__ == "__main__":
    unittest.main()