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 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 on 05-Nov-2000, with some ideas (such as using classes to
categorize warnings) merged in from Paul Prescod's 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 Motivation
@ -74,40 +75,54 @@ APIs For Issuing Warnings
- To issue a warning from Python: - 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 The category argument, if given, must be a warning category
class (see below); it defaults to warnings.UserWarning. This class (see below); it defaults to warnings.UserWarning. This
may raise an exception if the particular warning issued is 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: - 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 Return 0 normally, 1 if an exception is raised (either because
argument must be a warning category class (see below). When the warning was transformed into an exception, or because of a
Py_Warn() function returns 1, the caller should do normal malfunction in the implementation, such as running out of
exception handling. [Question: what about issuing warnings memory). The category argument must be a warning category class
during lexing or parsing, which don't have the exception (see below) or NULL, in which case it defaults to
machinery available?] 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 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 categories. This categorization is useful to be able to filter
out groups of warnings. The classes defined in this module have out groups of warnings. The following warnings category classes
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
are currently defined: are currently defined:
- Warning -- this is the base class of all warning category - Warning -- this is the base class of all warning category
classes. A warning category must always be a subclass of this classes and it itself a subclass of Exception
class.
- UserWarning -- the default category for sys.warn() - UserWarning -- the default category for warnings.warn()
- DeprecationWarning -- base category for warnings about deprecated - DeprecationWarning -- base category for warnings about deprecated
features features
@ -115,8 +130,19 @@ Warnings Categories
- SyntaxWarning -- base category for warnings about dubious - SyntaxWarning -- base category for warnings about dubious
syntactic features syntactic features
Other categories may be proposed during the review period for this - RuntimeWarning -- base category for warnings about dubious
PEP. 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 The Warnings Filter
@ -127,7 +153,8 @@ The Warnings Filter
There are three sides to the warnings filter: There are three sides to the warnings filter:
- The data structures used to efficiently determine the - 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. - 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 First, the warning filter collects the module and line number
where the warning is issued; this information is readily available 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 Conceptually, the warnings filter maintains an ordered list of
filter specifications; any specific warning is matched against 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 - category is a class (a subclass of warnings.Warning) of which
the warning category must be a subclass in order to match the warning category must be a subclass in order to match
- message is a regular expression that the warning message must - message is a compiled regular expression that the warning
match 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 - lineno is an integer that the line number where the warning
occurred must match, or 0 to match all line numbers 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 - "once" -- print only the first occurrence of matching
warnings warnings
The Warning class is derived from the built-in Exception class, so Since the Warning class is derived from the built-in Exception
that to turn a warning into an error we raise category(message). 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 When the warnings filter decides to issue a warning (but not when
it decides to raise an exception), it passes the information about 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 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 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 This checks the types of the arguments, compiles the message and
in front of the warnings filter. 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. 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 Command Line Syntax
@ -246,9 +277,12 @@ Command Line Syntax
All parts except 'action' may be omitted, where an empty value All parts except 'action' may be omitted, where an empty value
after stripping whitespace is the same as an omitted value. after stripping whitespace is the same as an omitted value.
Each -W option results into a call to sys.filterwarnings(); thus The C code that parses the Python command line saves the body of
later -W options override earlier -W options for warnings they all -W options in a list of strings, which is made available to
both match. 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: Examples:
@ -281,36 +315,12 @@ Open Issues
Some open issues off the top of my head: Some open issues off the top of my head:
- The proposal has all the Python API functions in the sys module, - What about issuing warnings during lexing or parsing, which
except that the warning categories are in the warnings module. don't have the exception machinery available?
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)?
- It's tempting to leave the implementation in Python and add an - The proposed command line syntax is a bit ugly (although the
absolute minimal amount of C code, only to make the standard simple cases aren't so bad: -Werror, -Wignore, etc.). Anybody
warning categories available from C code. The Py_Warn() got a better idea?
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?
- I'm a bit worried that the filter specifications are too - I'm a bit worried that the filter specifications are too
complex. Perhaps filtering only on category and module (not on 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. - 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 Implementation