Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
additional test for RuntimeError wrapping
  • Loading branch information
JelleZijlstra committed Mar 2, 2017
commit 9caa2435a76836a9c7cf2b35f123e72e6186e297
11 changes: 9 additions & 2 deletions Lib/contextlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,15 @@ async def __aexit__(self, type, value, traceback):
except RuntimeError as exc:
if exc is value:
return False
if exc.__cause__ is value:
return False
# Avoid suppressing if a StopIteration exception
# was passed to throw() and later wrapped into a RuntimeError
# (see PEP 479 for sync generators; async generators also
# have this behavior). But do this only if the exception wrapped
# by the RuntimeError is actully Stop(Async)Iteration (see
# issue29692).
if isinstance(value, (StopIteration, StopAsyncIteration)):
if exc.__cause__ is value:
return False
raise
except:
if sys.exc_info()[1] is not value:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why we use sys.exc_info() here instead of

except BaseException as exc:
    if exc is not value:
        raise

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did this in order to not make unnecessary changes relative to the @contextmanager implementation, although I agree your code is better. I'm happy to change this if @ncoghlan is OK with it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It uses sys.exc_info() because the corresponding _GeneratorContextManager code predates the acceptance and implementation of PEP 352, and is also common between 2.7 and 3.x (and hence needs to handle exceptions that don't inherit from BaseException)

I'm fine with switching this to using the BaseException form, but add a comment to the synchronous version saying it's written that way on purpose to keep the code consistent with the 2.7 branch and the contextlib2 backport.

Expand Down
20 changes: 20 additions & 0 deletions Lib/test/test_contextlib_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,26 @@ async def woohoo():
else:
self.fail(f'{stop_exc} was suppressed')

@_async_test
async def test_contextmanager_wrap_runtimeerror(self):
@asynccontextmanager
async def woohoo():
try:
yield
except Exception as exc:
raise RuntimeError(f'caught {exc}') from exc

with self.assertRaises(RuntimeError):
async with woohoo():
1 / 0

# If the context manager wrapped StopAsyncIteration in a RuntimeError,
# we also unwrap it, because we can't tell whether the wrapping was
# done by the generator machinery or by the generator itself.
with self.assertRaises(StopAsyncIteration):
async with woohoo():
raise StopAsyncIteration

def _create_contextmanager_attribs(self):
def attribs(**kw):
def decorate(func):
Expand Down