]> granicus.if.org Git - python/commitdiff
Fix bug 1052242. Also includes rewrite of test case using unittest and
authorSkip Montanaro <skip@pobox.com>
Thu, 4 Nov 2004 04:31:30 +0000 (04:31 +0000)
committerSkip Montanaro <skip@pobox.com>
Thu, 4 Nov 2004 04:31:30 +0000 (04:31 +0000)
avoiding use of popen.

Doc/lib/libatexit.tex
Lib/atexit.py
Lib/test/test_atexit.py
Misc/NEWS

index c9775d1b6ddf673b503271fd22808507d2188c19..922f5d43738a806622cf7de06f0823a5c4e4f1e9 100644 (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}
 
 
index 85ccb248e4c5b3b94cd95c5bf5aa364f180e77f1..e109eb590da7eb0c3723eca3c381ce5e0778f631 100644 (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__":
index 1d120dfe2ca9356e816c03b303159b0e5513e54b..e57c48aef991f9f0c16cd1838c63039d50764ebf 100644 (file)
-# 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()
index e2b1937277041251740fd99f96e20fd45295c1bb..f44e10d3075f9653683f04d573d385f380caf8fa 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -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?