mailRe: RelaxWarnings and RelaxErrors.


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

Header


Content

Posted by Chris MacRaild on August 14, 2006 - 17:28:
On Tue, 2006-08-15 at 00:27 +1000, Edward d'Auvergne wrote:
On 8/14/06, Chris MacRaild wrote:
On Sat, 2006-08-12 at 16:01 +1000, Edward d'Auvergne wrote:
I've implemented most of a new relax error/warning system that has
been talked about in this and a few other threads.  The changes are
located within the 1.2 line (the new system can sit side-by-side with
the old RelaxError system without affected program execution).  The
way these new RelaxErrors and RelaxWarnings are called is by typing
something like:

relax> RelaxBadError('Hello')

This is because RelaxBadError (a test error) is a class instance and
I've created a '__call__()' function for these simple, non-exception
objects.  Are there any features of the old system missing in the new
that you would like added?  Can you see useful new ways that this
mailable framework can be extended?  Or can you see disadvantages with
this approach?


I agree that this approach gives us a great deal of flexibility when it
comes to how errors and warnings will behave. I'm a bit concerned about
some of the features of Python exceptions that we loose by going this
way, however.

So am I.  I don't know if it is the way to go!

I'm a big fan of the try/except style of error managment
that Python encourages, and its not at all clear how we might impliment
that easily in the new framework.

I'm not so keen about the 'try:' system.  For small programs it works
beautifully.  However within relax exceptions can be quite difficult
to handle.  For example handling an exception thrown by thread (in
threading mode this is a major headache that I haven't fully solved).
This will come up again if MPI is implemented.  Another issue is
exception handling when there is a mixture of Python and C.  It would
be useful if we could precisely define this behaviour.  A grep for try
statement should show how much of a hack the try system is within
relax.  If there are any useful properties of exceptions that are lost
though - then it may not be worth dropping the system (unless we can
mimic anything that seems useful).


I know very little about threading, and only slightly more about MPI, so
I don't really understand the basis of the problems, but it seems to me
that whatever happens with relax errors, there will always be builtin
Python exceptions that need to be dealt with. As such these are problems
that need to be resolved whatever is decided about relax errors. Given
that, the benefits of Python's exception handling are worth keeping
because we are stuck with their disadvantages.


This means that while its easy to
generate classes of error with specific and complex behaviour, its not
easy to override that behaviour in a specific context.

The __call__() function can be defined specifically at runtime within
the 'error.py' file.  But if necessary any part of relax can override
it with a new function (although that should probably not be done).


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.


Ultimately I guess I'm looking at the distinction between an error (or
warning) and an exception.  An error or warning means something has gone
wrong that the user needs to know about. An exception is just another
programing tool for controling how our programs respond to events. As an
example, consider the way python iterator objects work: they iterate
through a series of objects, returning them one a time. When they run
out of objects, they raise a StopIteration exception. Clearly this
exception does not indicate an error that the user needs to know about,
or even an unexpected event (most iterators will stop eventually).
Instead code using iterators needs to use an appropriate try/except
construct to catch the StopIteration and respond appropriately. The nice
thing about exception behaviour in this context is that this try/except
can be at any level in the code - it doesn't need to sit in the same
function that directly interacts with the iterator. The message that the
iterator has run out of objects gets passed to the appropriate place
without any extra code from us. Thus we get much simpler, clearer code.

This issue that needs to be dealt with, then, is how to have the sort of
error handling at the users end that we want (errors and warnings that
can be up- and down-graded, rich debugging behaviour, etc) as well as
having good exception handling at the code level, for both builtin
Python exceptions and specific relax exceptions.

We could leave the Python exceptions as is (the try statements usually
end in 'expect: raise' so that these are always raised) while having
relax defined errors which aren't actually exceptions.  It would be
possible to define all of the necessary behaviour of Exception objects
within the RelaxError objects while avoiding 'raise' statements.

I'm not sure how this could be achieved. The defining feature of an
exception is that it stops the normal program flow and starts looking
for an appropriate handling mechanism (ie. an appropriate except
statement) by propagating up the stack of function calls, examining each
calling function in turn for the applicable handler. Calling a function,
by definition, can only extend the stack downwards, it cannot regress up
the stack in the way an exception does.

Another reason is that a RelaxError based on the Exception class is
difficult when scripting.  The try statement will catch it and ignore
it, but the execution of the script cannot be continued.  Then again,
is there a situation where ignoring errors and continuing program
execution would be useful?

I dont follow this. Try/except statements will only catch the exceptions
you tell them to catch, all others will be propagated. Clearly it is a
bad idea to cast except statements too broadly, but its an easy problem
to fix in the code.


In addition to the options we've already considered, there is the
possibility of coding appropriately rich behaviour into a replacement
function for sys.excepthook. This is the function that is called when an
exception is not caught by an appropriate handler. It's default
behaviour is to print the traceback and display the error. It is passed
both the exception class and the specific instance that was raised, as
well as the traceback object. This should be enough to get some quite
rich behaviour without too much hacking, I think.

I wonder, what is more important - the 'raise' semantics, the
Exception objects, and all the related behaviour or simply the
printing out to stderr?  All the ancillary relax specific behaviour
can be implemented in both systems.  But what are we trying to really
achieve with the RelaxErrors?


Again I make the distinction between user level errors and exceptions.
From my point of view the important thing that errors must achieve for
the user is clear communication of the problem and useful debugging
features like saving state. Clearly Exception objects cannot achieve all
of this on their own, but I think we can code Exception handling
features into relax which do achieve these things. Downgrading an error
to a warning is more difficult to achieve, but is not necessary if
warnings are used where appropriate. I can only imagine wanting to
downgrade an error if I didn't think it should have been an error in the
first place, so better to raise a warning at that point.

Exceptions must achieve something very different inside relax - they
need to communicate unexpected results up the stack of function calls to
the point where they can be dealt with. Exceptions only become errors
when there is no way of dealing with them. As far as I am aware, the
only reasonable way of achieving this is through the Python Exception
propagation mechanism.


Chris




Related Messages


Powered by MHonArc, Updated Tue Aug 15 08:02:38 2006