I often have a generator which iterates over some collection, processes the elements and yields the results. The processing of the elements might raise exceptions. I'd like to handle such an exception happening in such a generator in the caller of the generator, without terminating the generator. After all, just the processing failed, not the iterating, so the generator could well continue with the next iterated element. Example displaying the situation:
def generator():
for element in collection:
yield process(element) # might raise Exception
def caller():
for product in generator():
doSomethingWith(product)
I'd like to handle the exception thrown in process()
in caller()
. Unfortunately, the exception in process()
will happen in generator()
and thus terminate the generator (if we do not do something more complicated here). So a naive approach will fail:
def generator():
for element in collection:
yield process(element) # might raise Exception
def caller():
for product in generator():
try: # wrong place for try, as the loop will already fail
doSomethingWith(product)
except Exception as problem:
handleProblem(problem)
My working but flawed approach currently is to hand out the exception as yielded value:
def generator():
for element in collection:
try:
yield process(element) # might raise Exception
except Exception as problem:
yield problem
def caller():
for yielded in generator():
if isinstance(yielded, Exception):
problem = yielded
handleProblem(problem)
else:
product = yielded
doSomethingWith(product)
This works but has several drawbacks like
- It depends on the fact that the product and the exception can be distinguished (won't work on generators which normally yield exceptions). We could use wrapping to solve that of course.
- It needs that ugly check-pattern right after the receiving of the generated value (
if isinstance(...)
). - It does not use the normal Python exception handling, so things like re-raising the exceptions might pose a problem (didn't check on that thoroughly).
Do you have any better approach on this? I'd prefer to use some try
/except
pattern in the caller()
for handling the exception instead of using if
as it provides more flexibility like catching only specific exceptions (and others not).