diff --git a/contextlib2.py b/contextlib2.py index a6acf65..f08df14 100644 --- a/contextlib2.py +++ b/contextlib2.py @@ -102,6 +102,9 @@ def __exit__(self, type, value, traceback): # raised inside the "with" statement from being suppressed. return exc is not value except RuntimeError as exc: + # Don't re-raise the passed in exception + if exc is value: + return False # Likewise, avoid suppressing if a StopIteration exception # was passed to throw() and later wrapped into a RuntimeError # (see PEP 479). diff --git a/test_contextlib2.py b/test_contextlib2.py index 3aba52f..e9cd0c7 100755 --- a/test_contextlib2.py +++ b/test_contextlib2.py @@ -739,6 +739,44 @@ def __call__(self, *exc_details): pass + def test_dont_reraise_RuntimeError(self): + # https://bugs.python.org/issue27122 + class UniqueException(Exception): pass + class UniqueRuntimeError(RuntimeError): pass + + @contextmanager + def second(): + try: + yield 1 + except Exception as exc: + # Py2 compatible explicit exception chaining + new_exc = UniqueException("new exception") + new_exc.__cause__ = exc + raise new_exc + + @contextmanager + def first(): + try: + yield 1 + except Exception as exc: + raise exc + + # The UniqueRuntimeError should be caught by second()'s exception + # handler which chain raised a new UniqueException. + with self.assertRaises(UniqueException) as err_ctx: + with ExitStack() as es_ctx: + es_ctx.enter_context(second()) + es_ctx.enter_context(first()) + raise UniqueRuntimeError("please no infinite loop.") + + exc = err_ctx.exception + self.assertIsInstance(exc, UniqueException) + self.assertIsInstance(exc.__cause__, UniqueRuntimeError) + if check_exception_chaining: + self.assertIs(exc.__context__, exc.__cause__) + self.assertIsNone(exc.__cause__.__context__) + self.assertIsNone(exc.__cause__.__cause__) + class TestRedirectStream: