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
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}

View File

@ -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__":

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
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()

View File

@ -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?