New version of the PEP. Changes based upon the second
(delayed-import) implentation. Address some of Paul's concerns.
This commit is contained in:
parent
ef23f1f0cf
commit
600a71428c
193
pep-0230.txt
193
pep-0230.txt
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue