Exceptions for the whole class

Question:

I’m writing a program in Python, and nearly every method im my class is written like this:

def someMethod(self):
    try:
       #...
    except someException:
       #in case of exception, do something here

       #e.g display a dialog box to inform the user 
       #that he has done something wrong

As the class grows, it is a little bit annoying to write the same try-except block over and over. Is it possible to create some sort of ‘global’ exception for the whole class? What’s the recommended way in Python to deal with this?

Asked By: user2494129

||

Answers:

I can think of two options:

  1. Write a decorator that can wrap each method in the try block.
  2. Write a “dispatcher” method that calls the appropriate method inside a try block, then call that method instead of the individual ones. That is, instead of calling obj.someMethod(), obj.otherMethod, you call obj.dispatch('someMethod') or obj.dispatch('otherMethod'), where dispatch is a wrapper that contains the try block.

Your approach seems like a bit of a strange design, though. It might make more sense to have the dialog-box stuff in some other part of the code, some higher-level event loop that catches errors and displays messages about them.

Answered By: BrenBarn

Write one or more exception handler functions that, given a function and the exception raised in it, does what you want to do (e.g. displays an alert). If you need more than one, write them.

def message(func, e):
    print "Exception", type(e).__name__, "in", func.__name__
    print str(e)

Now write a decorator that applies a given handler to a called function:

import functools

def handle_with(handler, *exceptions):
    try:
        handler, cleanup = handler
    except TypeError:
        cleanup = lambda f, e: None
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            try:
                return func(*args, **kwargs)
            except exceptions or Exception as e:
                return handler(func, e)
            else:
                e = None
            finally:
                cleanup(func, e)
        return wrapper
    return decorator

This only captures the exceptions you specify. If you don’t specify any, Exception is caught. Additionally, the first argument can be a tuple (or other sequence) of two handler functions; the second handler, if given, is called in a finally clause. The value returned from the primary handler is returned as the value of the function call.

Now, given the above, you can write:

@handle_with(message, TypeError, ValueError)
def add(x, y):
    return x + y

You could also do this with a context manager:

from contextlib import contextmanager 

@contextmanager
def handler(handler, *exceptions):
    try:
        handler, cleanup = handler
    except TypeError:
        cleanup = lambda e: None
    try:
        yield
    except exceptions or Exception as e:
        handler(e)
    else:
        e = None
    finally:
        cleanup(e)

Now you can write:

def message(e):
    print "Exception", type(e).__name__
    print str(e)

 def add(x, y):
     with handler(message, TypeError, ValueError):
         return x + y

Note that the context manager doesn’t know what function it’s in (you can find this out, sorta, using inspect, though this is "magic" so I didn’t do it) so it gives you a little less useful information. Also, the context manager doesn’t give you the opportunity to return anything in your handler.

Answered By: kindall
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.