speedplane
speedplane

Reputation: 16141

Python - Conditionally Catching Exceptions

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

Upvotes: 27

Views: 18378

Answers (8)

wim
wim

Reputation: 363566

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

Upvotes: 4

slacy
slacy

Reputation: 11783

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

Upvotes: 1

chown
chown

Reputation: 52798

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, 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, e:
        print e.message
    # Will pass MyExceptionTwo up the call chain

def my_other_func():
    try:
        my_func(your_arg)
    except MyExceptionTwo, e:
        print str(e)
    # No need to catch MyException here since we know my_func() handles it
    # but we can hadle MyExceptionTwo here

Upvotes: 33

Gringo Suave
Gringo Suave

Reputation: 31958

Yes. I prefer positive conditions when it makes sense:

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

Upvotes: 2

Ethan Furman
Ethan Furman

Reputation: 69288

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, 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, 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, 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, e:
        if handler is not None:
            handler(e)
        else:
            log_this_exception()

Upvotes: 2

Raymond Hettinger
Raymond Hettinger

Reputation: 226754

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'

Upvotes: 20

morningstar
morningstar

Reputation: 9162

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.

Upvotes: 4

millimoose
millimoose

Reputation: 39990

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";

Upvotes: 5

Related Questions