idiomatic way to collect and report multiple exceptions?

Chris Rebert crebert at ucsd.edu
Fri May 7 00:56:10 EDT 2010


On Thu, May 6, 2010 at 8:50 PM, Ben Cohen <ncohen at ucsd.edu> wrote:
> Is there a pythonic way to collect and display multiple exceptions at the same time?
>
> For example let's say you're trying to validate the elements of a list and you'd like to validate as many of the elements as possible in one run and still report exception's raised while validating a failed element.
>
> eg -- I'd like to do something like this:
>
> errors = []
> for item in data:
>        try:
>                process(item)
>        except ValidationError as e:
>                errors.append(e)
> raise MultipleValidationErrors(*errors)
>
> where if the raised MultipleValidationErrors exception goes uncaught the interpreter will print a nice traceback that includes the tracebacks of each raised ValidationError.  But I don't know how MultipleValidationErrors should be written ...

import sys, traceback

def _traceback_for(exc_info):
    return ''.join(traceback.format_exception(*exc_info))

# StandardError can obviously be replaced with
# whatever exception superclass you want.
class MultipleValidationErrors(StandardError):
    def __init__(self, errors=None):
        self.errors = errors or []

    def __str__(self):
        tracebacks = "\n\n".join(_traceback_for(exc_info) for exc_info in \
            self.errors)
        parts=("See the following exception tracebacks:", "="*78, tracebacks)
        msg = '\n'.join(parts)
        return msg

    def capture_current_exception(self):
        self.errors.append(sys.exc_info())

    def do_raise(self):
        """Raises itself if it contains any errors"""
        if self.errors:
            raise self


#Example usage:
multiple_err = MultipleValidationErrors()
for c in "hello":
    try:
        int(c) # obviously fails
    except ValueError: # whatever error type you care about
        multiple_err.capture_current_exception()
multiple_err.do_raise()


Output from example:
Traceback (most recent call last):
  File "tmp.py", line 35, in <module>
    multiple_err.do_raise()
  File "tmp.py", line 25, in do_raise
    raise self
__main__.MultipleValidationErrors: See the following exception tracebacks:
==============================================================================
Traceback (most recent call last):
  File "tmp.py", line 32, in <module>
    int(c) # obviously fails
ValueError: invalid literal for int() with base 10: 'h'


Traceback (most recent call last):
  File "tmp.py", line 32, in <module>
    int(c) # obviously fails
ValueError: invalid literal for int() with base 10: 'e'


Traceback (most recent call last):
  File "tmp.py", line 32, in <module>
    int(c) # obviously fails
ValueError: invalid literal for int() with base 10: 'l'


Traceback (most recent call last):
  File "tmp.py", line 32, in <module>
    int(c) # obviously fails
ValueError: invalid literal for int() with base 10: 'l'


Traceback (most recent call last):
  File "tmp.py", line 32, in <module>
    int(c) # obviously fails
ValueError: invalid literal for int() with base 10: 'o'


Cheers,
Chris
--
Go Tritons!
http://blog.rebertia.com



More information about the Python-list mailing list