#3021: Antoine Pitrou's Lexical exception handlers
diff --git a/Lib/doctest.py b/Lib/doctest.py
index dad8333..74be21e 100644
--- a/Lib/doctest.py
+++ b/Lib/doctest.py
@@ -1242,10 +1242,9 @@
# The example raised an exception: check if it was expected.
else:
- exc_info = sys.exc_info()
- exc_msg = traceback.format_exception_only(*exc_info[:2])[-1]
+ exc_msg = traceback.format_exception_only(*exception[:2])[-1]
if not quiet:
- got += _exception_traceback(exc_info)
+ got += _exception_traceback(exception)
# If `example.exc_msg` is None, then we weren't expecting
# an exception.
@@ -1275,7 +1274,7 @@
elif outcome is BOOM:
if not quiet:
self.report_unexpected_exception(out, test, example,
- exc_info)
+ exception)
failures += 1
else:
assert False, ("unknown outcome", outcome)
diff --git a/Lib/inspect.py b/Lib/inspect.py
index 5758abd..e89b5f0 100644
--- a/Lib/inspect.py
+++ b/Lib/inspect.py
@@ -197,9 +197,6 @@
f_back next outer frame object (this frame's caller)
f_builtins built-in namespace seen by this frame
f_code code object being executed in this frame
- f_exc_traceback traceback if raised in this frame, or None
- f_exc_type exception type if raised in this frame, or None
- f_exc_value exception value if raised in this frame, or None
f_globals global namespace seen by this frame
f_lasti index of last attempted instruction in bytecode
f_lineno current line number in Python source code
diff --git a/Lib/opcode.py b/Lib/opcode.py
index eac0b63..50e10ee 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -105,6 +105,7 @@
def_op('YIELD_VALUE', 86)
def_op('POP_BLOCK', 87)
def_op('END_FINALLY', 88)
+def_op('POP_EXCEPT', 89)
HAVE_ARGUMENT = 90 # Opcodes from here have an argument:
diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py
index 41b9413..9068554 100644
--- a/Lib/test/test_exceptions.py
+++ b/Lib/test/test_exceptions.py
@@ -427,6 +427,7 @@
local_ref = obj
raise MyException(obj)
+ # Qualified "except" with "as"
obj = MyObj()
wr = weakref.ref(obj)
try:
@@ -437,6 +438,113 @@
obj = wr()
self.failUnless(obj is None, "%s" % obj)
+ # Qualified "except" without "as"
+ obj = MyObj()
+ wr = weakref.ref(obj)
+ try:
+ inner_raising_func()
+ except MyException:
+ pass
+ obj = None
+ obj = wr()
+ self.failUnless(obj is None, "%s" % obj)
+
+ # Bare "except"
+ obj = MyObj()
+ wr = weakref.ref(obj)
+ try:
+ inner_raising_func()
+ except:
+ pass
+ obj = None
+ obj = wr()
+ self.failUnless(obj is None, "%s" % obj)
+
+ # "except" with premature block leave
+ obj = MyObj()
+ wr = weakref.ref(obj)
+ for i in [0]:
+ try:
+ inner_raising_func()
+ except:
+ break
+ obj = None
+ obj = wr()
+ self.failUnless(obj is None, "%s" % obj)
+
+ # "except" block raising another exception
+ obj = MyObj()
+ wr = weakref.ref(obj)
+ try:
+ try:
+ inner_raising_func()
+ except:
+ raise KeyError
+ except KeyError:
+ obj = None
+ obj = wr()
+ self.failUnless(obj is None, "%s" % obj)
+
+ # Some complicated construct
+ obj = MyObj()
+ wr = weakref.ref(obj)
+ try:
+ inner_raising_func()
+ except MyException:
+ try:
+ try:
+ raise
+ finally:
+ raise
+ except MyException:
+ pass
+ obj = None
+ obj = wr()
+ self.failUnless(obj is None, "%s" % obj)
+
+ # Inside an exception-silencing "with" block
+ class Context:
+ def __enter__(self):
+ return self
+ def __exit__ (self, exc_type, exc_value, exc_tb):
+ return True
+ obj = MyObj()
+ wr = weakref.ref(obj)
+ with Context():
+ inner_raising_func()
+ obj = None
+ obj = wr()
+ self.failUnless(obj is None, "%s" % obj)
+
+ def test_generator_leaking(self):
+ # Test that generator exception state doesn't leak into the calling
+ # frame
+ def yield_raise():
+ try:
+ raise KeyError("caught")
+ except KeyError:
+ yield sys.exc_info()[0]
+ yield sys.exc_info()[0]
+ yield sys.exc_info()[0]
+ g = yield_raise()
+ self.assertEquals(next(g), KeyError)
+ self.assertEquals(sys.exc_info()[0], None)
+ self.assertEquals(next(g), KeyError)
+ self.assertEquals(sys.exc_info()[0], None)
+ self.assertEquals(next(g), None)
+
+ # Same test, but inside an exception handler
+ try:
+ raise TypeError("foo")
+ except TypeError:
+ g = yield_raise()
+ self.assertEquals(next(g), KeyError)
+ self.assertEquals(sys.exc_info()[0], TypeError)
+ self.assertEquals(next(g), KeyError)
+ self.assertEquals(sys.exc_info()[0], TypeError)
+ self.assertEquals(next(g), TypeError)
+ del g
+ self.assertEquals(sys.exc_info()[0], TypeError)
def test_main():
run_unittest(ExceptionTests)
diff --git a/Lib/test/test_raise.py b/Lib/test/test_raise.py
index 89e2190..5f0070e 100644
--- a/Lib/test/test_raise.py
+++ b/Lib/test/test_raise.py
@@ -16,6 +16,13 @@
return sys.exc_info()[2]
+class Context:
+ def __enter__(self):
+ return self
+ def __exit__(self, exc_type, exc_value, exc_tb):
+ return True
+
+
class TestRaise(unittest.TestCase):
def test_invalid_reraise(self):
try:
@@ -37,6 +44,71 @@
else:
self.fail("No exception raised")
+ def test_except_reraise(self):
+ def reraise():
+ try:
+ raise TypeError("foo")
+ except:
+ try:
+ raise KeyError("caught")
+ except KeyError:
+ pass
+ raise
+ self.assertRaises(TypeError, reraise)
+
+ def test_finally_reraise(self):
+ def reraise():
+ try:
+ raise TypeError("foo")
+ except:
+ try:
+ raise KeyError("caught")
+ finally:
+ raise
+ self.assertRaises(KeyError, reraise)
+
+ def test_nested_reraise(self):
+ def nested_reraise():
+ raise
+ def reraise():
+ try:
+ raise TypeError("foo")
+ except:
+ nested_reraise()
+ self.assertRaises(TypeError, reraise)
+
+ def test_with_reraise1(self):
+ def reraise():
+ try:
+ raise TypeError("foo")
+ except:
+ with Context():
+ pass
+ raise
+ self.assertRaises(TypeError, reraise)
+
+ def test_with_reraise2(self):
+ def reraise():
+ try:
+ raise TypeError("foo")
+ except:
+ with Context():
+ raise KeyError("caught")
+ raise
+ self.assertRaises(TypeError, reraise)
+
+ def test_yield_reraise(self):
+ def reraise():
+ try:
+ raise TypeError("foo")
+ except:
+ yield 1
+ raise
+ g = reraise()
+ next(g)
+ self.assertRaises(TypeError, lambda: next(g))
+ self.assertRaises(StopIteration, lambda: next(g))
+
def test_erroneous_exception(self):
class MyException(Exception):
def __init__(self):
@@ -158,6 +230,5 @@
def test_main():
support.run_unittest(__name__)
-
if __name__ == "__main__":
unittest.main()