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

avoiding use of popen.
This commit is contained in:
Skip Montanaro 2004-11-04 04:31:30 +00:00
parent ed306292d6
commit 599bd5e1e1
4 changed files with 117 additions and 54 deletions

View File

@ -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 order. The assumption is that lower level modules will normally be
imported before higher level modules and thus must be cleaned up imported before higher level modules and thus must be cleaned up
later. 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} \end{funcdesc}

View File

@ -15,9 +15,22 @@ def _run_exitfuncs():
last in, first out. last in, first out.
""" """
exc_info = None
while _exithandlers: while _exithandlers:
func, targs, kargs = _exithandlers.pop() 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): def register(func, *targs, **kargs):
"""register a function to be executed upon normal program termination """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 # Assume it's another registered exit function - append it to our list
register(sys.exitfunc) register(sys.exitfunc)
sys.exitfunc = _run_exitfuncs sys.exitfunc = _run_exitfuncs
del sys del sys
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -1,66 +1,100 @@
# Test the atexit module.
from test.test_support import TESTFN, vereq, is_jython
import atexit
from os import popen, unlink
import sys import sys
import unittest
executable = sys.executable import StringIO
if is_jython:
executable = "jython"
input = """\
import atexit import atexit
from test import test_support
def handler1(): class TestCase(unittest.TestCase):
print "handler1" 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): def test_order(self):
print "handler2", args, kargs # 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) def test_sys_override(self):
atexit.register(handler2) # be sure a preset sys.exitfunc is handled properly
atexit.register(handler2, 7, kw="abc") 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" def test_raise(self):
f = file(fname, "w") # be sure raises are handled properly
f.write(input) s = StringIO.StringIO()
f.close() 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)) def h2(self):
output = p.read() print "h2"
p.close()
vereq(output, """\
handler2 (7,) {'kw': 'abc'}
handler2 () {}
handler1
""")
input = """\ def h3(self):
def direct(): print "h3"
print "direct exit"
import sys def h4(self, *args, **kwargs):
sys.exitfunc = direct print "h4", args, kwargs
# Make sure atexit doesn't drop def raise1(self):
def indirect(): raise TypeError
print "indirect exit"
import atexit def raise2(self):
atexit.register(indirect) raise SystemError
"""
f = file(fname, "w") def test_main():
f.write(input) test_support.run_unittest(TestCase)
f.close()
p = popen('"%s" %s' % (executable, fname))
output = p.read()
p.close()
vereq(output, """\
indirect exit
direct exit
""")
unlink(fname) if __name__ == "__main__":
test_main()

View File

@ -2,6 +2,17 @@
Python News 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.) (editors: check NEWS.help for information about editing NEWS using ReST.)
What's New in Python 2.4 beta 2? What's New in Python 2.4 beta 2?