| 
									
										
										
										
											2020-12-15 17:12:02 +01:00
										 |  |  | """
 | 
					
						
							|  |  |  | Tests run by test_atexit in a subprocess since it clears atexit callbacks. | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | import atexit | 
					
						
							|  |  |  | import sys | 
					
						
							|  |  |  | import unittest | 
					
						
							|  |  |  | from test import support | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class GeneralTest(unittest.TestCase): | 
					
						
							|  |  |  |     def setUp(self): | 
					
						
							|  |  |  |         atexit._clear() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def tearDown(self): | 
					
						
							|  |  |  |         atexit._clear() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def assert_raises_unraisable(self, exc_type, func, *args): | 
					
						
							|  |  |  |         with support.catch_unraisable_exception() as cm: | 
					
						
							|  |  |  |             atexit.register(func, *args) | 
					
						
							|  |  |  |             atexit._run_exitfuncs() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-03 09:45:53 +02:00
										 |  |  |             self.assertIsNone(cm.unraisable.object) | 
					
						
							|  |  |  |             self.assertEqual(cm.unraisable.err_msg, | 
					
						
							|  |  |  |                     f'Exception ignored in atexit callback {func!r}') | 
					
						
							| 
									
										
										
										
											2020-12-15 17:12:02 +01:00
										 |  |  |             self.assertEqual(cm.unraisable.exc_type, exc_type) | 
					
						
							|  |  |  |             self.assertEqual(type(cm.unraisable.exc_value), exc_type) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_order(self): | 
					
						
							|  |  |  |         # Check that callbacks are called in reverse order with the expected | 
					
						
							|  |  |  |         # positional and keyword arguments. | 
					
						
							|  |  |  |         calls = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def func1(*args, **kwargs): | 
					
						
							|  |  |  |             calls.append(('func1', args, kwargs)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def func2(*args, **kwargs): | 
					
						
							|  |  |  |             calls.append(('func2', args, kwargs)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # be sure args are handled properly | 
					
						
							|  |  |  |         atexit.register(func1, 1, 2) | 
					
						
							|  |  |  |         atexit.register(func2) | 
					
						
							|  |  |  |         atexit.register(func2, 3, key="value") | 
					
						
							|  |  |  |         atexit._run_exitfuncs() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.assertEqual(calls, | 
					
						
							|  |  |  |                          [('func2', (3,), {'key': 'value'}), | 
					
						
							|  |  |  |                           ('func2', (), {}), | 
					
						
							|  |  |  |                           ('func1', (1, 2), {})]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_badargs(self): | 
					
						
							|  |  |  |         def func(): | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # func() has no parameter, but it's called with 2 parameters | 
					
						
							|  |  |  |         self.assert_raises_unraisable(TypeError, func, 1 ,2) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_raise(self): | 
					
						
							|  |  |  |         def raise_type_error(): | 
					
						
							|  |  |  |             raise TypeError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.assert_raises_unraisable(TypeError, raise_type_error) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_raise_unnormalized(self): | 
					
						
							|  |  |  |         # bpo-10756: Make sure that an unnormalized exception is handled | 
					
						
							|  |  |  |         # properly. | 
					
						
							|  |  |  |         def div_zero(): | 
					
						
							|  |  |  |             1 / 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.assert_raises_unraisable(ZeroDivisionError, div_zero) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_exit(self): | 
					
						
							|  |  |  |         self.assert_raises_unraisable(SystemExit, sys.exit) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_stress(self): | 
					
						
							|  |  |  |         a = [0] | 
					
						
							|  |  |  |         def inc(): | 
					
						
							|  |  |  |             a[0] += 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for i in range(128): | 
					
						
							|  |  |  |             atexit.register(inc) | 
					
						
							|  |  |  |         atexit._run_exitfuncs() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.assertEqual(a[0], 128) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_clear(self): | 
					
						
							|  |  |  |         a = [0] | 
					
						
							|  |  |  |         def inc(): | 
					
						
							|  |  |  |             a[0] += 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         atexit.register(inc) | 
					
						
							|  |  |  |         atexit._clear() | 
					
						
							|  |  |  |         atexit._run_exitfuncs() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.assertEqual(a[0], 0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_unregister(self): | 
					
						
							|  |  |  |         a = [0] | 
					
						
							|  |  |  |         def inc(): | 
					
						
							|  |  |  |             a[0] += 1 | 
					
						
							|  |  |  |         def dec(): | 
					
						
							|  |  |  |             a[0] -= 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for i in range(4): | 
					
						
							|  |  |  |             atexit.register(inc) | 
					
						
							|  |  |  |         atexit.register(dec) | 
					
						
							|  |  |  |         atexit.unregister(inc) | 
					
						
							|  |  |  |         atexit._run_exitfuncs() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.assertEqual(a[0], -1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_bound_methods(self): | 
					
						
							|  |  |  |         l = [] | 
					
						
							|  |  |  |         atexit.register(l.append, 5) | 
					
						
							|  |  |  |         atexit._run_exitfuncs() | 
					
						
							|  |  |  |         self.assertEqual(l, [5]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         atexit.unregister(l.append) | 
					
						
							|  |  |  |         atexit._run_exitfuncs() | 
					
						
							|  |  |  |         self.assertEqual(l, [5]) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-09 13:53:44 +00:00
										 |  |  |     def test_atexit_with_unregistered_function(self): | 
					
						
							|  |  |  |         # See bpo-46025 for more info | 
					
						
							|  |  |  |         def func(): | 
					
						
							|  |  |  |             atexit.unregister(func) | 
					
						
							|  |  |  |             1/0 | 
					
						
							|  |  |  |         atexit.register(func) | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             with support.catch_unraisable_exception() as cm: | 
					
						
							|  |  |  |                 atexit._run_exitfuncs() | 
					
						
							| 
									
										
										
										
											2023-11-03 09:45:53 +02:00
										 |  |  |                 self.assertIsNone(cm.unraisable.object) | 
					
						
							|  |  |  |                 self.assertEqual(cm.unraisable.err_msg, | 
					
						
							|  |  |  |                         f'Exception ignored in atexit callback {func!r}') | 
					
						
							| 
									
										
										
										
											2021-12-09 13:53:44 +00:00
										 |  |  |                 self.assertEqual(cm.unraisable.exc_type, ZeroDivisionError) | 
					
						
							|  |  |  |                 self.assertEqual(type(cm.unraisable.exc_value), ZeroDivisionError) | 
					
						
							|  |  |  |         finally: | 
					
						
							|  |  |  |             atexit.unregister(func) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-15 17:12:02 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | if __name__ == "__main__": | 
					
						
							|  |  |  |     unittest.main() |