mailRe: r2533 - in /1.2: errors.py prompt/interpreter.py relax


Others Months | Index by Date | Thread Index
>>   [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Header


Content

Posted by Edward d'Auvergne on August 11, 2006 - 19:02:
I've been thinking some more about this, and wonder if there is a better
way of doing it. The problem is that Python exceptions are generally
pretty minimalist constructions, and it seems hard to avoid very crude
hacks when getting them to do anything more sophisticated.

I know, you can get some very weird behaviour if you try any fancy modifications.

this is historical - in early versions of Python, there was no special
Exception class, and raise could act on any object. This is still the
case, though it's deprecated, so there is nothing the Exception itself
can do to change the way raise handles it. The way around it is to do
the appropriate handling tasks in the program code itself. The sort of
thing I'm thinking about would have:

class BaseError(Exception):
    # -1 => never, 0 => if Debug, 1 => always
    save_state = 0
    def __str__(self):
        return ("RelaxError: " + self.text + "\n")

class RelaxFault(BaseError):
    save_state = 1
    def __init__(self):
        ...

Then in the main program script, around line 151 (in the if mode ==
'prompt' or mode == 'script' block) we would have:

try:
    self.interpreter.run()
except BaseError:
    err = sys.exc_info()
    exception = err[1]
    if exception.save_state == 1
        state.save()
    elif exception.save_state == 0
        if Debug:
            self.interpreter._State.save()
    del err, exception
    raise

That is a good setup!!! I just wonder if this should be at the level of the interpreter/scripting or higher level. I suppose for other interfaces you may want different behaviours for the RelaxErrors and RelaxWarnings.

The logic here is that BaseError simply defines a flag indicating the
default behaviour, and subclasses can override this as appropriate. Then
we use the more normal exception handling machinery to ensure that the
behaviour occurs, rather than coding that into the exception itself.

The flexibility is perfect for say the (possible future) MPI interface, a GUI, a web interface, or as you say below the test-suite.

This gives us even more flexibility, because we might want (around line
180):

# Run the tests.
elif mode == 'test suite':
    try:
        Test_suite(self)
    except BaseError:
        err = sys.exc_info()
        exception = err[1]
        if exception.save_state >= 0
            self.interpreter._State.save()
        del err, exception
        raise

Here exceptions will cause a state.save(), irrespective of Debug status,
because we will always want to know about about errors during testing.

state.save() only pickles the contents of 'self.relax.data' and so for many tests may not be very useful. It would be best if the user just sent the traceback first as the saved state is relatively large (compared to the text of the traceback) and in most cases won't be necessary. If necessary we could then tell the user to run the tests with --debug (unless someone thinks otherwise) and then tell them to attach the saved state to the bug report (this should save space on the bug tracker or mailing lists).

The other advantage of this approach is that the state.save() only
happens if the exception has propagated all the way to the top of the
stack. With the save_state() function in __init__(), the state is saved
when the exception class is instantiated ie. when it is raised, even if
it is subsequently caught and dealt with.

One of my thoughts from a few years back was to completely redo the entire Error system into something a little easier to understand. For example to print a RelaxError you type something like:

if bad:
   RelaxBadError.raise('This is a bad error')

if vect_len == 0:
   RelaxZeroVectorWarning.raise(res = 1)

The RelaxBadError is a class, except it isn't subclassed from
'Exception'.  It has a simple 'raise()' function which takes the
string, formats it, etc (there maybe a better name for this function).
'raise()' could also call a special error function which say takes
the formatted string, prints a traceback to stderr, prints
'RelaxError: ' followed by the formatted string to stderr, then
executes 'sys.exit()'.

A similar system could be used for the RelaxWarnings except the
traceback isn't printed and 'sys.exit()' is not called.  These very
simple objects remove the need for using the 'raise' statement and
attempting to catch it with 'try:' statements.  I don't think that
this change will be too much of a challenge.  It will also allow the
upgrading of warnings to errors (by calling the special error function
rather than the special warning function) using the '--pedantic' flag
and the downgrading of errors to warnings using the '--ignore-errors'
flag.  It will make the fine control you suggest in you post much
easier and will make the entire error/warning system much cleaner.  Is
there any error behaviour I have missed?  It also allows a cleaner
implementation of the state.save() function and anything else we may
want to add in the future for the other UIs (GUI, web, MPI,
test-suite, threading, etc).

Edward



Related Messages


Powered by MHonArc, Updated Fri Aug 11 20:20:29 2006