Commit 599bd5e1 authored by Skip Montanaro's avatar Skip Montanaro

Fix bug 1052242. Also includes rewrite of test case using unittest and

avoiding use of popen.
parent ed306292
......@@ -39,6 +39,12 @@ completes), all functions registered are called in last in, first out
order. The assumption is that lower level modules will normally be
imported before higher level modules and thus must be cleaned up
later.
If an exception is raised during execution of the exit handlers, a traceback
is printed (unless SystemExit is raised) and the exception information is
saved. After all exit handlers have had a chance to run the last exception
to be raised is reraised.
\end{funcdesc}
......
......@@ -15,9 +15,22 @@ def _run_exitfuncs():
last in, first out.
"""
exc_info = None
while _exithandlers:
func, targs, kargs = _exithandlers.pop()
func(*targs, **kargs)
try:
func(*targs, **kargs)
except SystemExit:
exc_info = sys.exc_info()
except:
import sys, traceback
print >> sys.stderr, "Error in atexit._run_exitfuncs:"
traceback.print_exc()
exc_info = sys.exc_info()
if exc_info is not None:
raise exc_info[0], exc_info[1], exc_info[2]
def register(func, *targs, **kargs):
"""register a function to be executed upon normal program termination
......@@ -33,7 +46,6 @@ if hasattr(sys, "exitfunc"):
# Assume it's another registered exit function - append it to our list
register(sys.exitfunc)
sys.exitfunc = _run_exitfuncs
del sys
if __name__ == "__main__":
......
# Test the atexit module.
from test.test_support import TESTFN, vereq, is_jython
import atexit
from os import popen, unlink
import sys
executable = sys.executable
if is_jython:
executable = "jython"
input = """\
import unittest
import StringIO
import atexit
from test import test_support
def handler1():
print "handler1"
class TestCase(unittest.TestCase):
def test_args(self):
# be sure args are handled properly
s = StringIO.StringIO()
sys.stdout = sys.stderr = s
save_handlers = atexit._exithandlers
atexit._exithandlers = []
try:
atexit.register(self.h1)
atexit.register(self.h4)
atexit.register(self.h4, 4, kw="abc")
atexit._run_exitfuncs()
finally:
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
atexit._exithandlers = save_handlers
self.assertEqual(s.getvalue(), "h4 (4,) {'kw': 'abc'}\nh4 () {}\nh1\n")
def handler2(*args, **kargs):
print "handler2", args, kargs
def test_order(self):
# be sure handlers are executed in reverse order
s = StringIO.StringIO()
sys.stdout = sys.stderr = s
save_handlers = atexit._exithandlers
atexit._exithandlers = []
try:
atexit.register(self.h1)
atexit.register(self.h2)
atexit.register(self.h3)
atexit._run_exitfuncs()
finally:
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
atexit._exithandlers = save_handlers
self.assertEqual(s.getvalue(), "h3\nh2\nh1\n")
atexit.register(handler1)
atexit.register(handler2)
atexit.register(handler2, 7, kw="abc")
"""
def test_sys_override(self):
# be sure a preset sys.exitfunc is handled properly
s = StringIO.StringIO()
sys.stdout = sys.stderr = s
save_handlers = atexit._exithandlers
atexit._exithandlers = []
exfunc = sys.exitfunc
sys.exitfunc = self.h1
reload(atexit)
try:
atexit.register(self.h2)
atexit._run_exitfuncs()
finally:
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
atexit._exithandlers = save_handlers
sys.exitfunc = exfunc
self.assertEqual(s.getvalue(), "h2\nh1\n")
fname = TESTFN + ".py"
f = file(fname, "w")
f.write(input)
f.close()
def test_raise(self):
# be sure raises are handled properly
s = StringIO.StringIO()
sys.stdout = sys.stderr = s
save_handlers = atexit._exithandlers
atexit._exithandlers = []
try:
atexit.register(self.raise1)
atexit.register(self.raise2)
self.assertRaises(TypeError, atexit._run_exitfuncs)
finally:
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
atexit._exithandlers = save_handlers
### helpers
def h1(self):
print "h1"
p = popen('"%s" %s' % (executable, fname))
output = p.read()
p.close()
vereq(output, """\
handler2 (7,) {'kw': 'abc'}
handler2 () {}
handler1
""")
def h2(self):
print "h2"
input = """\
def direct():
print "direct exit"
def h3(self):
print "h3"
import sys
sys.exitfunc = direct
def h4(self, *args, **kwargs):
print "h4", args, kwargs
# Make sure atexit doesn't drop
def indirect():
print "indirect exit"
def raise1(self):
raise TypeError
import atexit
atexit.register(indirect)
"""
def raise2(self):
raise SystemError
f = file(fname, "w")
f.write(input)
f.close()
def test_main():
test_support.run_unittest(TestCase)
p = popen('"%s" %s' % (executable, fname))
output = p.read()
p.close()
vereq(output, """\
indirect exit
direct exit
""")
unlink(fname)
if __name__ == "__main__":
test_main()
......@@ -2,6 +2,17 @@
Python News
+++++++++++
What's New in Python 2.4 release candidate 1?
=============================================
Library
-------
- Bug 1052242: If exceptions are raised by an atexit handler function an
attempt is made to execute the remaining handlers. The last exception
raised is re-raised.
(editors: check NEWS.help for information about editing NEWS using ReST.)
What's New in Python 2.4 beta 2?
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment