> relax> import warnings
> relax> def format(message, category, filename, lineno):
> relax| return "RelaxWarning: %s\n" % message
> relax|
> relax> warnings.formatwarning = format
> relax> warnings.warn('Hello')
> RelaxWarning: Hello
> relax> help(warnings.formatwarning)
> relax> warnings.warn('Hello')
> relax>
>
> Huh??? Where did RelaxWarning disappear to??? Same thing happens if
> you use the real Python help function on any 'warnings' objects,
> including itself??? I'm pretty sure this should not happen!
I think the problem is that the default warning behaviour is to print a
given warning only once from a specific location in the code. Because
you are working at the interactive prompt, it always looks like the same
location, so the warning will only be printed once (it has nothing to do
with help). Try:
relax> import warnings
relax> warnings.filterwarnings('always')
relax> def format(message, category, filename, lineno):
relax| return "RelaxWarning: %s\n" % message
relax|
relax> warnings.formatwarning = format
relax> warnings.warn('Hello')
RelaxWarning: Hello
relax> help(warnings.formatwarning)
relax> warnings.warn('Hello')
RelaxWarning: Hello
Of course! I should have seen that one.
> If there are too many problems caused by bugs in the Python warning
> system we can write our own warning system in the future. Anyway,
> enough bashing of the Python warning system.
>
> A problem that needs to be solved is how do we implement specific
> warnings? For example if two distant areas of relax test for zero
> length XH bond vectors, there should be one function which takes the
> residue number and formats a standard message for that warning.
> However the 'warn()' function expects a message? What if we have a
> class object called RelaxZeroLengthWarning which has a __call__()
> method which returns the formatted string? You could then, from
> within different parts of relax, generate an error by having the code:
>
> if len(xh) == 0.0:
> warn(RelaxZeroLengthWarning(res))
>
> where warn was placed into __builtin__ by relax (see the
> 'nan_catch_test' branch) and 'res' is the residue number. There
> might, however, be a much better way to implement specific warnings.
> The rest of the system could be setup as in the 'nan_catch_test'
> branch.
>
This is a good point. After some head-scratching, I think the 'correct'
way to do it is to treat each warning category specifically in the
formatwarning function. So we would have something more like:
def format(message, category, filename, lineno):
if issubclass(category, RelaxZeroLengthWarning):
message = "Zero length vector at residue %s" % message
# ...lots more specific warning cases...
if issubclass(category, BaseWarning):
message = "RelaxWarning: %s\n" % message
return message
warnings.formatwarning = format
Then the 'message' for RelaxZeroLengthWarning when we do warn() is just
the residue, and format() does the rest.
A number of RelaxErrors take a number of arguments and from my reading
of the docs I would guess that the warning system would assert that
the 'message' argument to 'warn()' must be a string. How about using
the '__call__()' function to pass in the relevant data to the class
using keywords and then store it within the object. Then the
subsequently called format function can use the else-if chain to pull
out and format the stored data?
Edward