mirror of
https://github.com/python/cpython.git
synced 2025-09-27 18:59:43 +00:00
Merge #20317 from 3.3
This commit is contained in:
commit
d58831e688
3 changed files with 38 additions and 1 deletions
|
@ -298,11 +298,19 @@ class ExitStack(object):
|
||||||
# we were actually nesting multiple with statements
|
# we were actually nesting multiple with statements
|
||||||
frame_exc = sys.exc_info()[1]
|
frame_exc = sys.exc_info()[1]
|
||||||
def _fix_exception_context(new_exc, old_exc):
|
def _fix_exception_context(new_exc, old_exc):
|
||||||
|
# Context isn't what we want, so find the end of the chain
|
||||||
while 1:
|
while 1:
|
||||||
exc_context = new_exc.__context__
|
exc_context = new_exc.__context__
|
||||||
if exc_context in (None, frame_exc):
|
if exc_context is old_exc:
|
||||||
|
# Context is already set correctly (see issue 20317)
|
||||||
|
return
|
||||||
|
if exc_context is None or exc_context is frame_exc:
|
||||||
break
|
break
|
||||||
|
details = id(new_exc), id(old_exc), id(exc_context)
|
||||||
|
raise Exception(str(details))
|
||||||
new_exc = exc_context
|
new_exc = exc_context
|
||||||
|
# Change the end of the chain to point to the exception
|
||||||
|
# we expect it to reference
|
||||||
new_exc.__context__ = old_exc
|
new_exc.__context__ = old_exc
|
||||||
|
|
||||||
# Callbacks are invoked in LIFO order to match the behaviour of
|
# Callbacks are invoked in LIFO order to match the behaviour of
|
||||||
|
|
|
@ -626,6 +626,29 @@ class TestExitStack(unittest.TestCase):
|
||||||
else:
|
else:
|
||||||
self.fail("Expected KeyError, but no exception was raised")
|
self.fail("Expected KeyError, but no exception was raised")
|
||||||
|
|
||||||
|
def test_exit_exception_with_correct_context(self):
|
||||||
|
# http://bugs.python.org/issue20317
|
||||||
|
@contextmanager
|
||||||
|
def gets_the_context_right():
|
||||||
|
try:
|
||||||
|
yield 6
|
||||||
|
finally:
|
||||||
|
1 / 0
|
||||||
|
|
||||||
|
# The contextmanager already fixes the context, so prior to the
|
||||||
|
# fix, ExitStack would try to fix it *again* and get into an
|
||||||
|
# infinite self-referential loop
|
||||||
|
try:
|
||||||
|
with ExitStack() as stack:
|
||||||
|
stack.enter_context(gets_the_context_right())
|
||||||
|
stack.enter_context(gets_the_context_right())
|
||||||
|
stack.enter_context(gets_the_context_right())
|
||||||
|
except ZeroDivisionError as exc:
|
||||||
|
self.assertIsInstance(exc.__context__, ZeroDivisionError)
|
||||||
|
self.assertIsInstance(exc.__context__.__context__, ZeroDivisionError)
|
||||||
|
self.assertIsNone(exc.__context__.__context__.__context__)
|
||||||
|
|
||||||
|
|
||||||
def test_body_exception_suppress(self):
|
def test_body_exception_suppress(self):
|
||||||
def suppress_exc(*exc_details):
|
def suppress_exc(*exc_details):
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -37,6 +37,12 @@ Library
|
||||||
microsecond is now rounded to one millisecond, instead of being rounded to
|
microsecond is now rounded to one millisecond, instead of being rounded to
|
||||||
zero.
|
zero.
|
||||||
|
|
||||||
|
- Issue #20317: ExitStack.__exit__ could create a self-referential loop if an
|
||||||
|
exception raised by a cleanup operation already had its context set
|
||||||
|
correctly (for example, by the @contextmanager decorator). The infinite
|
||||||
|
loop this caused is now avoided by checking if the expected context is
|
||||||
|
already set before trying to fix it.
|
||||||
|
|
||||||
- Issue #20311: select.epoll.poll() now rounds the timeout away from zero,
|
- Issue #20311: select.epoll.poll() now rounds the timeout away from zero,
|
||||||
instead of rounding towards zero. For example, a timeout of one microsecond
|
instead of rounding towards zero. For example, a timeout of one microsecond
|
||||||
is now rounded to one millisecond, instead of being rounded to zero.
|
is now rounded to one millisecond, instead of being rounded to zero.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue