forked from python/cpython
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
bpo-42639: atexit._run_exitfuncs() uses sys.unraisablehook (pythonGH-…
…23779) atexit._run_exitfuncs() now logs callback exceptions using sys.unraisablehook, rather than logging them directly into sys.stderr and raising the last exception. Run GeneralTest of test_atexit in a subprocess since it calls atexit._clear() which clears all atexit callbacks. _PyAtExit_Fini() sets state->callbacks to NULL.
- Loading branch information
Showing
5 changed files
with
140 additions
and
184 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
""" | ||
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() | ||
|
||
self.assertEqual(cm.unraisable.object, func) | ||
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]) | ||
|
||
|
||
if __name__ == "__main__": | ||
unittest.main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
3 changes: 3 additions & 0 deletions
3
Misc/NEWS.d/next/Library/2020-12-15-15-14-29.bpo-42639.uJ3h8I.rst
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
:func:`atexit._run_exitfuncs` now logs callback exceptions using | ||
:data:`sys.unraisablehook`, rather than logging them directly into | ||
:data:`sys.stderr` and raise the last exception. |
Oops, something went wrong.