generators and exceptions

Clark C. Evans cce at clarkevans.com
Mon Mar 17 08:27:55 CET 2003


Thank you for responding Tim, as you expected my question is less
about what generator behavior is, as it is about what generator
behavior with regard to exceptions could be.

On Sat, Mar 15, 2003 at 11:20:46PM -0500, Tim Peters wrote:
| >     from __future__ import generators
| >     class MyExc(Exception): pass
| >     def mygen(val):
| >         while val > 0:
| >            if val % 2: raise MyExc
| >            yield val

               val -= 1

| Read the PEP and your expectations will change <wink>.  The code exactly as
| you gave should obviously produce only 4 (val is set to -1 and so the loop
| exits).  With the suggested guess at what was intended, it should still
| produce only 4, but due to raising an exception within the generator when
| val is 3 (instead of falling off the end because val is -1).

Right.  I did, but this still didn't stop my expectations.  I was wishing 
that I could resume the generator after the exception was thrown.

| > I expect 4, 2 beacuse an equivalent iterator would produce 4, 2.
| 
| Unclear what that means ("equivalent iterator") -- if you raise an exception
| within *any* function, and don't catch it within that function, the
| function's useful life ends.  Generator functions aren't an exception to
| this, and neither are functions implementing any other kind of iterator.

To me, generators as a great syntax for writing complicated 
iterators, the 'equivalent iterator' would be something like...

   class myiter:
       def __iter__(self): return self
       def __init__(self, val):
           self.val = val
       def next(self):
           val = self.val
           if val < 1:  raise StopIteration
           self.val = val - 1
           if val % 2: raise MyExc
           return val
           
What's interesting here is that I can raise an exception from
within the iterator and it doesn't 'dismiss' the iterator; that 
is, I can call next() again and it may produce a second time.

| > It seems that generators die on the first exception... is there a way
| > around this?
| 
| No.
|
| > I'm asking beacuse I'm using generators in a non-blocking database
| > system, where I want to raise WouldBlock in my generator, but still be
| > able to call the generator at a later time when it may not block...
| 
| Resuming a generator that has raised a (any) exception results in
| StopIteration getting raised immediately, so, no, you can't do that
| directly.

Is there a serious technical reason for this behavior?  Could it
have worked some other way, i.e., why not let a generator survive 
an exception, as the above iterator does so well.  A generator is 
fundamentally different from a function; I tend to picture it as
great syntax sugar for making iterators.  However, and unfortunately,
a great many of my iterators raise "recoverable exceptions".

Best,

Clark





More information about the Python-list mailing list