Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Keeping try block small when catching exceptions in generator

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.)

like image 735
Alfe Avatar asked Nov 23 '12 13:11

Alfe


People also ask

When would you use a try except block?

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.

Is try except good practice?

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.

How do you handle exceptions in stackoverflow Python?

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.

Should I use exceptions in Python?

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.


3 Answers

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)
like image 94
ecatmur Avatar answered Oct 06 '22 01:10

ecatmur


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.)

like image 38
Alfe Avatar answered Oct 06 '22 02:10

Alfe


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 .
like image 31
warvariuc Avatar answered Oct 06 '22 03:10

warvariuc