[Python-ideas] Catching of multiple exceptions without halting execution

Steven D'Aprano steve at pearwood.info
Tue Jun 25 01:41:37 CEST 2013


On Mon, Jun 24, 2013 at 08:13:12PM +0100, Samuel Littley wrote:
> I find I often have to run several tests on data input, making a list of
> which tests succeed, which fail, and somehow determine where to go after
> running all the tests. 

Sounds like you need a test framework, rather than new syntax. Have you 
looked at unittest, or outside of the standard library, nose?


> Obviously, the standard way of showing that a
> test as failed is to raise an exception, caught by try/except/finally,
> however this would only allow one test to be flagged as failing,
> requiring multiple runs to correct every fault that may exist.

Both doctest and unittest in the standrad library collect multiple 
exceptions in a single run. You could look at how they do it.


> I propose an alternative to try/except, as follows:

The bar to getting new syntax is very high. Even when a proposed feature 
is a good idea, it may not be introduced with new syntax. E.g. Enums, 
warnings. For example, there is *masses* of code out there that uses 
"validate" as a function name or other variable. Turning it into a 
keyword, as you suggest, would break people's code. There needs to be a 
really, really good reason to break people's code.


> validate:
>     // Code to run tests, raising exceptions if tests fail
> accept:
>     // Code to run if all tests pass (i.e. no exceptions)
> reject es:
>     // Code to handle each failed test
> except:
>     // Code to handle non-test related exceptions
> finally:
>     // Code to be always executed 
> 
> The difference to a normal try/except is a different type of exception,
> which, rather than halting execution, is added to the list `es`, which
> the reject block could then loop through to display error messages,
> require re-entry, etc. Standard exceptions could be raised and caught by
> the except block. Alternatively, the except block could not be a part of
> this, and instead all exceptions are caught in the reject block, which
> could then raise exceptions itself to be caught by a try/except/finally
> around the validate/accept/reject/finally


This can be trivially performed with existing syntax:

exceptions = []
try:  # instead of "validate"
    test_code()
except (ValueError, TypeError) as err:
    # "expected exceptions", do nothing
    pass
except KeyboardInterrupt:
    # Exceptions to allow through untouched
    raise
except (ZeroDivisionError, UnicodeEncodeError) as err:
    # instead of "reject"
    # Expected failures you don't wish to ignore
    exceptions.append(err)
except:
    # Catch all for everything else
    do_other_error()
else:  # instead of "accept"
    do_no_errors()
finally:
    cleanup()


So that's three new keywords we don't need, and ten thousand programs 
that won't be broken by this change :-)

Put the whole thing inside a loop, and you have the beginnings of a test 
framework.



-- 
Steven


More information about the Python-ideas mailing list