This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: contextmanager eats StopIteration
Type: Stage:
Components: Library (Lib) Versions: Python 2.5
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: ncoghlan Nosy List: Rhamphoryncus, klaas, ncoghlan, nnorwitz
Priority: normal Keywords:

Created on 2007-04-22 06:56 by Rhamphoryncus, last changed 2022-04-11 14:56 by admin. This issue is now closed.

Files
File name Uploaded Description Edit
contextmanager-testcase.py Rhamphoryncus, 2007-04-22 06:56 Test case
Messages (6)
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) * (Python committer) 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) * (Python committer) 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) * (Python committer) Date: 2007-11-07 07:21
Nick, can you backport this and add a NEWS entry?  Thanks.
msg57197 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2007-11-07 12:27
Done in rev 58901
History
Date User Action Args
2022-04-11 14:56:23adminsetgithub: 44880
2007-11-07 12:27:18ncoghlansetstatus: open -> closed
resolution: accepted -> fixed
messages: + msg57197
2007-11-07 07:21:41nnorwitzsetnosy: + nnorwitz
messages: + msg57193
2007-11-02 10:14:05ncoghlansetassignee: ncoghlan
2007-11-02 10:13:39ncoghlansetresolution: accepted
messages: + msg57046
components: + Library (Lib), - None
versions: + Python 2.5
2007-11-02 09:45:18ncoghlansetnosy: + ncoghlan
messages: + msg57045
2007-10-31 20:26:22klaassetnosy: + klaas
messages: + msg57002
2007-04-22 06:56:50Rhamphoryncuscreate