mailRe: RelaxWarnings and RelaxErrors.


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

Header


Content

Posted by Edward d'Auvergne on August 16, 2006 - 09:55:
> > This isn't quite what I mean. Consider some function in relax that is
> > called in many different contexts. Imagine that it is prone to a
> > particular type of error, which we signal by a RelaxError. Now it is
> > quite possible that the correct response to that error depends on the
> > context in which we are calling this function. eg. in some cases the
> > error might signal a fatal error in the data that the user must fix,
> > while in other contexts it might be something simple that can be fixed
> > silently and re-tried. If the error is an exception, in the first
> > context, the code which calls the function will simply do so without
> > catching the error so it is propagated to the user. In the second case,
> > the code will call the function in a try statement, with the fix in the
> > except clause. On the other hand, if relax errors are just ordinary
> > functions that are called when an error arises, there is no easy way of
> > dealing with these two cases - the error has the same effect independent
> > of the context from which it is called. It is this lack of flexibility
> > that concerns me when we talk about not using exceptions for our error
> > handling.
>
> This could be rectified by a flag, so the following examples will have
> very similar functionality to the try-raise-except paradigm but
> without the breaking of inner loops and functions:
>
> RelaxBadError('Hello', exit=1)   # Print the error to stderr and quit.
> RelaxBadError('Hello', exit=0)   # Print the error to stderr but
> continue operating as if nothing happened.

This allows the function in which the error occurs some flexibility in
error handling. My point above was that any function is ignorant of the
context in which it was called, so may well be ignorant of the
appropriate way of dealing with its own errors, because this may well be
dependent on that context.

Good point, flags set by the code in which the error occurs isn't such a great idea. That code shouldn't care.

There are two possible ways of dealing with
this that I can see. One involves the Python Exception system, and the
other involves a complex series of error codes passed back and forwards
between functions. The fact that we don't have to resort to this latter
option is one of the reasons that Python code is so clear, simple and
easily maintained and debugged.

The other point is that we should never raise a RelaxError and then
continue as if nothing happened - the whole point of an error is to say
something unexpected has happened and must be dealt with in some way. If
the 'error' doesn't need dealing with (ie. if we can continue as if
nothing happened), then it is no error at all (possibly its worth a
warning, if it reflects something the user might want to be aware of, or
a debug statement if it might cause real errors later on).

I agree. I was just concerned about responses to previous posts where it was suggested that relax should ignore errors, continue operations, and then print (possibly for the second time) the error message. I'm happy to leave errors as errors.

> And
> there are only two real behaviours I see as necessary for an error
> system:  printing a message to stderr and terminating execution.
> Possibly returning an error code to the shell would be useful as well.

Here I disagree. Exception handling systems must be able to alter
program flow in appropriate ways so that the event causing the exception
can be dealt with. When we are dealing only with fatal user errors, then
printing a message and termination is quite adequate, but we also need
to be able to deal well with errors that can be silently fixed, and this
requires flow control.

Again this is a good point. If the Qt GUI is one day added to relax the behaviour when an exception occurs should be very different (and possibly specific to different parts of the interface).

> The raise statement breaks the inner loops and function calls.  It's
> just not possible to continue the script execution.  This is because
> within 'prompt/interpreter.py' the 'try' function attempts to run the
> 'execfile()' function which executes the script.  A raise statement
> within relax kills the execfile() function.  If the raise statement
> occurs 1/3 of the way through the script - it's not possible to catch
> this and then continue with the other 2/3's because we have moved
> above the execfile() position of the stack.  The try-raise-except
> paradigm is a very strong breaking system where you can only partially
> mitigate the damage using the try statement.  Smooth continuous
> execution is just not possible.

Of course, once you get to the try statement in prompt/interpreter.py it
is too late to go back, but there is nothing to stop you putting a try
satement in the inner loop itself, if that is the best place for it.
Then the exception can be caught, fixed and everything will continue,
just as you want. Exceptions should only get to the try statement in
prompt/interpreter.py if there is no way of fixing the problem. If that
is the case then we don't ever want to continue the script, because we
know there is a problem that can't be fixed.

Then it's agreed - errors are terminal (unless some other part of relax disagrees). Execution won't continue and then print an error message at the end. That was my original line of argument so I'm happy to keep the current RelaxError system. I don't like the idea of continued execution of the script because you may never realise that a fatal error has occured (or someone may not care to fix it and just publish whatever pops out at the end).

>   I've also put into place the warning system which doesn't
> print the stack, prints the warning to stderr, and the program
> continues execution.  I've also added two functions - one upgrades the
> warnings so that sys.exit(1) is called, the other downgrades the
> errors so that sys.exit(1) is not called.  The interesting property of
> this new system is that smooth continuous operation is guaranteed.
> This is because it moves down the stack (which can be reversed) rather
> than up as the raise statement mandates (which cannot be reversed).
> The more interesting property from my point of view is that the
> warning and error systems are very similar.  The Python Exception and
> Warning systems are incompatible.

Because they do two very different things. Warnings communicate a
potentially dangerous state to the user without affecting program flow.
Exceptions communicate some event (usually unexpected) that needs to be
dealt with by the program. This often requires that program flow be
disrupted in quite specific ways, so that the affected opperation can be
backed up and tried again. Exceptions only become errors that the user
should know anything about if the program has no way of dealing with the
exception. The systems are incomatible because they need to achieve two
very different things. That said I don't much like the Python warnings
system, and I suspect yours will serve relax's purposes much better.
Where I dissagree is with an exception system that tries to work in the
same way.

I like the argument - warnings can be turned into errors but errors always remain as errors. That solves the original question (the ability to change errors->warnings and warnings->errors) posed at the start of this thread (https://mail.gna.org/public/relax-devel/2006-08/msg00052.html).

When you talk about the warning system I attempted to implement, are
you talking about the system in
svn://svn.gna.org/svn/relax/branches/nan_catch_test or
svn://svn.gna.org/svn/relax/1.2?  In the 'nan_catch_text' branch I've
used the Python warning system which is ugly and bizarre.  In the 1.2
line I've created RelaxWarning classes with __call__() methods which
define the behaviour of the warning (I'm assuming this is the one
you're talking about).  The Python warning system may be the way to go
though as there is a mechanism to scale warnings into errors (although
it may need a lot of fine tuning to fit with the RelaxError system
based on Exception).  I can't see a way of turning the
RelaxWarning.__call__() function into an Exception though.

> The only two features of the Python Exception system that I can see
> are the printing of the stack to stderr and the printing of the error
> message to stderr.
>

Again, the feature which defines an exception is that it controls
program flow in such a way as to allow the program every opportunity to
fix the problem before bothering the user with it. Yes this means that
you loose the ability to continue blindly through the error, but that is
the point - the error needs to be fixed, then you can think about how to
continue or repaet what you were trying to do. Ideally all of this will
be done programaticaly, with the user kept in blissful ignorance. Only
where this is not possible is it desirable to start printing error
mesages and terminating.

I now see this other feature of moving up the stack (rather than down) as useful. With errors being terminal (whereby terminal is terminal being defined by the UI mode), then moving down the stack is of no use to the program. Allowing the end user the option of smooth continuous operation is not good because as you said, errors are bad! By moving up the stack to where the interface functions lye then the interface at that position can determine what to do. For example quitting, starting a new thread (or terminating all threads and then quitting), popping up a Qt window saying to try again or that the input was incorrect, displaying a web page explaining what went wrong, etc., these can all be determined within the stack at the position of the interface code using try statements. The fine details behind this can be examined more closely once someone decides that they would like to add another type of interface onto relax.

> Prevention of an error can be done within both
> systems.

Yes, but prevention is not the goal of an exception handling system.
If we could prevent all errors, then there would be no need for
exception handling. Keep in mind that it is often easier to ask
forgivness than permission. That is it is often better to fix an error
after the event than to try to do exhaustive error checking before hand.

The prompt user functions do implement quite exhaustive error checking to prevent the user from accidentally inputting an incorrect value. And one of my aims with relax is to catch absolutely everything that can go wrong and then handle it appropriately. Preventing all these failsafes by using something like the '--ignore-errors' command line flag and allowing smooth continuous operation (which probably won't be possible because a large cascade of errors is likely) is bad.

This is one of the reasons why exception handling in other languages is
often different than in Python. Most languages tend to prefer the 'look
before you leap' idiom of error handling. There are several good reasons
why python prefers a more forgiveness based approach. Some relate to the
things I've said above, others include:
1) It is often impossible to anticipate all neccessary checks

These should default to the builtin Python errors, but yes you can't foresee everything.

2) A long list of checks diminishes the readability and maintainabity of
code

It is still worthwhile to catch all scenarios to aid the user.

3) In something as complex as relax, the relevant state may change between
when the checks occured and the operation is attempted.

To try and sum up my position: I like the Warnings system as it is
proposed - it will probably offer more clarity and flexibility than the
Python Warnings. I'm open to the idea that the proposed RelaxError system
do the job of handling communication with the user and debugging jobs when
fatal errors arise. I'm not at all happy with the idea that the proposed
RelaxError system might replace Python Exceptions for internal exception
handling within relax. I believe that exceptions need to do a job which
is fundamentally different from the user communications role of the
proposed system.

The non-Python warning system would however be difficult to escalate into an error. Then again using the Python warning system would also be complex due to it's non-trivial implementation. I originally proposed the idea of the conversion of errors into warnings because of previous discussions but I am very happy to dump the idea. The argument, which I originally didn't think of, about sending exception signals back up the stack for the interface functions to catch is good and therefore it is worth using the Python Exception system. Because of our positions (unless somebody else disagrees) RelaxErrors will therefore remain as Exception objects. The UI can then use try statements and decide what to do with the exception, defaulting back to returning an error to the user. To separate the issues it may be worth starting a new thread to discuss how we should implement a decent RelaxWarning system.

Edward



Related Messages


Powered by MHonArc, Updated Wed Aug 16 12:20:35 2006