21

A python newbie question: I need to do the following

try:
  do-something()
except error1:
  ...
except error2:
  ...
except:
  ...
#Here I need to do something if any exception of the above exception was thrown.

I can set a flag and do this. But is there a cleaner way of doing this?

amit
  • 10,612
  • 11
  • 61
  • 60
  • What's unclean about setting a flag? I mean, an `if` statement is generally considered inoffensive... – detly Nov 11 '10 at 06:11
  • 2
    +1 for a flag. otherwise you are going to confuse things with an extra level of nesting. Perhaps there is a better way to structure the flow of the code so you don't need to do this at all – John La Rooy Nov 11 '10 at 06:15
  • @detly What's unclean about a flag? It's easy to mishandle. If you can replace a flag-solution with one using structuring, this typically is an improvement. So, yes, if there is no other short way, then a flag might be the one correct way to do it, but it's kind of a last resort. – Alfe Jun 07 '17 at 09:22
  • Related: [How to determine if an exception was raised once you're in the finally block?](https://stackoverflow.com/q/49099637/674039) (note: I also settled on a flag as the best option) – wim Nov 05 '19 at 19:20

7 Answers7

9

Actually I don't like flags and consider them as the last resort solution. In this case I'd consider something like this:

def f():
  try:
    do_something()
  except E1:
    handle_E1()
  except E2:
    handle_E2()
  else:
    return
  do_stuff_to_be_done_in_case_any_exception_occurred()

Of course, this is only an option if you can return in the else: case.

Another option might be to rethrow the exception and recatch it for a more general handling of errors. This might even be the cleanest approach:

def f():
  try:  # general error handling
    try:  # specific error handling
      do_something()
    except E1:
      handle_E1()
      raise
    except E2:
      handle_E2()
      raise
  except (E1, E2):
    do_stuff_to_be_done_in_case_any_exception_occurred()
Alfe
  • 56,346
  • 20
  • 107
  • 159
  • 1
    Opinions differ ... this is a lot uglier than a simple flag, if you ask me. – wim Nov 05 '19 at 19:23
  • I like your first example & would most likely prefer over a flag where applicable. For the second, I have to agree with @wim – corvus Aug 07 '20 at 22:19
  • 1
    @corvus When I wrote that I don't like flags it wasn't purely personal preference. There are also objective reasons (which they teach in CS) to avoid them like statefulness and capsulation of code snippets. It's way easier to introduce bugs (errors you don't notice during development or compile time) in managing flag variables (especially if you have several of them) than if you stick to proper patterns instead. – Alfe Aug 11 '20 at 00:15
  • 1
    @Alfe your point is well taken. Thanks for the perspective. – corvus Aug 11 '20 at 06:17
  • Agree about the flags, this is a nice solution if the `do_stuff_...` is something complex, otherwise I think it is more readable to just write it in each `except` clause. – vlizana Aug 18 '20 at 19:02
  • @vlizana _That_ is something I would barely recommend. This is called code-doubling and is the root of all evil in industrial code development. If it is more complex than `return` or `break`, I would not do that. Why? Because if this repeated code needs a change, the maintainer might apply the change in one of the occurrences but not in all. The code is close together, so the doubling is obvious, you reply? Well, after the next maintainer added more code to the existing lines, the occurrences might be further apart, making this less obvious. No, I don't recommend code doubling. Not at all. – Alfe Aug 18 '20 at 23:42
6

You can do this with a nested try. The except block of the outer try should catch all exceptions. Its body is another try that immediately re-raises the exception. The except blocks of the inner try actually handle the individual exceptions. You can use the finally block in the inner try to do what you want: run something after any exception, but only after an exception.

Here is a little interactive example (modeled on Applesoft BASIC for nostalgia purposes).

try:
    input("]")  # for Python 3: eval(input("]"))
except:
    try:
       #Here do something if any exception was thrown.
       raise
    except SyntaxError:
       print "?SYNTAX",
    except ValueError:
       print "?ILLEGAL QUANTITY",
    # additional handlers here
    except:
       print "?UNKNOWN",
    finally:
       print "ERROR"
Chris
  • 5,788
  • 4
  • 29
  • 40
kindall
  • 178,883
  • 35
  • 278
  • 309
  • I am unsure if I am misunderstanding your code example, but finally is executed even if no exception occurred. – Natan Mar 14 '22 at 13:51
  • 1
    the `finally` clause is attached to the inner `try`, which is reached when an exception is caught and re-raised by the outer `try`. so we know definitively that an exception has been raised, otherwise we'd never be in the inner `try` block. The inner `except` clauses handle all possible exceptions (note bare `except:`) and the `finally` clause is of course always executed. – kindall Sep 06 '22 at 19:37
5

I just tried a couple different idea's out and it looks like a flag is your best bet.

  • else suite is only called if there is no exception
  • finally will always be called
David
  • 17,673
  • 10
  • 68
  • 97
2

This is the best way I can think of. Looks like a code smell though

try:
  exception_flag = True
  do-something()
  exception_flag = False
except error1:
  ...
except error2:
  ...
except:
  ...
finally:
  if exception_flag:
    ...

You wouldn't need the finally if you are not reraising exceptions in the handler

John La Rooy
  • 295,403
  • 53
  • 369
  • 502
1

From the docs: http://docs.python.org/reference/compound_stmts.html#finally

If finally is present, it specifies a ‘cleanup’ handler. The try clause is executed, including any except and else clauses. If an exception occurs in any of the clauses and is not handled, the exception is temporarily saved. The finally clause is executed. If there is a saved exception, it is re-raised at the end of the finally clause. If the finally clause raises another exception or executes a return or break statement, the saved exception is lost. The exception information is not available to the program during execution of the finally clause.

relima
  • 3,462
  • 5
  • 34
  • 53
  • 8
    The body of a `finally` clause will execute even if there is **no** exception. I don't think this is what the OP wants. – detly Nov 11 '10 at 05:25
  • yes. finally will execute always. I want something like opposite of else. – amit Nov 11 '10 at 05:48
0

It's not clear if you need to handle differently error1, error2 etc. If not, then the following code will do the trick:

try:
    do_something()
except (error1, error2, error3), exception_variable:
    handle_any_of_these_exceptions()

if you do need to handle different errors differently as well as having the common code, then in the except block, you can have code of the following type:

    if isinstance(exception_variable, error1):
       do_things_specific_to_error1()
DaveP
  • 6,952
  • 1
  • 24
  • 37
  • i need to do different things. using isinstance looks like unpythonic, but definitely an option. – amit Nov 11 '10 at 05:59
  • @amit I generally agree that `isinstance` is a bad thing but `except` implicitly uses it by only exceptions based on their type to begin with. I think it's appropriate here. – aaronasterling Nov 11 '10 at 06:07
0

I think this is a more neat solution by using return in the try clause. If everything works, we will return the value we got in bar(). If we get an exception, we will run the next code, in this case raising another exception. Demonstrated with a randint.

import random

def foo():
    try:
        return bar()
    except IndexError:
        print('Error.')
    raise KeyError('Error msg')
    
def bar():
    res = random.randint(0, 2)
    if res == 0:
        raise IndexError
    return res

res = foo()
print(res)
ErikXIII
  • 557
  • 2
  • 12