From: Gregory Ewing on
exarkun(a)twistedmatrix.com wrote:

> Which is unfortunate, because it's not that hard to get StopIteration
> without explicitly raising it yourself and this behavior makes it
> difficult to debug such situations.

It might not be hard if you set out to do it, but in
my experience it's pretty rare to end up getting a
StopIteration raised accidentally in an unexpected
place.

--
Greg
From: Albert van der Horst on
In article <mailman.1818.1260694428.2873.python-list(a)python.org>,
Gabriel Genellina <gagsl-py2(a)yahoo.com.ar> wrote:
<SNIP>
>
>Despite a promise in PEP 289, generator expressions semantics isn't
>explained in detail in the language reference. I can't tell if the
>difference is intentional, accidental, undocumented behavior, an
>implementation accident, a bug, or what...

Philosophically speaking ...
An important feature that is not documented is a severe defect.
(important maps to severe).
Before it is documented, there can be no discrepancy between
specification and implementation so other defects are formally
not present in relation to this situation.

>--
>Gabriel Genellina
>

Groetjes Albert.


--
--
Albert van der Horst, UTRECHT,THE NETHERLANDS
Economic growth -- being exponential -- ultimately falters.
albert(a)spe&ar&c.xs4all.nl &=n http://home.hccnet.nl/a.w.m.van.der.horst

From: Gregory Ewing on
Albert van der Horst wrote:

> An important feature that is not documented is a severe defect.

This isn't something that I would expect to find documented
under the heading of generator expressions, because it doesn't
have anything to do with them. It's an interaction between
the iterator protocol and the list() constructor. Any other
iterable that leaked a StopIteration exception would cause
the same effect.

--
Greg
From: Tom Machinski on
Thanks for the comment and discussion guys.

Bottom line, I'm going to have to remove this pattern from my code:

foo = (foo for foo in foos if foo.bar).next()

I used to have that a lot in cases where not finding at least one
valid foo is an actual fatal error. But using StopIteration to signal
a fatal condition becomes a bug when interacting with list() as shown
in the original post.

It would be nice if there was a builtin for "get the first element in
a genexp, or raise an exception (which isn't StopIteration)", sort of
like:

from itertools import islice

def first_or_raise(genexp):
L = list(islice(genexp, 1))
if not L:
raise RuntimeError('no elements found')
return L[0]

I also think Jean-Paul's had a good point about how the problems in
the list/genexp interaction could be addressed.

Thank you,

-- Tom
From: Steven D'Aprano on
On Wed, 30 Dec 2009 15:18:11 -0800, Tom Machinski wrote:

> Thanks for the comment and discussion guys.
>
> Bottom line, I'm going to have to remove this pattern from my code:
>
> foo = (foo for foo in foos if foo.bar).next()

I don't see why. What's wrong with it? Unless you embed it in a call to
list, or similar, it will explicitly raise StopIteration as expected.


> I used to have that a lot in cases where not finding at least one valid
> foo is an actual fatal error.

What's wrong with the obvious solution?

if not any(foo for foo in foos if foo.bar):
raise ValueError('need at least one valid foo')



> But using StopIteration to signal a fatal
> condition becomes a bug when interacting with list() as shown in the
> original post.

You shouldn't use StopIteration to signal fatal conditions, because
that's not what it is for. It's acceptable to catch it when *directly*
calling next, but otherwise you should expect that StopIteration will be
caught and suppressed by just about anything.


> It would be nice if there was a builtin for "get the first element in a
> genexp, or raise an exception (which isn't StopIteration)",

Not everything needs to be a built-in.

def get_first_or_fail(iterable_or_sequence):
it = iter(iterable_or_sequence)
try:
return it.next() # use next(it) in Python 3
except StopIteration:
raise ValueError('empty iterable')

This is perfectly usable as a helper function, or it's short enough to be
used in-line if you prefer.


--
Steven