[Python-Dev] Warnings PEP

Guido van Rossum guido@python.org
Mon, 06 Nov 2000 22:07:49 -0500


[Guido]
> > 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.

[Paul]
> 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.

I'm thinking of a different situation, where the user isn't very Unix
or Python sophisticated or the code generating warnings isn't easily
accessible for editing, or the fix isn't obvious (which I expect to be
often the case for __del__ and int division warnings).

Just as with exception handlers, where unqualified except clauses are
bad because of the risk that they mask real errors, I want to avoid
the likelihood that end users (those who can't or shouldn't fix the
errors) have to turn off all warnings.

> > > 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 

(Did you forget to complete a sentence there?)

I say there's no need for new syntax, this can just be a function.

> 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"

Hm.  Raise was made a statement because (1) Modula-3 did so, and (2)
it is a flow control modifier.  You haven't made a good case for why
warn() can't be a function.

> > 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

Possibly -- and then 'assert' is a poor choice of name for the
feature.  Objection denied.

> 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.

Indeed.  But binding arbitrary unrelated semantics to an existing
statement with a carefully chosen name is poor design too.  You might
as well propose print<<file as a new input statement: they're both I/O
related. :-)

> > 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.

OK.

> > 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

The idea of using a class hierarchy to classify warnings is definitely
a good one.

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

Yeah, it's not clear what to do.  You would like the -w option to poke
a value in a class variable, but the problem there is that it doesn't
know in which module the class is defined.  (On the other hand, if
that problem could be solved, it would be the preferred solution --
since it would solve the problem of typos in the -w argument neatly.)

> 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.

Yes: if you change -no-w into -n, they can all be short options.  Note
that no matter what syntax we choose, we'll always be deviants
compared to Perl and GCC: those require a -w or -W option to *enable*
warnings.  (I found some great quotes in the Perl man page:

    Whenever you get mysterious behavior, try the -w switch!!!  Whenever
    you don't get mysterious behavior, try using -w anyway.

    The -w switch produces some lovely diagnostics.

    Did we mention that you should definitely consider using the -w
    switch?

    Bugs
        The -w switch is not mandatory.
)

> > >     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. :)

Indeed it does. :-)  Then what did you mean by your remark about the
precedence?

--Guido van Rossum (home page: http://www.python.org/~guido/)