Fred Drake | c19425d | 2000-06-28 15:07:31 +0000 | [diff] [blame] | 1 | import atexit |
Marcel Plch | 776407f | 2017-12-20 11:17:58 +0100 | [diff] [blame] | 2 | import os |
Victor Stinner | 83d5204 | 2020-12-14 22:40:40 +0100 | [diff] [blame] | 3 | import sys |
| 4 | import textwrap |
| 5 | import unittest |
Benjamin Peterson | ee8712c | 2008-05-20 21:35:26 +0000 | [diff] [blame] | 6 | from test import support |
Antoine Pitrou | fc5db95 | 2017-12-13 02:29:07 +0100 | [diff] [blame] | 7 | from test.support import script_helper |
Fred Drake | c19425d | 2000-06-28 15:07:31 +0000 | [diff] [blame] | 8 | |
Antoine Pitrou | 2d350fd | 2013-08-01 20:56:12 +0200 | [diff] [blame] | 9 | |
| 10 | class GeneralTest(unittest.TestCase): |
Victor Stinner | 3ca2b8f | 2020-12-15 17:12:02 +0100 | [diff] [blame] | 11 | def test_general(self): |
| 12 | # Run _test_atexit.py in a subprocess since it calls atexit._clear() |
| 13 | script = support.findfile("_test_atexit.py") |
| 14 | script_helper.run_test_script(script) |
Antoine Pitrou | 2d350fd | 2013-08-01 20:56:12 +0200 | [diff] [blame] | 15 | |
Victor Stinner | 3ca2b8f | 2020-12-15 17:12:02 +0100 | [diff] [blame] | 16 | class FunctionalTest(unittest.TestCase): |
Antoine Pitrou | fc5db95 | 2017-12-13 02:29:07 +0100 | [diff] [blame] | 17 | def test_shutdown(self): |
| 18 | # Actually test the shutdown mechanism in a subprocess |
Victor Stinner | 83d5204 | 2020-12-14 22:40:40 +0100 | [diff] [blame] | 19 | code = textwrap.dedent(""" |
Antoine Pitrou | fc5db95 | 2017-12-13 02:29:07 +0100 | [diff] [blame] | 20 | import atexit |
| 21 | |
| 22 | def f(msg): |
| 23 | print(msg) |
| 24 | |
| 25 | atexit.register(f, "one") |
| 26 | atexit.register(f, "two") |
Victor Stinner | 83d5204 | 2020-12-14 22:40:40 +0100 | [diff] [blame] | 27 | """) |
Antoine Pitrou | fc5db95 | 2017-12-13 02:29:07 +0100 | [diff] [blame] | 28 | res = script_helper.assert_python_ok("-c", code) |
| 29 | self.assertEqual(res.out.decode().splitlines(), ["two", "one"]) |
| 30 | self.assertFalse(res.err) |
| 31 | |
Victor Stinner | b8fa135 | 2020-12-15 14:34:19 +0100 | [diff] [blame] | 32 | def test_atexit_instances(self): |
| 33 | # bpo-42639: It is safe to have more than one atexit instance. |
| 34 | code = textwrap.dedent(""" |
| 35 | import sys |
| 36 | import atexit as atexit1 |
| 37 | del sys.modules['atexit'] |
| 38 | import atexit as atexit2 |
| 39 | del sys.modules['atexit'] |
| 40 | |
| 41 | assert atexit2 is not atexit1 |
| 42 | |
| 43 | atexit1.register(print, "atexit1") |
| 44 | atexit2.register(print, "atexit2") |
| 45 | """) |
| 46 | res = script_helper.assert_python_ok("-c", code) |
| 47 | self.assertEqual(res.out.decode().splitlines(), ["atexit2", "atexit1"]) |
| 48 | self.assertFalse(res.err) |
| 49 | |
Tim Peters | 012b69c | 2002-07-16 19:30:59 +0000 | [diff] [blame] | 50 | |
Serhiy Storchaka | 24c738a | 2017-03-19 20:20:10 +0200 | [diff] [blame] | 51 | @support.cpython_only |
Antoine Pitrou | 2d350fd | 2013-08-01 20:56:12 +0200 | [diff] [blame] | 52 | class SubinterpreterTest(unittest.TestCase): |
| 53 | |
| 54 | def test_callbacks_leak(self): |
| 55 | # This test shows a leak in refleak mode if atexit doesn't |
| 56 | # take care to free callbacks in its per-subinterpreter module |
| 57 | # state. |
| 58 | n = atexit._ncallbacks() |
Victor Stinner | 83d5204 | 2020-12-14 22:40:40 +0100 | [diff] [blame] | 59 | code = textwrap.dedent(r""" |
Antoine Pitrou | 2d350fd | 2013-08-01 20:56:12 +0200 | [diff] [blame] | 60 | import atexit |
| 61 | def f(): |
| 62 | pass |
| 63 | atexit.register(f) |
| 64 | del atexit |
Victor Stinner | 83d5204 | 2020-12-14 22:40:40 +0100 | [diff] [blame] | 65 | """) |
Victor Stinner | ed3b0bc | 2013-11-23 12:27:24 +0100 | [diff] [blame] | 66 | ret = support.run_in_subinterp(code) |
Antoine Pitrou | 2d350fd | 2013-08-01 20:56:12 +0200 | [diff] [blame] | 67 | self.assertEqual(ret, 0) |
| 68 | self.assertEqual(atexit._ncallbacks(), n) |
| 69 | |
| 70 | def test_callbacks_leak_refcycle(self): |
| 71 | # Similar to the above, but with a refcycle through the atexit |
| 72 | # module. |
| 73 | n = atexit._ncallbacks() |
Victor Stinner | 83d5204 | 2020-12-14 22:40:40 +0100 | [diff] [blame] | 74 | code = textwrap.dedent(r""" |
Antoine Pitrou | 2d350fd | 2013-08-01 20:56:12 +0200 | [diff] [blame] | 75 | import atexit |
| 76 | def f(): |
| 77 | pass |
| 78 | atexit.register(f) |
| 79 | atexit.__atexit = atexit |
Victor Stinner | 83d5204 | 2020-12-14 22:40:40 +0100 | [diff] [blame] | 80 | """) |
Victor Stinner | ed3b0bc | 2013-11-23 12:27:24 +0100 | [diff] [blame] | 81 | ret = support.run_in_subinterp(code) |
Antoine Pitrou | 2d350fd | 2013-08-01 20:56:12 +0200 | [diff] [blame] | 82 | self.assertEqual(ret, 0) |
| 83 | self.assertEqual(atexit._ncallbacks(), n) |
| 84 | |
Marcel Plch | 776407f | 2017-12-20 11:17:58 +0100 | [diff] [blame] | 85 | def test_callback_on_subinterpreter_teardown(self): |
| 86 | # This tests if a callback is called on |
| 87 | # subinterpreter teardown. |
| 88 | expected = b"The test has passed!" |
| 89 | r, w = os.pipe() |
| 90 | |
Victor Stinner | 83d5204 | 2020-12-14 22:40:40 +0100 | [diff] [blame] | 91 | code = textwrap.dedent(r""" |
Marcel Plch | 776407f | 2017-12-20 11:17:58 +0100 | [diff] [blame] | 92 | import os |
| 93 | import atexit |
| 94 | def callback(): |
| 95 | os.write({:d}, b"The test has passed!") |
| 96 | atexit.register(callback) |
Victor Stinner | 83d5204 | 2020-12-14 22:40:40 +0100 | [diff] [blame] | 97 | """.format(w)) |
Marcel Plch | 776407f | 2017-12-20 11:17:58 +0100 | [diff] [blame] | 98 | ret = support.run_in_subinterp(code) |
| 99 | os.close(w) |
| 100 | self.assertEqual(os.read(r, len(expected)), expected) |
| 101 | os.close(r) |
| 102 | |
Antoine Pitrou | 2d350fd | 2013-08-01 20:56:12 +0200 | [diff] [blame] | 103 | |
Skip Montanaro | 599bd5e | 2004-11-04 04:31:30 +0000 | [diff] [blame] | 104 | if __name__ == "__main__": |
Zachary Ware | 38c707e | 2015-04-13 15:00:43 -0500 | [diff] [blame] | 105 | unittest.main() |