Index: Lib/atexit.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/atexit.py,v retrieving revision 1.7 diff -u -r1.7 atexit.py --- Lib/atexit.py 12 Feb 2004 17:35:05 -0000 1.7 +++ Lib/atexit.py 1 Nov 2004 00:23:48 -0000 @@ -15,9 +15,22 @@ 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 @@ # Assume it's another registered exit function - append it to our list register(sys.exitfunc) sys.exitfunc = _run_exitfuncs - del sys if __name__ == "__main__": Index: Lib/test/test_atexit.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/test/test_atexit.py,v retrieving revision 1.10 diff -u -r1.10 test_atexit.py --- Lib/test/test_atexit.py 24 Feb 2003 15:26:39 -0000 1.10 +++ Lib/test/test_atexit.py 1 Nov 2004 00:23:48 -0000 @@ -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" - -def handler2(*args, **kargs): - print "handler2", args, kargs - -atexit.register(handler1) -atexit.register(handler2) -atexit.register(handler2, 7, kw="abc") -""" - -fname = TESTFN + ".py" -f = file(fname, "w") -f.write(input) -f.close() - -p = popen('"%s" %s' % (executable, fname)) -output = p.read() -p.close() -vereq(output, """\ -handler2 (7,) {'kw': 'abc'} -handler2 () {} -handler1 -""") - -input = """\ -def direct(): - print "direct exit" +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 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") + + 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") + + 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" + + def h2(self): + print "h2" + + def h3(self): + print "h3" + + def h4(self, *args, **kwargs): + print "h4", args, kwargs + + def raise1(self): + raise TypeError + + def raise2(self): + raise SystemError -import sys -sys.exitfunc = direct - -# Make sure atexit doesn't drop -def indirect(): - print "indirect exit" - -import atexit -atexit.register(indirect) -""" +def test_main(): + test_support.run_unittest(TestCase) -f = file(fname, "w") -f.write(input) -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() Index: Doc/lib/libatexit.tex =================================================================== RCS file: /cvsroot/python/python/dist/src/Doc/lib/libatexit.tex,v retrieving revision 1.8 diff -u -r1.8 libatexit.tex --- Doc/lib/libatexit.tex 8 Apr 2003 17:46:53 -0000 1.8 +++ Doc/lib/libatexit.tex 1 Nov 2004 00:23:48 -0000 @@ -39,6 +39,12 @@ 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}