Issue1705170
Created on 2007-04-22 06:56 by Rhamphoryncus, last changed 2007-11-07 12:27 by ncoghlan.
|
msg31860 - (view) |
Author: Adam Olsen (Rhamphoryncus) |
Date: 2007-04-22 06:56 |
|
This is the same bug recently reported by Bob Sidebotham on python-dev (http://mail.python.org/pipermail/python-dev/2007-April/072484.html), but my test-case doesn't involve an overly broad try/except block.
It also seems similar to Bug 1462485, which has been closed and marked fixed, but I can reproduce with my test-case using both python 2.5 and python-trunk (revision 54759).
The irony is that catching the exception to add debugging forces it to be instantiated, thereby avoiding the bug.
|
|
msg57002 - (view) |
Author: Mike Klaas (klaas) |
Date: 2007-10-31 20:26 |
|
Verified on 2.5.0. The problem stems from contextmanager.__exit__:
def __exit__(self, type, value, traceback):
if type is None:
try:
self.gen.next()
except StopIteration:
return
else:
raise RuntimeError("generator didn't stop")
else:
try:
self.gen.throw(type, value, traceback)
raise RuntimeError("generator didn't stop after throw
()")
except StopIteration, exc:
# Suppress the exception *unless* it's the same
exception that
# was passed to throw(). This prevents a StopIteration
# raised inside the "with" statement from being
suppressed
return exc is not value
except:
# only re-raise if it's *not* the exception that was
# passed to throw(), because __exit__() must not raise
# an exception unless __exit__() itself failed. But
throw()
# has to raise the exception to signal propagation, so
this
# fixes the impedance mismatch between the throw()
protocol
# and the __exit__() protocol.
#
if sys.exc_info()[1] is not value:
raise
Conjecture: internal StopIteration exceptions are always the same
instance (PyExc_StopIteration) when propagated to python, invalidating
the return exc is not value
check.
|
|
msg57045 - (view) |
Author: Nick Coghlan (ncoghlan) |
Date: 2007-11-02 09:45 |
|
Close, but not quite. The problem is that the 'value' argument may be
None if instantiation of the exception hasn't been forced before
__exit__ gets called.
>>> class TestWith(object):
... def __enter__(self):
... pass
... def __exit__(self, exc_type, exc_value, exc_tb):
... print exc_type, exc_value, exc_tb
...
>>> from __future__ import with_statement
>>> with TestWith(): iter([]).next()
...
<type 'exceptions.StopIteration'> None <traceback object at 0xb76bed4c>
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
That 'None' in the middle there is the problem - contextmanager.__exit__
needs to be detecting that and instantiating the passed in exception if
it isn't already instantiated. Something like the following at the start
of the else clause should do the trick:
if value is None:
value = type()
|
|
msg57046 - (view) |
Author: Nick Coghlan (ncoghlan) |
Date: 2007-11-02 10:13 |
|
Fixed for 2.6 in rev 58766. I'm not sure if it will be possible to get
this into 2.5.2.
Leaving open against 2.5 until it is checked in on the maintenance branch.
|
|
msg57193 - (view) |
Author: Neal Norwitz (nnorwitz) |
Date: 2007-11-07 07:21 |
|
Nick, can you backport this and add a NEWS entry? Thanks.
|
|
msg57197 - (view) |
Author: Nick Coghlan (ncoghlan) |
Date: 2007-11-07 12:27 |
|
Done in rev 58901
|
|
| Date |
User |
Action |
Args |
| 2007-11-07 12:27:18 | ncoghlan | set | status: open -> closed resolution: accepted -> fixed messages:
+ msg57197 |
| 2007-11-07 07:21:41 | nnorwitz | set | nosy:
+ nnorwitz messages:
+ msg57193 |
| 2007-11-02 10:14:05 | ncoghlan | set | assignee: ncoghlan |
| 2007-11-02 10:13:39 | ncoghlan | set | resolution: accepted messages:
+ msg57046 components:
+ Library (Lib), - None versions:
+ Python 2.5 |
| 2007-11-02 09:45:18 | ncoghlan | set | nosy:
+ ncoghlan messages:
+ msg57045 |
| 2007-10-31 20:26:22 | klaas | set | nosy:
+ klaas messages:
+ msg57002 |
| 2007-04-22 06:56:50 | Rhamphoryncus | create | |
|