Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cpython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
cpython
Commits
09761e7c
Commit
09761e7c
authored
Jan 22, 2014
by
Nick Coghlan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Issue #20317: Don't create a reference loop in ExitStack
parent
0e3b0e39
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
38 additions
and
1 deletion
+38
-1
Lib/contextlib.py
Lib/contextlib.py
+9
-1
Lib/test/test_contextlib.py
Lib/test/test_contextlib.py
+23
-0
Misc/NEWS
Misc/NEWS
+6
-0
No files found.
Lib/contextlib.py
View file @
09761e7c
...
@@ -231,11 +231,19 @@ class ExitStack(object):
...
@@ -231,11 +231,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
...
...
Lib/test/test_contextlib.py
View file @
09761e7c
...
@@ -600,6 +600,29 @@ class TestExitStack(unittest.TestCase):
...
@@ -600,6 +600,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
...
...
Misc/NEWS
View file @
09761e7c
...
@@ -50,6 +50,12 @@ Core and Builtins
...
@@ -50,6 +50,12 @@ Core and Builtins
Library
Library
-------
-------
- 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.
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment