Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-102799: use sys.exception() instead of sys.exc_info() in contextlib #103311

Merged
merged 7 commits into from
Jul 20, 2023
Merged
54 changes: 32 additions & 22 deletions Lib/contextlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -553,11 +553,12 @@ def __enter__(self):
return self

def __exit__(self, *exc_details):
received_exc = exc_details[0] is not None
exc = exc_details[1]
received_exc = exc is not None

# We manipulate the exception state so it behaves as though
# we were actually nesting multiple with statements
frame_exc = sys.exc_info()[1]
frame_exc = sys.exception()
def _fix_exception_context(new_exc, old_exc):
# Context may not be correct, so find the end of the chain
while 1:
Expand All @@ -580,24 +581,28 @@ def _fix_exception_context(new_exc, old_exc):
is_sync, cb = self._exit_callbacks.pop()
assert is_sync
try:
if exc is None:
exc_details = None, None, None
else:
exc_details = type(exc), exc, exc.__traceback__
if cb(*exc_details):
suppressed_exc = True
pending_raise = False
exc_details = (None, None, None)
except:
new_exc_details = sys.exc_info()
exc = None
except BaseException as new_exc:
# simulate the stack of exceptions by setting the context
_fix_exception_context(new_exc_details[1], exc_details[1])
_fix_exception_context(new_exc, exc)
pending_raise = True
exc_details = new_exc_details
exc = new_exc

if pending_raise:
try:
# bare "raise exc_details[1]" replaces our carefully
# bare "raise exc" replaces our carefully
# set-up context
fixed_ctx = exc_details[1].__context__
raise exc_details[1]
fixed_ctx = exc.__context__
raise exc
except BaseException:
exc_details[1].__context__ = fixed_ctx
exc.__context__ = fixed_ctx
raise
return received_exc and suppressed_exc

Expand Down Expand Up @@ -693,11 +698,12 @@ async def __aenter__(self):
return self

async def __aexit__(self, *exc_details):
received_exc = exc_details[0] is not None
exc = exc_details[1]
received_exc = exc is not None

# We manipulate the exception state so it behaves as though
# we were actually nesting multiple with statements
frame_exc = sys.exc_info()[1]
frame_exc = sys.exception()
def _fix_exception_context(new_exc, old_exc):
# Context may not be correct, so find the end of the chain
while 1:
Expand All @@ -719,6 +725,10 @@ def _fix_exception_context(new_exc, old_exc):
while self._exit_callbacks:
is_sync, cb = self._exit_callbacks.pop()
try:
if exc is None:
exc_details = None, None, None
else:
exc_details = type(exc), exc, exc.__traceback__
if is_sync:
cb_suppress = cb(*exc_details)
else:
Expand All @@ -727,21 +737,21 @@ def _fix_exception_context(new_exc, old_exc):
if cb_suppress:
suppressed_exc = True
pending_raise = False
exc_details = (None, None, None)
except:
new_exc_details = sys.exc_info()
exc = None
except BaseException as new_exc:
# simulate the stack of exceptions by setting the context
_fix_exception_context(new_exc_details[1], exc_details[1])
_fix_exception_context(new_exc, exc)
pending_raise = True
exc_details = new_exc_details
exc = new_exc

if pending_raise:
try:
# bare "raise exc_details[1]" replaces our carefully
# bare "raise exc" replaces our carefully
# set-up context
fixed_ctx = exc_details[1].__context__
raise exc_details[1]
fixed_ctx = exc.__context__
raise exc
except BaseException:
exc_details[1].__context__ = fixed_ctx
exc.__context__ = fixed_ctx
raise
return received_exc and suppressed_exc

Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_contextlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -1075,7 +1075,7 @@ def first():
class TestExitStack(TestBaseExitStack, unittest.TestCase):
exit_stack = ExitStack
callback_error_internal_frames = [
('__exit__', 'raise exc_details[1]'),
('__exit__', 'raise exc'),
('__exit__', 'if cb(*exc_details):'),
]

Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_contextlib_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,7 @@ def __exit__(self, *exc_details):
('__exit__', 'return self.run_coroutine(self.__aexit__(*exc_details))'),
('run_coroutine', 'raise exc'),
('run_coroutine', 'raise exc'),
('__aexit__', 'raise exc_details[1]'),
('__aexit__', 'raise exc'),
('__aexit__', 'cb_suppress = cb(*exc_details)'),
]

Expand Down
Loading