| # Copyright 2007 Google, Inc. All Rights Reserved. | |
| # Licensed to PSF under a Contributor Agreement. | |
| """Tests for the raise statement.""" | |
| from test import support | |
| import sys | |
| import types | |
| import unittest | |
| def get_tb(): | |
| try: | |
| raise OSError() | |
| except OSError as e: | |
| return e.__traceback__ | |
| 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: | |
| raise | |
| except RuntimeError as e: | |
| self.assertIn("No active exception", str(e)) | |
| else: | |
| self.fail("No exception raised") | |
| def test_reraise(self): | |
| try: | |
| try: | |
| raise IndexError() | |
| except IndexError as e: | |
| exc1 = e | |
| raise | |
| except IndexError as exc2: | |
| self.assertIs(exc1, exc2) | |
| else: | |
| self.fail("No exception raised") | |
| def test_except_reraise(self): | |
| def reraise(): | |
| try: | |
| raise TypeError("foo") | |
| except TypeError: | |
| try: | |
| raise KeyError("caught") | |
| except KeyError: | |
| pass | |
| raise | |
| self.assertRaises(TypeError, reraise) | |
| def test_finally_reraise(self): | |
| def reraise(): | |
| try: | |
| raise TypeError("foo") | |
| except TypeError: | |
| 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 TypeError: | |
| nested_reraise() | |
| self.assertRaises(TypeError, reraise) | |
| def test_raise_from_None(self): | |
| try: | |
| try: | |
| raise TypeError("foo") | |
| except TypeError: | |
| raise ValueError() from None | |
| except ValueError as e: | |
| self.assertIsInstance(e.__context__, TypeError) | |
| self.assertIsNone(e.__cause__) | |
| def test_with_reraise1(self): | |
| def reraise(): | |
| try: | |
| raise TypeError("foo") | |
| except TypeError: | |
| with Context(): | |
| pass | |
| raise | |
| self.assertRaises(TypeError, reraise) | |
| def test_with_reraise2(self): | |
| def reraise(): | |
| try: | |
| raise TypeError("foo") | |
| except TypeError: | |
| with Context(): | |
| raise KeyError("caught") | |
| raise | |
| self.assertRaises(TypeError, reraise) | |
| def test_yield_reraise(self): | |
| def reraise(): | |
| try: | |
| raise TypeError("foo") | |
| except TypeError: | |
| 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): | |
| raise RuntimeError() | |
| try: | |
| raise MyException | |
| except RuntimeError: | |
| pass | |
| else: | |
| self.fail("No exception raised") | |
| def test_new_returns_invalid_instance(self): | |
| # See issue #11627. | |
| class MyException(Exception): | |
| def __new__(cls, *args): | |
| return object() | |
| with self.assertRaises(TypeError): | |
| raise MyException | |
| def test_assert_with_tuple_arg(self): | |
| try: | |
| assert False, (3,) | |
| except AssertionError as e: | |
| self.assertEqual(str(e), "(3,)") | |
| class TestCause(unittest.TestCase): | |
| def testCauseSyntax(self): | |
| try: | |
| try: | |
| try: | |
| raise TypeError | |
| except Exception: | |
| raise ValueError from None | |
| except ValueError as exc: | |
| self.assertIsNone(exc.__cause__) | |
| self.assertTrue(exc.__suppress_context__) | |
| exc.__suppress_context__ = False | |
| raise exc | |
| except ValueError as exc: | |
| e = exc | |
| self.assertIsNone(e.__cause__) | |
| self.assertFalse(e.__suppress_context__) | |
| self.assertIsInstance(e.__context__, TypeError) | |
| def test_invalid_cause(self): | |
| try: | |
| raise IndexError from 5 | |
| except TypeError as e: | |
| self.assertIn("exception cause", str(e)) | |
| else: | |
| self.fail("No exception raised") | |
| def test_class_cause(self): | |
| try: | |
| raise IndexError from KeyError | |
| except IndexError as e: | |
| self.assertIsInstance(e.__cause__, KeyError) | |
| else: | |
| self.fail("No exception raised") | |
| def test_class_cause_nonexception_result(self): | |
| # See https://github.com/python/cpython/issues/140530. | |
| class ConstructMortal(BaseException): | |
| def __new__(*args, **kwargs): | |
| return ["mortal value"] | |
| msg = ".*should have returned an instance of BaseException.*" | |
| with self.assertRaisesRegex(TypeError, msg): | |
| raise IndexError from ConstructMortal | |
| def test_instance_cause(self): | |
| cause = KeyError() | |
| try: | |
| raise IndexError from cause | |
| except IndexError as e: | |
| self.assertIs(e.__cause__, cause) | |
| else: | |
| self.fail("No exception raised") | |
| def test_erroneous_cause(self): | |
| class MyException(Exception): | |
| def __init__(self): | |
| raise RuntimeError() | |
| try: | |
| raise IndexError from MyException | |
| except RuntimeError: | |
| pass | |
| else: | |
| self.fail("No exception raised") | |
| class TestTraceback(unittest.TestCase): | |
| def test_sets_traceback(self): | |
| try: | |
| raise IndexError() | |
| except IndexError as e: | |
| self.assertIsInstance(e.__traceback__, types.TracebackType) | |
| else: | |
| self.fail("No exception raised") | |
| def test_accepts_traceback(self): | |
| tb = get_tb() | |
| try: | |
| raise IndexError().with_traceback(tb) | |
| except IndexError as e: | |
| self.assertNotEqual(e.__traceback__, tb) | |
| self.assertEqual(e.__traceback__.tb_next, tb) | |
| else: | |
| self.fail("No exception raised") | |
| class TestTracebackType(unittest.TestCase): | |
| def raiser(self): | |
| raise ValueError | |
| def test_attrs(self): | |
| try: | |
| self.raiser() | |
| except Exception as exc: | |
| tb = exc.__traceback__ | |
| self.assertIsInstance(tb.tb_next, types.TracebackType) | |
| self.assertIs(tb.tb_frame, sys._getframe()) | |
| self.assertIsInstance(tb.tb_lasti, int) | |
| self.assertIsInstance(tb.tb_lineno, int) | |
| self.assertIs(tb.tb_next.tb_next, None) | |
| # Invalid assignments | |
| with self.assertRaises(AttributeError): | |
| del tb.tb_next | |
| with self.assertRaises(TypeError): | |
| tb.tb_next = "asdf" | |
| # Loops | |
| with self.assertRaises(ValueError): | |
| tb.tb_next = tb | |
| with self.assertRaises(ValueError): | |
| tb.tb_next.tb_next = tb | |
| # Valid assignments | |
| tb.tb_next = None | |
| self.assertIs(tb.tb_next, None) | |
| new_tb = get_tb() | |
| tb.tb_next = new_tb | |
| self.assertIs(tb.tb_next, new_tb) | |
| def test_constructor(self): | |
| other_tb = get_tb() | |
| frame = sys._getframe() | |
| tb = types.TracebackType(other_tb, frame, 1, 2) | |
| self.assertEqual(tb.tb_next, other_tb) | |
| self.assertEqual(tb.tb_frame, frame) | |
| self.assertEqual(tb.tb_lasti, 1) | |
| self.assertEqual(tb.tb_lineno, 2) | |
| tb = types.TracebackType(None, frame, 1, 2) | |
| self.assertEqual(tb.tb_next, None) | |
| with self.assertRaises(TypeError): | |
| types.TracebackType("no", frame, 1, 2) | |
| with self.assertRaises(TypeError): | |
| types.TracebackType(other_tb, "no", 1, 2) | |
| with self.assertRaises(TypeError): | |
| types.TracebackType(other_tb, frame, "no", 2) | |
| with self.assertRaises(TypeError): | |
| types.TracebackType(other_tb, frame, 1, "nuh-uh") | |
| class TestContext(unittest.TestCase): | |
| def test_instance_context_instance_raise(self): | |
| context = IndexError() | |
| try: | |
| try: | |
| raise context | |
| except IndexError: | |
| raise OSError() | |
| except OSError as e: | |
| self.assertIs(e.__context__, context) | |
| else: | |
| self.fail("No exception raised") | |
| def test_class_context_instance_raise(self): | |
| context = IndexError | |
| try: | |
| try: | |
| raise context | |
| except IndexError: | |
| raise OSError() | |
| except OSError as e: | |
| self.assertIsNot(e.__context__, context) | |
| self.assertIsInstance(e.__context__, context) | |
| else: | |
| self.fail("No exception raised") | |
| def test_class_context_class_raise(self): | |
| context = IndexError | |
| try: | |
| try: | |
| raise context | |
| except IndexError: | |
| raise OSError | |
| except OSError as e: | |
| self.assertIsNot(e.__context__, context) | |
| self.assertIsInstance(e.__context__, context) | |
| else: | |
| self.fail("No exception raised") | |
| def test_c_exception_context(self): | |
| try: | |
| try: | |
| 1/0 | |
| except ZeroDivisionError: | |
| raise OSError | |
| except OSError as e: | |
| self.assertIsInstance(e.__context__, ZeroDivisionError) | |
| else: | |
| self.fail("No exception raised") | |
| def test_c_exception_raise(self): | |
| try: | |
| try: | |
| 1/0 | |
| except ZeroDivisionError: | |
| xyzzy | |
| except NameError as e: | |
| self.assertIsInstance(e.__context__, ZeroDivisionError) | |
| else: | |
| self.fail("No exception raised") | |
| def test_noraise_finally(self): | |
| try: | |
| try: | |
| pass | |
| finally: | |
| raise OSError | |
| except OSError as e: | |
| self.assertIsNone(e.__context__) | |
| else: | |
| self.fail("No exception raised") | |
| def test_raise_finally(self): | |
| try: | |
| try: | |
| 1/0 | |
| finally: | |
| raise OSError | |
| except OSError as e: | |
| self.assertIsInstance(e.__context__, ZeroDivisionError) | |
| else: | |
| self.fail("No exception raised") | |
| def test_context_manager(self): | |
| class ContextManager: | |
| def __enter__(self): | |
| pass | |
| def __exit__(self, t, v, tb): | |
| xyzzy | |
| try: | |
| with ContextManager(): | |
| 1/0 | |
| except NameError as e: | |
| self.assertIsInstance(e.__context__, ZeroDivisionError) | |
| else: | |
| self.fail("No exception raised") | |
| def test_cycle_broken(self): | |
| # Self-cycles (when re-raising a caught exception) are broken | |
| try: | |
| try: | |
| 1/0 | |
| except ZeroDivisionError as e: | |
| raise e | |
| except ZeroDivisionError as e: | |
| self.assertIsNone(e.__context__) | |
| def test_reraise_cycle_broken(self): | |
| # Non-trivial context cycles (through re-raising a previous exception) | |
| # are broken too. | |
| try: | |
| try: | |
| xyzzy | |
| except NameError as a: | |
| try: | |
| 1/0 | |
| except ZeroDivisionError: | |
| raise a | |
| except NameError as e: | |
| self.assertIsNone(e.__context__.__context__) | |
| def test_not_last(self): | |
| # Context is not necessarily the last exception | |
| context = Exception("context") | |
| try: | |
| raise context | |
| except Exception: | |
| try: | |
| raise Exception("caught") | |
| except Exception: | |
| pass | |
| try: | |
| raise Exception("new") | |
| except Exception as exc: | |
| raised = exc | |
| self.assertIs(raised.__context__, context) | |
| def test_3118(self): | |
| # deleting the generator caused the __context__ to be cleared | |
| def gen(): | |
| try: | |
| yield 1 | |
| finally: | |
| pass | |
| def f(): | |
| g = gen() | |
| next(g) | |
| try: | |
| try: | |
| raise ValueError | |
| except ValueError: | |
| del g | |
| raise KeyError | |
| except Exception as e: | |
| self.assertIsInstance(e.__context__, ValueError) | |
| f() | |
| def test_3611(self): | |
| import gc | |
| # A re-raised exception in a __del__ caused the __context__ | |
| # to be cleared | |
| class C: | |
| def __del__(self): | |
| try: | |
| 1/0 | |
| except ZeroDivisionError: | |
| raise | |
| def f(): | |
| x = C() | |
| try: | |
| try: | |
| f.x | |
| except AttributeError: | |
| # make x.__del__ trigger | |
| del x | |
| gc.collect() # For PyPy or other GCs. | |
| raise TypeError | |
| except Exception as e: | |
| self.assertNotEqual(e.__context__, None) | |
| self.assertIsInstance(e.__context__, AttributeError) | |
| with support.catch_unraisable_exception() as cm: | |
| f() | |
| self.assertEqual(ZeroDivisionError, cm.unraisable.exc_type) | |
| class TestRemovedFunctionality(unittest.TestCase): | |
| def test_tuples(self): | |
| try: | |
| raise (IndexError, KeyError) # This should be a tuple! | |
| except TypeError: | |
| pass | |
| else: | |
| self.fail("No exception raised") | |
| def test_strings(self): | |
| try: | |
| raise "foo" | |
| except TypeError: | |
| pass | |
| else: | |
| self.fail("No exception raised") | |
| if __name__ == "__main__": | |
| unittest.main() |