forked from python/peps
-
Notifications
You must be signed in to change notification settings - Fork 0
/
pep-0230.txt
417 lines (288 loc) · 13.6 KB
/
pep-0230.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
PEP: 230
Title: Warning Framework
Version: $Revision$
Last-Modified: $Date$
Author: [email protected] (Guido van Rossum)
Status: Final
Type: Standards Track
Content-Type: text/x-rst
Created: 28-Nov-2000
Python-Version: 2.1
Post-History: 05-Nov-2000
Abstract
========
This PEP proposes a C and Python level API, as well as command
line flags, to issue warning messages and control what happens to
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. Also, an attempt to
implement the proposal caused several small tweaks.
Motivation
==========
With Python 3000 looming, it is necessary to start issuing
warnings about the use of obsolete or deprecated features, in
addition to errors. There are also lots of other reasons to be
able to issue warnings, both from C and from Python code, both at
compile time and at run time.
Warnings aren't fatal, and thus it's possible that a program
triggers the same warning many times during a single execution.
It would be annoying if a program emitted an endless stream of
identical warnings. Therefore, a mechanism is needed that
suppresses multiple identical warnings.
It is also desirable to have user control over which warnings are
printed. While in general it is useful to see all warnings all
the time, there may be times where it is impractical to fix the
code right away in a production program. In this case, there
should be a way to suppress warnings.
It is also useful to be able to suppress specific warnings during
program development, e.g. when a warning is generated by a piece
of 3rd party code that cannot be fixed right away, or when there
is no way to fix the code (possibly a warning message is generated
for a perfectly fine piece of code). It would be unwise to offer
to suppress all warnings in such cases: the developer would miss
warnings about the rest of the code.
On the other hand, there are also situations conceivable where
some or all warnings are better treated as errors. For example,
it may be a local coding standard that a particular deprecated
feature should not be used. In order to enforce this, it is
useful to be able to turn the warning about this particular
feature into an error, raising an exception (without necessarily
turning all warnings into errors).
Therefore, I propose to introduce a flexible "warning filter"
which can filter out warnings or change them into exceptions,
based on:
- Where in the code they are generated (per package, module, or
function)
- The warning category (warning categories are discussed below)
- A specific warning message
The warning filter must be controllable both from the command line
and from Python code.
APIs For Issuing Warnings
=========================
- To issue a warning from Python::
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. 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 PyErr_Warn(PyObject *category, char *message);
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
===================
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 following warnings category classes
are currently defined:
- ``Warning`` -- this is the base class of all warning category
classes and it itself a subclass of Exception
- ``UserWarning`` -- the default category for ``warnings.warn()``
- ``DeprecationWarning`` -- base category for warnings about deprecated
features
- ``SyntaxWarning`` -- base category for warnings about dubious
syntactic features
- ``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
===================
The warnings filter control whether warnings are ignored,
displayed, or turned into errors (raising an exception).
There are three sides to the warnings filter:
- The data structures used to efficiently determine the
disposition of a particular ``warnings.warn()`` or ``PyErr_Warn()``
call.
- The API to control the filter from Python source code.
- The command line switches to control the filter.
The warnings filter works in several stages. It is optimized for
the (expected to be common) case where the same warning is issued
from the same place in the code over and over.
First, the warning filter collects the module and line number
where the warning is issued; this information is readily available
through ``sys._getframe()``.
Conceptually, the warnings filter maintains an ordered list of
filter specifications; any specific warning is matched against
each filter specification in the list in turn until a match is
found; the match determines the disposition of the match. Each
entry is a tuple as follows::
(category, message, module, lineno, action)
- 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 compiled regular expression that the warning
message must match (the match is case-insensitive)
- 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
- action is one of the following strings:
- "error" -- turn matching warnings into exceptions
- "ignore" -- never print matching warnings
- "always" -- always print matching warnings
- "default" -- print the first occurrence of matching warnings
for each location where the warning is issued
- "module" -- print the first occurrence of matching warnings
for each module where the warning is issued
- "once" -- print only the first occurrence of matching
warnings
Since the ``Warning`` class is derived from the built-in ``Exception``
class, to turn a warning into an error we simply raise
``category(message)``.
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 ``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. 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
====================================
::
warnings.filterwarnings(message, category, module, lineno, action)
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.
::
warnings.resetwarnings()
Reset the warnings filter to empty.
Command Line Syntax
===================
There should be command line options to specify the most common
filtering actions, which I expect to include at least:
- suppress all warnings
- suppress a particular warning message everywhere
- suppress all warnings in a particular module
- turn all warnings into exceptions
I propose the following command line option syntax::
-Waction[:message[:category[:module[:lineno]]]]
Where:
- 'action' is an abbreviation of one of the allowed actions
("error", "default", "ignore", "always", "once", or "module")
- 'message' is a message string; matches warnings whose message
text is an initial substring of 'message' (matching is
case-insensitive)
- 'category' is an abbreviation of a standard warning category
class name **or** a fully-qualified name for a user-defined
warning category class of the form [package.]module.classname
- 'module' is a module name (possibly package.module)
- 'lineno' is an integral line number
All parts except 'action' may be omitted, where an empty value
after stripping whitespace is the same as an omitted value.
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:
``-Werror``
Turn all warnings into errors
``-Wall``
Show all warnings
``-Wignore``
Ignore all warnings
``-Wi:hello``
Ignore warnings whose message text starts with "hello"
``-We::Deprecation``
Turn deprecation warnings into errors
``-Wi:::spam:10``
Ignore all warnings on line 10 of module spam
``-Wi:::spam -Wd:::spam:10``
Ignore all warnings in module spam except on line 10
``-We::Deprecation -Wd::Deprecation:spam``
Turn deprecation warnings into errors except in module spam
Open Issues
===========
Some open issues off the top of my head:
- What about issuing warnings during lexing or parsing, which
don't have the exception machinery available?
- 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
message text and line number) would be enough?
- There's a bit of confusion between module names and file names.
The reporting uses file names, but the filter specification uses
module names. Maybe it should allow filenames as well?
- I'm not at all convinced that packages are handled right.
- 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, Barry Warsaw and Fred Drake have brought up several
additional concerns that I feel aren't critical. I address them
here (the concerns are paraphrased, not exactly their 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.
- Barry: It would be nice if lineno can be a range specification.
Response: Too much complexity already.
- Barry: I'd like to add my own warning action. Maybe if 'action'
could be a callable as well as a string. Then in my IDE, I
could set that to "mygui.popupWarningsDialog".
Response: For that purpose you would override
``warnings.showwarning()``.
- Fred: why do the Warning category classes have to be in
``__builtin__``?
Response: that's the simplest implementation, given that the
warning categories must be available in C before the first
``PyErr_Warn()`` call, which imports the warnings module. I see no
problem with making them available as built-ins.
Implementation
==============
Here's a prototype implementation:
http://sourceforge.net/patch/?func=detailpatch&patch_id=102715&group_id=5470
..
Local Variables:
mode: indented-text
indent-tabs-mode: nil
End: