Martin C. Martin
Martin C. Martin

Reputation: 3682

Python: catch any exception and put it in a variable

To figure out what it would take to avoid some recursion, I need to catch any exception (edit: Not just ones derived from Exception, but all exceptions, including KeyboardInterrupt and user exceptions), put it in a variable, and later re-raise it outside the catch block. Essentially, I'm trying to roll my own finally block. Is this possible?

The actual problem is to call a number of cleanup functions, and if any of them fail, all the others should also be called, then the exception for the one that failed should still propagate. Here's my current solution, it takes a list of Popen objects:

def cleanupProcs(procs):
    if not procs:
        return

    proc = procs.pop(0)
    try:
        proc.terminate()
        proc.wait()
    finally:
        cleanupProcs(procs)

Is there an iterative way to do this? A more elegant way? A more Pythonic way?

Upvotes: 15

Views: 19898

Answers (7)

Tobias Kienzler
Tobias Kienzler

Reputation: 27423

If you want the stacktrace included:

try:
    # something
except:
    the_type, the_value, the_traceback = sys.exc_info()

later

raise the_type, the_value, the_traceback

(Related to this answer)

See also here for Python 2.7.

Upvotes: 15

Henry Keiter
Henry Keiter

Reputation: 17188

I'd probably use BaseException to catch anything that gets thrown, and iterate through all your cleanup functions (instead of using recursion). Then append any exceptions to a list, to deal with (re-raise, log, etc) as appropriate when you finish the cleanup.

def runCleanup(procs):
    exceptions = []
    for proc in procs:
        try:
            proc.terminate()
            proc.wait()
        except BaseException as e:
            exceptions.append(e) # Use sys.exc_info() for more detail

    return exceptions # To be handled or re-raised as needed

Upvotes: 3

JadedTuna
JadedTuna

Reputation: 1833

Thats easy:

>>> try:
...     #something
... except BaseException, e: # OK. Using BaseException instead of Exception
...     pass
... 
>>> 
>>> raise e
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
>>> 

Upvotes: 0

Wayne Werner
Wayne Werner

Reputation: 51847

In this case, I think I would argue that you may not be doing it right.

To me, the point of an exception is to signal a bum bum baaaaa exceptional circumstance. And when you are writing code that may fire an exception you have two responsible options - catch it and do something with it (like recover), or completely ignore it.

From what you say in your post, you don't really care that an exception happened. It shouldn't halt your program, and program flow should continue as normal. You do however want to know that an exception happened. And this is where the logging module comes in:

import logging
log = logging

def get_some_cheese():
    raise ValueError("Sorry, we're right out.")

try:
    get_some_cheese()
except:
    log.exception("What a waste of life")

When you log an exception it automagically adds stack trace information for you. After you config your logging a bit, you can get it setup to do all sorts of whatever things you want to do - send an email, write to a file or stdout/err, whatever. But then you'll get informed that an exception occured, but you can also simply recover from the error and continue on your merry way cleaning up whatever it is you need to clean up.

Upvotes: 4

Corley Brigman
Corley Brigman

Reputation: 12401

you can use:

procexceptions = []

except Exception, e:
    procexceptions.append(e)

and then later (after the loop for terminating processes) you can

raise procexceptions[0]

etc.

Upvotes: 1

Chris Johnson
Chris Johnson

Reputation: 21976

Like just about everything else in Python, exceptions are objects and therefore can be bound to names and operated upon. Here's a short example showing how to grab an exception and use it later:

>>> def to_int(x):
...     try:
...         return int(x)
...     except Exception, e:
...         print 'in exception block:', e
...     print 'after exception block:', e

>>> to_int('12')
12
>>> to_int('abc')
in exception block: invalid literal for int() with base 10: 'abc'
after exception block: invalid literal for int() with base 10: 'abc'

Upvotes: 0

viraptor
viraptor

Reputation: 34185

Openstack does something very similar for a different reason. Have a look at https://github.com/openstack/nova/blob/master/nova/openstack/common/excutils.py#L30 (function save_and_reraise_exception). In their case it works like a resource manager.

Upvotes: 0

Related Questions