Reputation: 59426
How can I keep the try block as small as possible when I have to catch an exception which can occur in a generator?
A typical situation looks like this:
for i in g():
process(i)
If g()
can raise an exception I need to catch, the first approach is this:
try:
for i in g():
process(i)
except SomeException as e:
pass # handle exception ...
But this will also catch the SomeException
if it occurs in process(i)
(this is what I do not want).
Is there a standard approach to handle this situation? Some kind of pattern?
What I am looking for would be something like this:
try:
for i in g():
except SomeException as e:
pass # handle exception ...
process(i)
(But this is syntactic nonsense of course.)
Upvotes: 5
Views: 387
Reputation: 59426
The straight-forward approach for this seems to be unwrap the for
construct (which makes it impossible to catch exceptions just in the generator, due to its syntax) into its components.
gen = g()
while True:
try:
i = gen.next()
except StopIteration:
break
process(i)
Now we can just add our expected exception to the try
block:
gen = g()
while True:
try:
i = gen.next()
except StopIteration:
break
except SomeException as e:
pass # handle exception ...
break
process(i)
Does that (besides it being ugly as hell) have disadvantages? And more: Is there a nicer solution?
(I won't accept my own answer because it being ugly, but maybe others like and upvote it.)
Upvotes: 2
Reputation: 157344
You could convert exceptions occurring in the inner block:
class InnerException(Exception):
pass
try:
for i in g():
try:
process(i)
except Exception as ex:
raise InnerException(ex)
except InnerException as ex:
raise ex.args[0]
except SomeException as e:
pass # handle exception ...
Another option is to write a local generator that wraps g
:
def safe_g():
try:
for i in g():
yield i
except SomeException as e:
pass # handle exception ...
for i in safe_g():
process(i)
Upvotes: 3
Reputation: 2175
I don't know, if it works. You could evaluate g()
into a list. I can't test it, because I don't have an iterator that throws an exception at hand.
try:
glist = list(g())
except SomeException as e:
pass # handle exception ...
for i in glist:
process(i)
Upvotes: 0
Reputation: 59594
In your generator raise a different kind of exception, that you will be able to distinguish.
class GeneratorError(Exception):
pass
def g():
try:
yield <smth>
except:
raise GeneratorError
try:
for i in g():
process(i)
except GeneratorError:
pass # handle generator error
except SomeException as e:
pass # handle exception .
Upvotes: 1