cpython: b3c2472c12a1 (original) (raw)
Mercurial > cpython
changeset 88637:b3c2472c12a1 3.3
Issue #20317: Don't create a reference loop in ExitStack [#20317]
Nick Coghlan ncoghlan@gmail.com | |
---|---|
date | Wed, 22 Jan 2014 22:24:46 +1000 |
parents | 1b89fd73c625 |
children | 46c3ea358784 b26db63bb931 |
files | Lib/contextlib.py Lib/test/test_contextlib.py Misc/NEWS |
diffstat | 3 files changed, 38 insertions(+), 1 deletions(-)[+] [-] Lib/contextlib.py 10 Lib/test/test_contextlib.py 23 Misc/NEWS 6 |
line wrap: on
line diff
--- a/Lib/contextlib.py +++ b/Lib/contextlib.py @@ -231,11 +231,19 @@ class ExitStack(object): # we were actually nesting multiple with statements frame_exc = sys.exc_info()[1] def _fix_exception_context(new_exc, old_exc):
# Context isn't what we want, so find the end of the chain[](#l1.7) while 1:[](#l1.8) exc_context = new_exc.__context__[](#l1.9)
if exc_context in (None, frame_exc):[](#l1.10)
if exc_context is old_exc:[](#l1.11)
# Context is already set correctly (see issue 20317)[](#l1.12)
return[](#l1.13)
if exc_context is None or exc_context is frame_exc:[](#l1.14) break[](#l1.15)
details = id(new_exc), id(old_exc), id(exc_context)[](#l1.16)
raise Exception(str(details))[](#l1.17) new_exc = exc_context[](#l1.18)
# Change the end of the chain to point to the exception[](#l1.19)
# we expect it to reference[](#l1.20) new_exc.__context__ = old_exc[](#l1.21)
# Callbacks are invoked in LIFO order to match the behaviour of
--- a/Lib/test/test_contextlib.py +++ b/Lib/test/test_contextlib.py @@ -600,6 +600,29 @@ class TestExitStack(unittest.TestCase): else: self.fail("Expected KeyError, but no exception was raised")
- def test_exit_exception_with_correct_context(self):
# http://bugs.python.org/issue20317[](#l2.8)
@contextmanager[](#l2.9)
def gets_the_context_right():[](#l2.10)
try:[](#l2.11)
yield 6[](#l2.12)
finally:[](#l2.13)
1 / 0[](#l2.14)
# The contextmanager already fixes the context, so prior to the[](#l2.16)
# fix, ExitStack would try to fix it *again* and get into an[](#l2.17)
# infinite self-referential loop[](#l2.18)
try:[](#l2.19)
with ExitStack() as stack:[](#l2.20)
stack.enter_context(gets_the_context_right())[](#l2.21)
stack.enter_context(gets_the_context_right())[](#l2.22)
stack.enter_context(gets_the_context_right())[](#l2.23)
except ZeroDivisionError as exc:[](#l2.24)
self.assertIsInstance(exc.__context__, ZeroDivisionError)[](#l2.25)
self.assertIsInstance(exc.__context__.__context__, ZeroDivisionError)[](#l2.26)
self.assertIsNone(exc.__context__.__context__.__context__)[](#l2.27)
+ + def test_body_exception_suppress(self): def suppress_exc(*exc_details): return True
--- a/Misc/NEWS +++ b/Misc/NEWS @@ -50,6 +50,12 @@ Core and Builtins Library ------- +- Issue #20317: ExitStack.exit could create a self-referential loop if an