[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