mirror of https://github.com/python/cpython
Fix bug 1052242. Also includes rewrite of test case using unittest and
avoiding use of popen.
This commit is contained in:
parent
ed306292d6
commit
599bd5e1e1
|
@ -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}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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__":
|
||||||
|
|
|
@ -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()
|
||||||
|
|
11
Misc/NEWS
11
Misc/NEWS
|
@ -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?
|
||||||
|
|
Loading…
Reference in New Issue