diff --git a/NEWS.rst b/NEWS.rst index ec57b0d..d3f68b1 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -1,6 +1,20 @@ Release History --------------- +0.5.4 (2016-07-31) +^^^^^^^^^^^^^^^^^^ + +* Thanks to the welcome efforts of Jannis Leidel, contextlib2 is now a + [Jazzband](https://jazzband.co/) project! This means that I (Nick Coghlan) + am no longer a single point of failure for backports of future contextlib + updates to earlier Python versions. + +* Issue `#7 `__: Backported + fix for CPython issue `#27122 `__, + preventing a potential infinite loop on Python 3.5 when handling + ``RuntimeError`` (CPython updates by Gregory P. Smith & Serhiy Storchaka) + + 0.5.3 (2016-05-02) ^^^^^^^^^^^^^^^^^^ @@ -53,8 +67,8 @@ Release History 0.4.0 (2012-05-05) ^^^^^^^^^^^^^^^^^^ -* Issue #8: Replace ContextStack with ExitStack (old ContextStack API - retained for backwards compatibility) +* (BitBucket) Issue #8: Replace ContextStack with ExitStack (old ContextStack + API retained for backwards compatibility) * Fall back to unittest2 if unittest is missing required functionality @@ -62,20 +76,20 @@ Release History 0.3.1 (2012-01-17) ^^^^^^^^^^^^^^^^^^ -* Issue #7: Add MANIFEST.in so PyPI package contains all relevant files - (patch contributed by Doug Latornell) +* (BitBucket) Issue #7: Add MANIFEST.in so PyPI package contains all relevant + files (patch contributed by Doug Latornell) 0.3 (2012-01-04) ^^^^^^^^^^^^^^^^ -* Issue #5: ContextStack.register no longer pointlessly returns the wrapped - function -* Issue #2: Add examples and recipes section to docs -* Issue #3: ContextStack.register_exit() now accepts objects with __exit__ - attributes in addition to accepting exit callbacks directly -* Issue #1: Add ContextStack.preserve() to move all registered callbacks to - a new ContextStack object +* (BitBucket) Issue #5: ContextStack.register no longer pointlessly returns the + wrapped function +* (BitBucket) Issue #2: Add examples and recipes section to docs +* (BitBucket) Issue #3: ContextStack.register_exit() now accepts objects with + __exit__ attributes in addition to accepting exit callbacks directly +* (BitBucket) Issue #1: Add ContextStack.preserve() to move all registered + callbacks to a new ContextStack object * Wrapped callbacks now expose __wrapped__ (for direct callbacks) or __self__ (for context manager methods) attributes to aid in introspection * Moved version number to a VERSION.txt file (read by both docs and setup.py) diff --git a/VERSION.txt b/VERSION.txt index be14282..7d85683 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -0.5.3 +0.5.4 diff --git a/contextlib2.py b/contextlib2.py index a6acf65..f08df14 100644 --- a/contextlib2.py +++ b/contextlib2.py @@ -102,6 +102,9 @@ class _GeneratorContextManager(ContextDecorator): # 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 @@ class TestExitStack(unittest.TestCase): 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: