28

Is it possible to conditionally handle exceptions? I would like to be able to write a function so that the caller can decide who handles the exception.

Something like this:

def my_func(my_arg, handle_exceptions): try: do_something(my_arg) except Exception as e if handle_exceptions: print("my_func is handling the exception") 
0

9 Answers 9

33

You can re-raise the exception if you don't want to handle it:

def my_func(my_arg, handle_exceptions): try: do_something(my_arg) except Exception as e: if not handle_exceptions: # preserve prior stack trace raise # Or, if you dont care about the stack prior to this point #raise Exception(e) # similarly, you can just re-raise e. The stack trace will start here though. #raise e else: print("my_func is handling the exception") 

Another option is to create your own exceptions that subclass Exception (or a specific exception like urllib2.HTTPError) and then only catch/throw (raise) your custom exception:

class MyException(Exception): def __init__(self, message): self.message = message class MyExceptionTwo(Exception): def __init__(self, message): self.message = message def __repr__(self): return "Hi, I'm MyExceptionTwo. My error message is: %s" % self.message def something(): if not tuesday: raise MyException("Error: it's not Tuesday.") else: raise MyExceptionTwo("Error: it's Tuesday.") def my_func(my_arg): try: something() except MyException as e: print(e.message) # Will pass MyExceptionTwo up the call chain def my_other_func(): try: my_func(your_arg) except MyExceptionTwo as e: print(str(e)) # No need to catch MyException here since we know my_func() handles it # but we can hadle MyExceptionTwo here 
Sign up to request clarification or add additional context in comments.

2 Comments

I guess that works, but that will mess with the stack trace if it never gets caught, right?
@speedplane If you just use raise, the stack will stay in-tact. If you raise e or raise Exception(e) it will start the stack trace at the point where you raised it.
20

The question just doesn't have enough answers ;-)

Here's one more for the record books. Just create a dummy exception:

class NeverMatch(Exception): 'An exception class that is never raised by any code anywhere' 

Then, use a conditional expression to decide whether to match the real exception or the placeholder exception (which never gets raised):

try: do_something(my_arg) except (Exception if handle_exceptions else NeverMatch) as e: print('I am handling it') 

Comments

5

Option 1:

Conditionally handle errors using a conditional statement in the except block:

def my_func(my_arg, handle_exceptions): try: do_something(my_arg) except Exception: if handle_exceptions: print("my_func is handling the exception") raise 

Re-raising the exception by using the bare raise statement within the except block will not put another stack frame in the traceback.

Option 2:

Except statements can specify multiple exception types using a tuple. An empty tuple would mean don't handle any types. Ergo, you may specify the types to handle using a variable:

def my_func(my_arg, handle_exceptions): errors = () if handle_exceptions else (Exception,) try: do_something(my_arg) except errors: print("my_func is handling the exception") 

Comments

4

The exception type can be a variable.

def my_func(my_arg, handle_exceptions): if handle_exceptions: exc_type = Exception else: exc_type = None try: do_something(my_arg); except exc_type, e: print "my_func is handling the exception"; 

Obfuscated Python ("Pythonic"?) version:

def my_func(my_arg, handle_exceptions): try: do_something(my_arg); except (handle_exceptions and Exception), e: print "my_func is handling the exception"; 

Works without the parentheses, actually, but as long as we're being obfuscated let's not confuse people with little known rules like precedence for except statements.

1 Comment

TypeError: catching classes that do not inherit from BaseException is not allowed
4

You can use:

def my_func(my_arg, handle_exceptions): try: do_something(my_arg); except Exception as e: if not handle_exceptions: raise print("my_func is handling the exception") 

Comments

2

You have a two basic choices:

  1. Treat handle_exceptions as a boolean, and re-raise if False
  2. Treat handle_exceptions as the exceptions to handle

Along the boolean route you have two basic choices:

def my_func(my_arg, handle_exceptions): try: do_something(my_arg) except Exception as e: if not handle_exceptions: raise print("my_func is handling the exception") 

or

def my_func(my_arg, handle_exceptions): if handle_exceptions: exceptions = ValueError, IndexError # or whatever else: exceptions = NoExceptions # None in 2.x, or custom Exception class in 3.x try: do_something(my_arg) except exceptions as e: print("my_func is handling the exception") 

Along the 'treat handle_exceptions as the exceptions to handle' route you can do this:

class NoExceptions(Exception): 'Dummy exception, never raised' def my_func(my_arg, handle_exceptions=NoExceptions): try: do_something(my_arg) except handle_exceptions as e: print("my_func is handling the exception") 

and you would call it like so:

my_func(some_arg, ValueError) # to handle ValueErrors 

or

my_func(some_arg) # to not handle any exeptions 

This has the advantage/disadvantage of the caller being able to specify which exceptions are handled. If you do take this last route you might also want to specify an exception handler, perhaps something like this:

def my_func(my_arg, handle_exceptions=NoExceptions, handler=None): try: do_something(my_arg) except handle_exceptions as e: if handler is not None: handler(e) else: log_this_exception() 

1 Comment

You don't need a NoExceptions class, an empty tuple works as well.
2

Yes. I prefer positive conditions when it makes sense:

def my_func(my_arg, handle_exceptions): try: do_something(my_arg); except Exception: if handle_exceptions: print("my_func is handling the exception.") else: raise 

Comments

1

Your draft is actually quite close to working already:

def my_func(my_arg, handle_exceptions=True): try: do_something(my_arg) except (Exception if handle_exceptions else ()) as exc: print("my_func is handling the exception") 

Comments

1

A nice looking technique to improve on the other answers is to wrap the conditional exception handling logic in a context manager that can be reused, like this:

from contextlib import contextmanager @contextmanager def ignore_errors_if(exception_type, skip_condition): try: yield except exception_type as excn: if skip_condition: logging.debug("SKIPPING EXCEPTION %s" % excn) # etc... pass else: raise excn 

Then, in your code, you can say:

def some_function(): # ... with ignore_errors_if(Exception, should_ignore_errors): result = some_funciton_that_might_raise() # Deal with result, although know that it might not be set... 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.