diff options
author | Benjamin Peterson <benjamin@python.org> | 2008-06-11 15:59:43 (GMT) |
---|---|---|
committer | Benjamin Peterson <benjamin@python.org> | 2008-06-11 15:59:43 (GMT) |
commit | eec3d7137929611b98dd593cd2f122cd91b723b2 (patch) | |
tree | 42721419d4fe3f53961ecfd7c1dea3224188ae40 /Lib | |
parent | e8465f2b413174084fcc2dc4cd7a53122c62ce4b (diff) | |
download | cpython-eec3d7137929611b98dd593cd2f122cd91b723b2.zip cpython-eec3d7137929611b98dd593cd2f122cd91b723b2.tar.gz cpython-eec3d7137929611b98dd593cd2f122cd91b723b2.tar.bz2 |
#3021: Antoine Pitrou's Lexical exception handlers
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/doctest.py | 7 | ||||
-rw-r--r-- | Lib/inspect.py | 3 | ||||
-rw-r--r-- | Lib/opcode.py | 1 | ||||
-rw-r--r-- | Lib/test/test_exceptions.py | 108 | ||||
-rw-r--r-- | Lib/test/test_raise.py | 73 |
5 files changed, 184 insertions, 8 deletions
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 @@ class DocTestRunner: # 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 @@ class DocTestRunner: 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 @@ def isframe(object): 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('MAKE_BYTES', 85) 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 @@ class ExceptionTests(unittest.TestCase): local_ref = obj raise MyException(obj) + # Qualified "except" with "as" obj = MyObj() wr = weakref.ref(obj) try: @@ -437,6 +438,113 @@ class ExceptionTests(unittest.TestCase): 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 @@ def get_tb(): 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 @@ class TestRaise(unittest.TestCase): 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 @@ class TestRemovedFunctionality(unittest.TestCase): def test_main(): support.run_unittest(__name__) - if __name__ == "__main__": unittest.main() |