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.)
The try block lets you test a block of code for errors. The except block lets you handle the error. The else block lets you execute code when there is no error.
What is the reason for the try-except-else to exist? A try block allows you to handle an expected error. The except block should only catch exceptions you are prepared to handle. If you handle an unexpected error, your code may do the wrong thing and hide bugs.
To catch all exceptions, you have to create a Django middleware and create a process_exception method. Then you get control over what to do when encountering any kind of exception. I think this answered your question.
In Python programming, exception handling allows a programmer to enable flow control. And it has no. of built-in exceptions to catch errors in case your code breaks. Using try-except is the most common and natural way of handling unexpected errors along with many more exception handling constructs.
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)
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.)
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 .
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With