summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSkip Montanaro <skip@pobox.com>2004-11-04 04:31:30 (GMT)
committerSkip Montanaro <skip@pobox.com>2004-11-04 04:31:30 (GMT)
commit599bd5e1e16aaa3a7652d245b6b2a018e772a557 (patch)
tree0c2437879be035ea36812826af8cb6565ee2045f
parented306292d68fe8ca3fb48f31129fe68229741519 (diff)
downloadcpython-599bd5e1e16aaa3a7652d245b6b2a018e772a557.zip
cpython-599bd5e1e16aaa3a7652d245b6b2a018e772a557.tar.gz
cpython-599bd5e1e16aaa3a7652d245b6b2a018e772a557.tar.bz2
Fix bug 1052242. Also includes rewrite of test case using unittest and
avoiding use of popen.
-rw-r--r--Doc/lib/libatexit.tex6
-rw-r--r--Lib/atexit.py16
-rw-r--r--Lib/test/test_atexit.py138
-rw-r--r--Misc/NEWS11
4 files changed, 117 insertions, 54 deletions
diff --git a/Doc/lib/libatexit.tex b/Doc/lib/libatexit.tex
index c9775d1..922f5d4 100644
--- a/Doc/lib/libatexit.tex
+++ b/Doc/lib/libatexit.tex
@@ -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}
diff --git a/Lib/atexit.py b/Lib/atexit.py
index 85ccb24..e109eb5 100644
--- a/Lib/atexit.py
+++ b/Lib/atexit.py
@@ -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__":
diff --git a/Lib/test/test_atexit.py b/Lib/test/test_atexit.py
index 1d120df..e57c48a 100644
--- a/Lib/test/test_atexit.py
+++ b/Lib/test/test_atexit.py
@@ -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()
diff --git a/Misc/NEWS b/Misc/NEWS
index e2b1937..f44e10d 100644
--- 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?