[Python-Dev] Warnings PEP

Paul Prescod paulp@ActiveState.com
Mon, 06 Nov 2000 14:01:27 -0800


Guido van Rossum wrote:
> 
> Paul, thanks for submitting a warnings framework.  I'd like to give
> some feedback, comparing it to my own proposal.  Please also give
> explicit feedback on mine!

Okay, but I'm a little bit worried about getting into a science project
-- in terms of my time to work on it. I'm perfectly happy with the two
requirements you mentioned but I haven't thought enough about filters to
be able to whip off ideas about them quickly.

> What's missing here is a set of requirements.  Comparing your proposal
> to my requirements, I find that you are not addressing two
> requirements that are important in my mind: a convenient and efficient
> C API, and a mechanism that prints a warning message only the first
> time that the warning is issued.  

I agree that these are both important things and I will add them to my
proposal. Also, I like your requirement that the target for warnings
should be easily customizable.

> Warnings are more a human engineering issue than a technical issue!
> That's also why I am emphasizing a C API -- I want to make it real
> easy to ussue quality warnings in the runtime.  It's also why I
> specify a rich (probably too rich!) filtering mechanism.

Let me put out the strawman proposal that "grep" is a nicely orthogonal
filtering mechanism. <duck!>

I guess my thinking is that you turn off warnings at the source. If you
get a warning about __del__ exceptions then you put in a try/except. If
you get a warning about an unused variable then you assign it to itself.
If you get a warning about integer division then you should pass in a
float and so forth.

> > Syntax
> >
> >     assert >> cls, test[[[, arg], arg]...]
> 
> I have several problems with this.  First of all, using "assert" means
> that in "optimizing" mode (python -O) you won't get *any* warnings.  I
> think that the decision to disable all warnings should be independent
> from the decision to "optimize".  

Arguable. I see it as "release" and "debug" configurations. python and
python_d.

> Second, you're hypergeneralizing the
> extended print syntax.  Just because I think it's okay to add >>file
> to the print syntax doesn't mean that it's now okay to add >>object
> syntax to all statements!

Well getting new syntax into Python is really, really hard so we've got
to squeeze out as much value out of what we have as possible. But
anyhow, assertions are not allowed to 

You and I agree that there is an important sociological dimension to
this. We can't require:

  import warnings

  warnings.warn("foo", "bar")

I prefer:

  warn Foo, "Bar"

just like:

  raise Foo, "Bar"

> I also don't see what warnings have to do with assertions.  Assertions
> are a mechanism to check for error conditions.  What happens if the
> error is detected is of less importance -- it could raise an exception
> (Python), issue a fatal error (C), or do nothing (in -O mode).

Warnings are issued when an error or dubious construct is detected.
Assertions are "fatal warnings". You agree that it is appropriate for
some "warnings" to kill the app in some circumstances. Isn't it just a
hop-skip-and-a-jump to say that warnings and errors are just points on
the spectrum:

Report Once
Report Always
Report and Die

Python trained me to think of function calls and object constructions as
being basically the same thing -- and keyword arguments and variable
names being basically the same thing. etc.

> With warnings I believe the issue is not so much the detection of the
> condition (for which a regular 'if' statement does just fine) but the
> reporting.  Again, this is motivated by the fact that I expect that
> flexible filtering is essential for a successful warning mechanism.

I don't see why assertions need special syntax but warnings do not! I
would have been happy for a "warn" keyword with assert-like syntax but I
don't think I'll see that in my lifetime.

> This is just a matter of exposition, but when I first read your PEP I
> had a hard time figuring out the purpose of the cls object.  It wasn't
> until I got to the very end where I saw your example classes that I
> realized what it is: it represents a specific warning condition or a
> group of related warning conditions.

It sort of evolved into an exception-like mechanism in that the class is
instantiated with arguments just as exception classes are.

> >         if action=="error":
> >             *** existing assertion code ***
> 
> That's just
> 
>               raise AssertionError, message
> 
> Right?

Well I'd like the traceback to emanate from the caller's position not
the warning handler's position. Python doesn't really have a way to say
that simply. This may well be implemented in C so it might not matter.

> Suggestion: have separate warning and error handlers, so that if I
> want to override these branches of the if statement I don't have to
> repeat the entire handler.

Good idea.

> I see no definition of sys.disabled_warnings.  Instead of
> sys.disabled_warnings you meant sys.suppress, right?

Right.

> >               return "suppress"
> >           for base in cls.__bases__:
> >               rc = self.get_user_request(base)
> >               if rc:
> >                   return rc
> >           else:
> >               return None
> 
> This approach (searching for the class name or the name of one of its
> base classes in a list) doesn't look very object-oriented.  It would
> make more sense to store the desired return value as a class or
> instance attribute.  The default warning action could be stored on the
> base class.

The way I came to this odd structure is I wanted most subclasses to be
just no-ops as many exceptions are:

class ActiveStateLintWarning(Warning): pass

class CodeLooksLikePerl(ActiveStateLintWarning): pass

class TooMuchOnOneLine(CodeLooksLikePerl): pass

So what __init__ could I write that would look up -wTooMuchOnOneLine and
then if it failed that, look up -wCodeLooksLikePerl and so forth?

It gets pretty mind-bending because you sort of want one method to call
another and yet you want it to be the *same* inherited method (i.e.
don't have to code it each time). E.g. you want to run the *same method*
at each level of the hierarchy. So I just do that directly.

> >     -w XXX => sys.warnings.append("XXX")
> >     -e XXX => sys.errors.append("XXX")
> >     -no-w XXX => sys.suppress.append("XXX")
> >     -wall => sys.default_warning_action => "warn"
> >     -eall => sys.default_warning_action => "error"
> >     -no-wall => sys.default_warning_action => "suppress"
> 
> Python doesn't support long options (I don't *like* long options so I
> doubt that this is a good occasion to start lobbying for them :-).  We
> can come up with different options though.

Are these long options? Or just overloaded behavior on -w, -e, -n .
Think of "all" as the base class for warnings or something.

> >     As per the code above, errors take precedence over warnings and
> >     warnings over suppressions unless a particular assertion class
> >     specifies otherwise.
> 
> I would use a different precedence scheme: a more specific filter
> takes precedence over a more general filter.  So -eall -wdubious would
> mean that "dubious" class warnings are warnings but all others are
> errors, and -wall -edubious would mean the opposite.

Good idea. I think the code may work that way already. :)

 Paul Prescod