[Python-ideas] Yield-From: GeneratorExit?
Jacob Holm
jh at improva.dk
Mon Mar 23 01:07:12 CET 2009
Hi Nick
Nick Coghlan wrote:
> Jacob Holm wrote:
>
>> If I understand Nick correctly, he would like to drop the "except
>> GeneratorExit: raise" part, and possibly change BaseException to
>> Exception. I don't like the idea of just dropping the "except
>> GeneratorExit: raise", as that brings us back in the situation where
>> shared subiterators are less useful. If we also change BaseException to
>> Exception, the only difference is that it will no longer be possible to
>> throw exceptions like SystemExit and KeyboardInterrupt that don't
>> inherit from Exception to a subiterator.
>>
>
> Note that as of 2.6, GeneratorExit doesn't inherit from Exception either
> - it now inherits directly from BaseException, just like the other two
> terminal exceptions:
>
I know this.
> All I'm saying is that if GeneratorExit doesn't get passed down then
> neither should SystemExit nor KeyboardInterrupt, while if the latter two
> *do* get passed down, then so should GeneratorExit.
>
I also know this, and I disagree. You are saying that because they have
the thing in commen that they do *not* inherit from Exception we should
treat them the same. This is like saying that anything that is not a
shade of green should be treated as red, completely ignoring the
possibility of other colors.
I like to see GeneratorExit handled as a special case by yield-from,
because:
1. It already has a special meaning in generators as the exception
raised in the generator when close is called.
2. It *enables* certain uses of yield-from that would require much
more more work to handle otherwise. I am thinking of the ability
to have multiple generators yield from the same iterator. Being
able to close one generator without closing the shared iterator
seems like a good thing.
3. While the GeneratorExit is not propagated directly, its expected
effect of finalizing the subiterator *is*. At least in CPython,
and assuming the subiterator does its finalization in a __del__
method, and that the generator holds the only reference. If the
subiterator is actually a generator, it will even look like the
GeneratorExit was propagated, due to the PEP 342 definition of close.
I don't like the idea of only throwing exceptions that inherit from
Exception to the subiterator, because it makes the following two
generators behave differently when thrown a non-Exception exception.
def generatorA():
try:
x = yield
except BaseException, e:
print type(e)
raise
def generatorB():
return (yield from generatorA())
The PEP is clearly intended to make them act identically. Quoting from
the PEP: "When the iterator is another generator, the effect is the same
as if the body of the subgenerator were inlined at the point of the
yield from expression".
Treating only GeneratorExit special allows them to behave exactly the
same (in CPython). If you only propagate exceptions that inherit from
Exception, you would have to write something like:
def generatorC():
g = generatorA()
while 1:
try:
return (yield from g)
except Exception:
# This exception comes from g, so just reraise
raise
except BaseException, e:
yield g.throw(e) # this exception was not propagated by yield-from, do it manually
to get the same effect.
I don't mind that the expansion as written in the PEP becomes very
slightly more complicated, as long as it makes the code using it
simpler to reason about.
- Jacob
More information about the Python-ideas
mailing list