Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 599bd5e

Browse files
author
Skip Montanaro
committed
Fix bug 1052242. Also includes rewrite of test case using unittest and
avoiding use of popen.
1 parent ed30629 commit 599bd5e

4 files changed

Lines changed: 117 additions & 54 deletions

File tree

Doc/lib/libatexit.tex

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ \section{\module{atexit} ---
3939
order. The assumption is that lower level modules will normally be
4040
imported before higher level modules and thus must be cleaned up
4141
later.
42+
43+
If an exception is raised during execution of the exit handlers, a traceback
44+
is printed (unless SystemExit is raised) and the exception information is
45+
saved. After all exit handlers have had a chance to run the last exception
46+
to be raised is reraised.
47+
4248
\end{funcdesc}
4349

4450

Lib/atexit.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,22 @@ def _run_exitfuncs():
1515
last in, first out.
1616
"""
1717

18+
exc_info = None
1819
while _exithandlers:
1920
func, targs, kargs = _exithandlers.pop()
20-
func(*targs, **kargs)
21+
try:
22+
func(*targs, **kargs)
23+
except SystemExit:
24+
exc_info = sys.exc_info()
25+
except:
26+
import sys, traceback
27+
print >> sys.stderr, "Error in atexit._run_exitfuncs:"
28+
traceback.print_exc()
29+
exc_info = sys.exc_info()
30+
31+
if exc_info is not None:
32+
raise exc_info[0], exc_info[1], exc_info[2]
33+
2134

2235
def register(func, *targs, **kargs):
2336
"""register a function to be executed upon normal program termination
@@ -33,7 +46,6 @@ def register(func, *targs, **kargs):
3346
# Assume it's another registered exit function - append it to our list
3447
register(sys.exitfunc)
3548
sys.exitfunc = _run_exitfuncs
36-
3749
del sys
3850

3951
if __name__ == "__main__":

Lib/test/test_atexit.py

Lines changed: 86 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,100 @@
1-
# Test the atexit module.
2-
from test.test_support import TESTFN, vereq, is_jython
3-
import atexit
4-
from os import popen, unlink
51
import sys
6-
7-
executable = sys.executable
8-
if is_jython:
9-
executable = "jython"
10-
11-
input = """\
2+
import unittest
3+
import StringIO
124
import atexit
5+
from test import test_support
136

14-
def handler1():
15-
print "handler1"
7+
class TestCase(unittest.TestCase):
8+
def test_args(self):
9+
# be sure args are handled properly
10+
s = StringIO.StringIO()
11+
sys.stdout = sys.stderr = s
12+
save_handlers = atexit._exithandlers
13+
atexit._exithandlers = []
14+
try:
15+
atexit.register(self.h1)
16+
atexit.register(self.h4)
17+
atexit.register(self.h4, 4, kw="abc")
18+
atexit._run_exitfuncs()
19+
finally:
20+
sys.stdout = sys.__stdout__
21+
sys.stderr = sys.__stderr__
22+
atexit._exithandlers = save_handlers
23+
self.assertEqual(s.getvalue(), "h4 (4,) {'kw': 'abc'}\nh4 () {}\nh1\n")
1624

17-
def handler2(*args, **kargs):
18-
print "handler2", args, kargs
25+
def test_order(self):
26+
# be sure handlers are executed in reverse order
27+
s = StringIO.StringIO()
28+
sys.stdout = sys.stderr = s
29+
save_handlers = atexit._exithandlers
30+
atexit._exithandlers = []
31+
try:
32+
atexit.register(self.h1)
33+
atexit.register(self.h2)
34+
atexit.register(self.h3)
35+
atexit._run_exitfuncs()
36+
finally:
37+
sys.stdout = sys.__stdout__
38+
sys.stderr = sys.__stderr__
39+
atexit._exithandlers = save_handlers
40+
self.assertEqual(s.getvalue(), "h3\nh2\nh1\n")
1941

20-
atexit.register(handler1)
21-
atexit.register(handler2)
22-
atexit.register(handler2, 7, kw="abc")
23-
"""
42+
def test_sys_override(self):
43+
# be sure a preset sys.exitfunc is handled properly
44+
s = StringIO.StringIO()
45+
sys.stdout = sys.stderr = s
46+
save_handlers = atexit._exithandlers
47+
atexit._exithandlers = []
48+
exfunc = sys.exitfunc
49+
sys.exitfunc = self.h1
50+
reload(atexit)
51+
try:
52+
atexit.register(self.h2)
53+
atexit._run_exitfuncs()
54+
finally:
55+
sys.stdout = sys.__stdout__
56+
sys.stderr = sys.__stderr__
57+
atexit._exithandlers = save_handlers
58+
sys.exitfunc = exfunc
59+
self.assertEqual(s.getvalue(), "h2\nh1\n")
2460

25-
fname = TESTFN + ".py"
26-
f = file(fname, "w")
27-
f.write(input)
28-
f.close()
61+
def test_raise(self):
62+
# be sure raises are handled properly
63+
s = StringIO.StringIO()
64+
sys.stdout = sys.stderr = s
65+
save_handlers = atexit._exithandlers
66+
atexit._exithandlers = []
67+
try:
68+
atexit.register(self.raise1)
69+
atexit.register(self.raise2)
70+
self.assertRaises(TypeError, atexit._run_exitfuncs)
71+
finally:
72+
sys.stdout = sys.__stdout__
73+
sys.stderr = sys.__stderr__
74+
atexit._exithandlers = save_handlers
75+
76+
### helpers
77+
def h1(self):
78+
print "h1"
2979

30-
p = popen('"%s" %s' % (executable, fname))
31-
output = p.read()
32-
p.close()
33-
vereq(output, """\
34-
handler2 (7,) {'kw': 'abc'}
35-
handler2 () {}
36-
handler1
37-
""")
80+
def h2(self):
81+
print "h2"
3882

39-
input = """\
40-
def direct():
41-
print "direct exit"
83+
def h3(self):
84+
print "h3"
4285

43-
import sys
44-
sys.exitfunc = direct
86+
def h4(self, *args, **kwargs):
87+
print "h4", args, kwargs
4588

46-
# Make sure atexit doesn't drop
47-
def indirect():
48-
print "indirect exit"
89+
def raise1(self):
90+
raise TypeError
4991

50-
import atexit
51-
atexit.register(indirect)
52-
"""
92+
def raise2(self):
93+
raise SystemError
5394

54-
f = file(fname, "w")
55-
f.write(input)
56-
f.close()
95+
def test_main():
96+
test_support.run_unittest(TestCase)
5797

58-
p = popen('"%s" %s' % (executable, fname))
59-
output = p.read()
60-
p.close()
61-
vereq(output, """\
62-
indirect exit
63-
direct exit
64-
""")
6598

66-
unlink(fname)
99+
if __name__ == "__main__":
100+
test_main()

Misc/NEWS

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@
22
Python News
33
+++++++++++
44

5+
What's New in Python 2.4 release candidate 1?
6+
=============================================
7+
8+
Library
9+
-------
10+
11+
- Bug 1052242: If exceptions are raised by an atexit handler function an
12+
attempt is made to execute the remaining handlers. The last exception
13+
raised is re-raised.
14+
15+
516
(editors: check NEWS.help for information about editing NEWS using ReST.)
617

718
What's New in Python 2.4 beta 2?

0 commit comments

Comments
 (0)