test_atexit.py 5.81 KB
Newer Older
1
import sys
2
import unittest
3
import io
4
import atexit
5
import os
6
from test import support
7
from test.support import script_helper
8

9 10 11
### helpers
def h1():
    print("h1")
12

13 14
def h2():
    print("h2")
15

16 17
def h3():
    print("h3")
18

19 20 21 22 23
def h4(*args, **kwargs):
    print("h4", args, kwargs)

def raise1():
    raise TypeError
Tim Peters's avatar
Tim Peters committed
24

25 26
def raise2():
    raise SystemError
27

28 29 30
def exit():
    raise SystemExit

31 32 33

class GeneralTest(unittest.TestCase):

34
    def setUp(self):
35 36
        self.save_stdout = sys.stdout
        self.save_stderr = sys.stderr
37
        self.stream = io.StringIO()
38 39
        sys.stdout = sys.stderr = self.stream
        atexit._clear()
40

41
    def tearDown(self):
Benjamin Peterson's avatar
Benjamin Peterson committed
42 43
        sys.stdout = self.save_stdout
        sys.stderr = self.save_stderr
44
        atexit._clear()
45

46 47 48 49 50 51
    def test_args(self):
        # be sure args are handled properly
        atexit.register(h1)
        atexit.register(h4)
        atexit.register(h4, 4, kw="abc")
        atexit._run_exitfuncs()
52

53 54
        self.assertEqual(self.stream.getvalue(),
                            "h4 (4,) {'kw': 'abc'}\nh4 () {}\nh1\n")
55

56 57 58 59
    def test_badargs(self):
        atexit.register(lambda: 1, 0, 0, (x for x in (1,2)), 0, 0)
        self.assertRaises(TypeError, atexit._run_exitfuncs)

60 61 62 63 64 65
    def test_order(self):
        # be sure handlers are executed in reverse order
        atexit.register(h1)
        atexit.register(h2)
        atexit.register(h3)
        atexit._run_exitfuncs()
66

67
        self.assertEqual(self.stream.getvalue(), "h3\nh2\nh1\n")
68

69 70 71 72
    def test_raise(self):
        # be sure raises are handled properly
        atexit.register(raise1)
        atexit.register(raise2)
73

74
        self.assertRaises(TypeError, atexit._run_exitfuncs)
75

76 77 78 79 80 81 82 83
    def test_raise_unnormalized(self):
        # Issue #10756: Make sure that an unnormalized exception is
        # handled properly
        atexit.register(lambda: 1 / 0)

        self.assertRaises(ZeroDivisionError, atexit._run_exitfuncs)
        self.assertIn("ZeroDivisionError", self.stream.getvalue())

84 85 86 87 88 89 90
    def test_exit(self):
        # be sure a SystemExit is handled properly
        atexit.register(exit)

        self.assertRaises(SystemExit, atexit._run_exitfuncs)
        self.assertEqual(self.stream.getvalue(), '')

91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
    def test_print_tracebacks(self):
        # Issue #18776: the tracebacks should be printed when errors occur.
        def f():
            1/0  # one
        def g():
            1/0  # two
        def h():
            1/0  # three
        atexit.register(f)
        atexit.register(g)
        atexit.register(h)

        self.assertRaises(ZeroDivisionError, atexit._run_exitfuncs)
        stderr = self.stream.getvalue()
        self.assertEqual(stderr.count("ZeroDivisionError"), 3)
        self.assertIn("# one", stderr)
        self.assertIn("# two", stderr)
        self.assertIn("# three", stderr)

110 111 112 113
    def test_stress(self):
        a = [0]
        def inc():
            a[0] += 1
114

115 116 117
        for i in range(128):
            atexit.register(inc)
        atexit._run_exitfuncs()
118

119
        self.assertEqual(a[0], 128)
120

121 122 123 124
    def test_clear(self):
        a = [0]
        def inc():
            a[0] += 1
125

126 127 128
        atexit.register(inc)
        atexit._clear()
        atexit._run_exitfuncs()
129

130
        self.assertEqual(a[0], 0)
131

132 133 134 135 136 137
    def test_unregister(self):
        a = [0]
        def inc():
            a[0] += 1
        def dec():
            a[0] -= 1
138 139

        for i in range(4):
140 141 142 143
            atexit.register(inc)
        atexit.register(dec)
        atexit.unregister(inc)
        atexit._run_exitfuncs()
144

145
        self.assertEqual(a[0], -1)
146

147 148 149 150 151
    def test_bound_methods(self):
        l = []
        atexit.register(l.append, 5)
        atexit._run_exitfuncs()
        self.assertEqual(l, [5])
152

153 154 155
        atexit.unregister(l.append)
        atexit._run_exitfuncs()
        self.assertEqual(l, [5])
156

157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
    def test_shutdown(self):
        # Actually test the shutdown mechanism in a subprocess
        code = """if 1:
            import atexit

            def f(msg):
                print(msg)

            atexit.register(f, "one")
            atexit.register(f, "two")
            """
        res = script_helper.assert_python_ok("-c", code)
        self.assertEqual(res.out.decode().splitlines(), ["two", "one"])
        self.assertFalse(res.err)

172

173
@support.cpython_only
174 175 176 177 178 179 180 181 182 183 184 185 186 187
class SubinterpreterTest(unittest.TestCase):

    def test_callbacks_leak(self):
        # This test shows a leak in refleak mode if atexit doesn't
        # take care to free callbacks in its per-subinterpreter module
        # state.
        n = atexit._ncallbacks()
        code = r"""if 1:
            import atexit
            def f():
                pass
            atexit.register(f)
            del atexit
            """
188
        ret = support.run_in_subinterp(code)
189 190 191 192 193 194 195 196 197 198 199 200 201 202
        self.assertEqual(ret, 0)
        self.assertEqual(atexit._ncallbacks(), n)

    def test_callbacks_leak_refcycle(self):
        # Similar to the above, but with a refcycle through the atexit
        # module.
        n = atexit._ncallbacks()
        code = r"""if 1:
            import atexit
            def f():
                pass
            atexit.register(f)
            atexit.__atexit = atexit
            """
203
        ret = support.run_in_subinterp(code)
204 205 206
        self.assertEqual(ret, 0)
        self.assertEqual(atexit._ncallbacks(), n)

207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
    def test_callback_on_subinterpreter_teardown(self):
        # This tests if a callback is called on
        # subinterpreter teardown.
        expected = b"The test has passed!"
        r, w = os.pipe()

        code = r"""if 1:
            import os
            import atexit
            def callback():
                os.write({:d}, b"The test has passed!")
            atexit.register(callback)
        """.format(w)
        ret = support.run_in_subinterp(code)
        os.close(w)
        self.assertEqual(os.read(r, len(expected)), expected)
        os.close(r)

225

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