New version of the PEP. Changes based upon the second

(delayed-import) implentation.  Address some of Paul's concerns.
This commit is contained in:
Guido van Rossum 2000-12-11 16:34:34 +00:00
parent ef23f1f0cf
commit 600a71428c
1 changed files with 119 additions and 74 deletions

View File

@ -16,7 +16,8 @@ Abstract
them. This is mostly based on GvR's proposal posted to python-dev
on 05-Nov-2000, with some ideas (such as using classes to
categorize warnings) merged in from Paul Prescod's
counter-proposal posted on the same date.
counter-proposal posted on the same date. Also, an attempt to
implement the proposal caused several small tweaks.
Motivation
@ -74,40 +75,54 @@ APIs For Issuing Warnings
- To issue a warning from Python:
sys.warn(message[, category])
import warnings
warnings.warn(message[, category[, stacklevel]])
The category argument, if given, must be a warning category
class (see below); it defaults to warnings.UserWarning. This
may raise an exception if the particular warning issued is
changed into an error by the warnings filter.
changed into an error by the warnings filter. The stacklevel
can be used by wrapper functions written in Python, like this:
def deprecation(message):
warn(message, DeprecationWarning, level=2)
This makes the warning refer to the deprecation()'s caller,
rather than to the source of deprecation() itself (since the
latter would defeat the purpose of the warning message).
- To issue a warning from C:
int Py_Warn(char *message, PyObject *category)
int PyErr_Warn(PyObject *category, char *message);
Return 0 normally, 1 if an exception is raised. The category
argument must be a warning category class (see below). When
Py_Warn() function returns 1, the caller should do normal
exception handling. [Question: what about issuing warnings
during lexing or parsing, which don't have the exception
machinery available?]
Return 0 normally, 1 if an exception is raised (either because
the warning was transformed into an exception, or because of a
malfunction in the implementation, such as running out of
memory). The category argument must be a warning category class
(see below) or NULL, in which case it defaults to
PyExc_RuntimeWarning. When PyErr_Warn() function returns 1, the
caller should do normal exception handling.
The current C implementation of PyErr_Warn() imports the
warnings module (implemented in Python) and calls its warn()
function. This minimizes the amount of C code that needs to be
added to implement the warning feature.
[XXX Open Issue: what about issuing warnings during lexing or
parsing, which don't have the exception machinery available?]
Warnings Categories
The "warnings" module defines classes representing warning
There are a number of built-in exceptions that represent warning
categories. This categorization is useful to be able to filter
out groups of warnings. The classes defined in this module have
no semantics attached to them and are never instantiated -- only
their names are used for filtering (see the section on the
warnings filter below). The following warnings category classes
out groups of warnings. The following warnings category classes
are currently defined:
- Warning -- this is the base class of all warning category
classes. A warning category must always be a subclass of this
class.
classes and it itself a subclass of Exception
- UserWarning -- the default category for sys.warn()
- UserWarning -- the default category for warnings.warn()
- DeprecationWarning -- base category for warnings about deprecated
features
@ -115,8 +130,19 @@ Warnings Categories
- SyntaxWarning -- base category for warnings about dubious
syntactic features
Other categories may be proposed during the review period for this
PEP.
- RuntimeWarning -- base category for warnings about dubious
runtime features
[XXX: Other warning categories may be proposed during the review
period for this PEP.]
These standard warning categories are available from C as
PyExc_Warning, PyExc_UserWarning, etc. From Python, they are
available in the __builtin__ module, so no import is necessary.
User code can define additional warning categories by subclassing
one of the standard warning categories. A warning category must
always be a subclass of the Warning class.
The Warnings Filter
@ -127,7 +153,8 @@ The Warnings Filter
There are three sides to the warnings filter:
- The data structures used to efficiently determine the
disposition of a particular Py_Warn() call.
disposition of a particular warnings.warn() or PyErr_Warn()
call.
- The API to control the filter from Python source code.
@ -139,7 +166,7 @@ The Warnings Filter
First, the warning filter collects the module and line number
where the warning is issued; this information is readily available
through PyEval_GetFrame().
through sys._getframe().
Conceptually, the warnings filter maintains an ordered list of
filter specifications; any specific warning is matched against
@ -152,10 +179,11 @@ The Warnings Filter
- category is a class (a subclass of warnings.Warning) of which
the warning category must be a subclass in order to match
- message is a regular expression that the warning message must
match
- message is a compiled regular expression that the warning
message must match
- module is a regular expression that the module name must match
- module is a compiled regular expression that the module name
must match
- lineno is an integer that the line number where the warning
occurred must match, or 0 to match all line numbers
@ -177,37 +205,40 @@ The Warnings Filter
- "once" -- print only the first occurrence of matching
warnings
The Warning class is derived from the built-in Exception class, so
that to turn a warning into an error we raise category(message).
Since the Warning class is derived from the built-in Exception
class, to turn a warning into an error we simply raise
category(message).
The Warnings Output Hook
Warnings Output And Formatting Hooks
When the warnings filter decides to issue a warning (but not when
it decides to raise an exception), it passes the information about
the function sys.showwarning(message, category, filename, lineno).
the function warnings.showwarning(message, category, filename, lineno).
The default implementation of this function writes the warning text
to sys.stderr, and shows the source line of the filename.
to sys.stderr, and shows the source line of the filename. It has
an optional 5th argument which can be used to specify a different
file than sys.stderr.
The formatting of warnings is done by a separate function,
warnings.formatwarning(message, category, filename, lineno). This
returns a string (that may contain newlines and ends in a newline)
that can be printed to get the identical effect of the
showwarning() function.
API For Manipulating Warning Filters
sys.filterwarnings(message, category, module, lineno, action)
warnings.filterwarnings(message, category, module, lineno, action)
This checks the types of the arguments and inserts them as a tuple
in front of the warnings filter.
This checks the types of the arguments, compiles the message and
module regular expressions, and inserts them as a tuple in front
of the warnings filter.
sys.resetwarnings()
warnings.resetwarnings()
Reset the warnings filter to empty.
sys.setupwarnings(args)
Parse command line options and initialize the warnings filter
accordingly. The argument should be sys.argv[1:] or equivalent.
Unrecognized options raise getopt.error. The return value is a
list containing the remaining (non-option) arguments.
Command Line Syntax
@ -246,9 +277,12 @@ Command Line Syntax
All parts except 'action' may be omitted, where an empty value
after stripping whitespace is the same as an omitted value.
Each -W option results into a call to sys.filterwarnings(); thus
later -W options override earlier -W options for warnings they
both match.
The C code that parses the Python command line saves the body of
all -W options in a list of strings, which is made available to
the warnings module as sys.warnoptions. The warnings module
parses these when it is first imported. Errors detected during
the parsing of sys.warnoptions are not fatal; a message is written
to sys.stderr and processing continues with the option.
Examples:
@ -281,36 +315,12 @@ Open Issues
Some open issues off the top of my head:
- The proposal has all the Python API functions in the sys module,
except that the warning categories are in the warnings module.
Perhaps everything should be in the warnings module (like the
prototype implementation)? Or perhaps warn() should be promoted
to a built-in (i.e. in the __builtin__ module)?
- What about issuing warnings during lexing or parsing, which
don't have the exception machinery available?
- It's tempting to leave the implementation in Python and add an
absolute minimal amount of C code, only to make the standard
warning categories available from C code. The Py_Warn()
function could call warnings.warn(). Similarly, the Python
main() function could collect -W options and pass them to
warnings.setupwarnings().
- The prototype implements a third argument to warn():
warn(message, category=UserWarning, level=1)
The 'level' argument could be used by wrapper functions written
in Python, like this:
def deprecation(message):
warn(message, DeprecationWarning, level=2)
This makes the warning refer to the deprecation()'s caller,
rather than to the source of deprecation() itself (the latter
would defeat the purpose of the warning message).
- The proposed command line syntax is ugly (although the simple
cases aren't so bad: -Werror, -Wignore, etc.). Anybody got a
better idea?
- The proposed command line syntax is a bit ugly (although the
simple cases aren't so bad: -Werror, -Wignore, etc.). Anybody
got a better idea?
- I'm a bit worried that the filter specifications are too
complex. Perhaps filtering only on category and module (not on
@ -322,7 +332,42 @@ Open Issues
- I'm not at all convinced that packages are handled right.
- Better names for the various API functions?
- Do we need more standard warning categories? Fewer?
- In order to minimize the start-up overhead, the warnings module
is imported by the first call to PyErr_Warn(). It does the
command line parsing for -W options upon import. Therefore, it
is possible that warning-free programs will not complain about
invalid -W options.
Rejected Concerns
Paul Prescod has brought up several additional concerns that I
feel aren't critical. I address them here (the concerns are
paraphrased, not necessarily exactly Paul's words).
- Paul: warn() should be a built-in or a statement to make it easily
available.
Response: "from warnings import warn" is easy enough.
- Paul: What if I have a speed-critical module that triggers
warnings in an inner loop. It should be possible to disable the
overhead for detecting the warning (not just suppress the
warning).
Response: rewrite the inner loop to avoid triggering the
warning.
- Paul: What if I want to see the full context of a warning?
Response: use -Werror to turn it into an exception.
- Paul: I prefer ":*:*:" to ":::" for leaving parts of the warning
spec out.
Response: I don't.
Implementation