|
1 """Python part of the warnings subsystem.""" |
|
2 |
|
3 # Note: function level imports should *not* be used |
|
4 # in this module as it may cause import lock deadlock. |
|
5 # See bug 683658. |
|
6 import linecache |
|
7 import sys |
|
8 import types |
|
9 |
|
10 __all__ = ["warn", "showwarning", "formatwarning", "filterwarnings", |
|
11 "resetwarnings", "catch_warnings"] |
|
12 |
|
13 |
|
14 def warnpy3k(message, category=None, stacklevel=1): |
|
15 """Issue a deprecation warning for Python 3.x related changes. |
|
16 |
|
17 Warnings are omitted unless Python is started with the -3 option. |
|
18 """ |
|
19 if sys.py3kwarning: |
|
20 if category is None: |
|
21 category = DeprecationWarning |
|
22 warn(message, category, stacklevel+1) |
|
23 |
|
24 def _show_warning(message, category, filename, lineno, file=None, line=None): |
|
25 """Hook to write a warning to a file; replace if you like.""" |
|
26 if file is None: |
|
27 file = sys.stderr |
|
28 try: |
|
29 file.write(formatwarning(message, category, filename, lineno, line)) |
|
30 except IOError: |
|
31 pass # the file (probably stderr) is invalid - this warning gets lost. |
|
32 # Keep a worrking version around in case the deprecation of the old API is |
|
33 # triggered. |
|
34 showwarning = _show_warning |
|
35 |
|
36 def formatwarning(message, category, filename, lineno, line=None): |
|
37 """Function to format a warning the standard way.""" |
|
38 s = "%s:%s: %s: %s\n" % (filename, lineno, category.__name__, message) |
|
39 line = linecache.getline(filename, lineno) if line is None else line |
|
40 if line: |
|
41 line = line.strip() |
|
42 s += " %s\n" % line |
|
43 return s |
|
44 |
|
45 def filterwarnings(action, message="", category=Warning, module="", lineno=0, |
|
46 append=0): |
|
47 """Insert an entry into the list of warnings filters (at the front). |
|
48 |
|
49 Use assertions to check that all arguments have the right type.""" |
|
50 import re |
|
51 assert action in ("error", "ignore", "always", "default", "module", |
|
52 "once"), "invalid action: %r" % (action,) |
|
53 assert isinstance(message, basestring), "message must be a string" |
|
54 assert isinstance(category, (type, types.ClassType)), \ |
|
55 "category must be a class" |
|
56 assert issubclass(category, Warning), "category must be a Warning subclass" |
|
57 assert isinstance(module, basestring), "module must be a string" |
|
58 assert isinstance(lineno, int) and lineno >= 0, \ |
|
59 "lineno must be an int >= 0" |
|
60 item = (action, re.compile(message, re.I), category, |
|
61 re.compile(module), lineno) |
|
62 if append: |
|
63 filters.append(item) |
|
64 else: |
|
65 filters.insert(0, item) |
|
66 |
|
67 def simplefilter(action, category=Warning, lineno=0, append=0): |
|
68 """Insert a simple entry into the list of warnings filters (at the front). |
|
69 |
|
70 A simple filter matches all modules and messages. |
|
71 """ |
|
72 assert action in ("error", "ignore", "always", "default", "module", |
|
73 "once"), "invalid action: %r" % (action,) |
|
74 assert isinstance(lineno, int) and lineno >= 0, \ |
|
75 "lineno must be an int >= 0" |
|
76 item = (action, None, category, None, lineno) |
|
77 if append: |
|
78 filters.append(item) |
|
79 else: |
|
80 filters.insert(0, item) |
|
81 |
|
82 def resetwarnings(): |
|
83 """Clear the list of warning filters, so that no filters are active.""" |
|
84 filters[:] = [] |
|
85 |
|
86 class _OptionError(Exception): |
|
87 """Exception used by option processing helpers.""" |
|
88 pass |
|
89 |
|
90 # Helper to process -W options passed via sys.warnoptions |
|
91 def _processoptions(args): |
|
92 for arg in args: |
|
93 try: |
|
94 _setoption(arg) |
|
95 except _OptionError, msg: |
|
96 print >>sys.stderr, "Invalid -W option ignored:", msg |
|
97 |
|
98 # Helper for _processoptions() |
|
99 def _setoption(arg): |
|
100 import re |
|
101 parts = arg.split(':') |
|
102 if len(parts) > 5: |
|
103 raise _OptionError("too many fields (max 5): %r" % (arg,)) |
|
104 while len(parts) < 5: |
|
105 parts.append('') |
|
106 action, message, category, module, lineno = [s.strip() |
|
107 for s in parts] |
|
108 action = _getaction(action) |
|
109 message = re.escape(message) |
|
110 category = _getcategory(category) |
|
111 module = re.escape(module) |
|
112 if module: |
|
113 module = module + '$' |
|
114 if lineno: |
|
115 try: |
|
116 lineno = int(lineno) |
|
117 if lineno < 0: |
|
118 raise ValueError |
|
119 except (ValueError, OverflowError): |
|
120 raise _OptionError("invalid lineno %r" % (lineno,)) |
|
121 else: |
|
122 lineno = 0 |
|
123 filterwarnings(action, message, category, module, lineno) |
|
124 |
|
125 # Helper for _setoption() |
|
126 def _getaction(action): |
|
127 if not action: |
|
128 return "default" |
|
129 if action == "all": return "always" # Alias |
|
130 for a in ('default', 'always', 'ignore', 'module', 'once', 'error'): |
|
131 if a.startswith(action): |
|
132 return a |
|
133 raise _OptionError("invalid action: %r" % (action,)) |
|
134 |
|
135 # Helper for _setoption() |
|
136 def _getcategory(category): |
|
137 import re |
|
138 if not category: |
|
139 return Warning |
|
140 if re.match("^[a-zA-Z0-9_]+$", category): |
|
141 try: |
|
142 cat = eval(category) |
|
143 except NameError: |
|
144 raise _OptionError("unknown warning category: %r" % (category,)) |
|
145 else: |
|
146 i = category.rfind(".") |
|
147 module = category[:i] |
|
148 klass = category[i+1:] |
|
149 try: |
|
150 m = __import__(module, None, None, [klass]) |
|
151 except ImportError: |
|
152 raise _OptionError("invalid module name: %r" % (module,)) |
|
153 try: |
|
154 cat = getattr(m, klass) |
|
155 except AttributeError: |
|
156 raise _OptionError("unknown warning category: %r" % (category,)) |
|
157 if not issubclass(cat, Warning): |
|
158 raise _OptionError("invalid warning category: %r" % (category,)) |
|
159 return cat |
|
160 |
|
161 |
|
162 # Code typically replaced by _warnings |
|
163 def warn(message, category=None, stacklevel=1): |
|
164 """Issue a warning, or maybe ignore it or raise an exception.""" |
|
165 # Check if message is already a Warning object |
|
166 if isinstance(message, Warning): |
|
167 category = message.__class__ |
|
168 # Check category argument |
|
169 if category is None: |
|
170 category = UserWarning |
|
171 assert issubclass(category, Warning) |
|
172 # Get context information |
|
173 try: |
|
174 caller = sys._getframe(stacklevel) |
|
175 except ValueError: |
|
176 globals = sys.__dict__ |
|
177 lineno = 1 |
|
178 else: |
|
179 globals = caller.f_globals |
|
180 lineno = caller.f_lineno |
|
181 if '__name__' in globals: |
|
182 module = globals['__name__'] |
|
183 else: |
|
184 module = "<string>" |
|
185 filename = globals.get('__file__') |
|
186 if filename: |
|
187 fnl = filename.lower() |
|
188 if fnl.endswith((".pyc", ".pyo")): |
|
189 filename = filename[:-1] |
|
190 else: |
|
191 if module == "__main__": |
|
192 try: |
|
193 filename = sys.argv[0] |
|
194 except AttributeError: |
|
195 # embedded interpreters don't have sys.argv, see bug #839151 |
|
196 filename = '__main__' |
|
197 if not filename: |
|
198 filename = module |
|
199 registry = globals.setdefault("__warningregistry__", {}) |
|
200 warn_explicit(message, category, filename, lineno, module, registry, |
|
201 globals) |
|
202 |
|
203 def warn_explicit(message, category, filename, lineno, |
|
204 module=None, registry=None, module_globals=None): |
|
205 lineno = int(lineno) |
|
206 if module is None: |
|
207 module = filename or "<unknown>" |
|
208 if module[-3:].lower() == ".py": |
|
209 module = module[:-3] # XXX What about leading pathname? |
|
210 if registry is None: |
|
211 registry = {} |
|
212 if isinstance(message, Warning): |
|
213 text = str(message) |
|
214 category = message.__class__ |
|
215 else: |
|
216 text = message |
|
217 message = category(message) |
|
218 key = (text, category, lineno) |
|
219 # Quick test for common case |
|
220 if registry.get(key): |
|
221 return |
|
222 # Search the filters |
|
223 for item in filters: |
|
224 action, msg, cat, mod, ln = item |
|
225 if ((msg is None or msg.match(text)) and |
|
226 issubclass(category, cat) and |
|
227 (mod is None or mod.match(module)) and |
|
228 (ln == 0 or lineno == ln)): |
|
229 break |
|
230 else: |
|
231 action = defaultaction |
|
232 # Early exit actions |
|
233 if action == "ignore": |
|
234 registry[key] = 1 |
|
235 return |
|
236 |
|
237 # Prime the linecache for formatting, in case the |
|
238 # "file" is actually in a zipfile or something. |
|
239 linecache.getlines(filename, module_globals) |
|
240 |
|
241 if action == "error": |
|
242 raise message |
|
243 # Other actions |
|
244 if action == "once": |
|
245 registry[key] = 1 |
|
246 oncekey = (text, category) |
|
247 if onceregistry.get(oncekey): |
|
248 return |
|
249 onceregistry[oncekey] = 1 |
|
250 elif action == "always": |
|
251 pass |
|
252 elif action == "module": |
|
253 registry[key] = 1 |
|
254 altkey = (text, category, 0) |
|
255 if registry.get(altkey): |
|
256 return |
|
257 registry[altkey] = 1 |
|
258 elif action == "default": |
|
259 registry[key] = 1 |
|
260 else: |
|
261 # Unrecognized actions are errors |
|
262 raise RuntimeError( |
|
263 "Unrecognized action (%r) in warnings.filters:\n %s" % |
|
264 (action, item)) |
|
265 # Warn if showwarning() does not support the 'line' argument. |
|
266 # Don't use 'inspect' as it relies on an extension module, which break the |
|
267 # build thanks to 'warnings' being imported by setup.py. |
|
268 fxn_code = None |
|
269 if hasattr(showwarning, 'func_code'): |
|
270 fxn_code = showwarning.func_code |
|
271 elif hasattr(showwarning, '__func__'): |
|
272 fxn_code = showwarning.__func__.func_code |
|
273 if fxn_code: |
|
274 args = fxn_code.co_varnames[:fxn_code.co_argcount] |
|
275 CO_VARARGS = 0x4 |
|
276 if 'line' not in args and not fxn_code.co_flags & CO_VARARGS: |
|
277 showwarning_msg = ("functions overriding warnings.showwarning() " |
|
278 "must support the 'line' argument") |
|
279 if message == showwarning_msg: |
|
280 _show_warning(message, category, filename, lineno) |
|
281 else: |
|
282 warn(showwarning_msg, DeprecationWarning) |
|
283 # Print message and context |
|
284 showwarning(message, category, filename, lineno) |
|
285 |
|
286 |
|
287 class WarningMessage(object): |
|
288 |
|
289 """Holds the result of a single showwarning() call.""" |
|
290 |
|
291 _WARNING_DETAILS = ("message", "category", "filename", "lineno", "file", |
|
292 "line") |
|
293 |
|
294 def __init__(self, message, category, filename, lineno, file=None, |
|
295 line=None): |
|
296 local_values = locals() |
|
297 for attr in self._WARNING_DETAILS: |
|
298 setattr(self, attr, local_values[attr]) |
|
299 self._category_name = category.__name__ if category else None |
|
300 |
|
301 def __str__(self): |
|
302 return ("{message : %r, category : %r, filename : %r, lineno : %s, " |
|
303 "line : %r}" % (self.message, self._category_name, |
|
304 self.filename, self.lineno, self.line)) |
|
305 |
|
306 |
|
307 class catch_warnings(object): |
|
308 |
|
309 """A context manager that copies and restores the warnings filter upon |
|
310 exiting the context. |
|
311 |
|
312 The 'record' argument specifies whether warnings should be captured by a |
|
313 custom implementation of warnings.showwarning() and be appended to a list |
|
314 returned by the context manager. Otherwise None is returned by the context |
|
315 manager. The objects appended to the list are arguments whose attributes |
|
316 mirror the arguments to showwarning(). |
|
317 |
|
318 The 'module' argument is to specify an alternative module to the module |
|
319 named 'warnings' and imported under that name. This argument is only useful |
|
320 when testing the warnings module itself. |
|
321 |
|
322 """ |
|
323 |
|
324 def __init__(self, record=False, module=None): |
|
325 """Specify whether to record warnings and if an alternative module |
|
326 should be used other than sys.modules['warnings']. |
|
327 |
|
328 For compatibility with Python 3.0, please consider all arguments to be |
|
329 keyword-only. |
|
330 |
|
331 """ |
|
332 self._record = record |
|
333 self._module = sys.modules['warnings'] if module is None else module |
|
334 self._entered = False |
|
335 |
|
336 def __repr__(self): |
|
337 args = [] |
|
338 if self._record: |
|
339 args.append("record=True") |
|
340 if self._module is not sys.modules['warnings']: |
|
341 args.append("module=%r" % self._module) |
|
342 name = type(self).__name__ |
|
343 return "%s(%s)" % (name, ", ".join(args)) |
|
344 |
|
345 def __enter__(self): |
|
346 if self._entered: |
|
347 raise RuntimeError("Cannot enter %r twice" % self) |
|
348 self._entered = True |
|
349 self._filters = self._module.filters |
|
350 self._module.filters = self._filters[:] |
|
351 self._showwarning = self._module.showwarning |
|
352 if self._record: |
|
353 log = [] |
|
354 def showwarning(*args, **kwargs): |
|
355 log.append(WarningMessage(*args, **kwargs)) |
|
356 self._module.showwarning = showwarning |
|
357 return log |
|
358 else: |
|
359 return None |
|
360 |
|
361 def __exit__(self, *exc_info): |
|
362 if not self._entered: |
|
363 raise RuntimeError("Cannot exit %r without entering first" % self) |
|
364 self._module.filters = self._filters |
|
365 self._module.showwarning = self._showwarning |
|
366 |
|
367 |
|
368 # filters contains a sequence of filter 5-tuples |
|
369 # The components of the 5-tuple are: |
|
370 # - an action: error, ignore, always, default, module, or once |
|
371 # - a compiled regex that must match the warning message |
|
372 # - a class representing the warning category |
|
373 # - a compiled regex that must match the module that is being warned |
|
374 # - a line number for the line being warning, or 0 to mean any line |
|
375 # If either if the compiled regexs are None, match anything. |
|
376 _warnings_defaults = False |
|
377 try: |
|
378 from _warnings import (filters, default_action, once_registry, |
|
379 warn, warn_explicit) |
|
380 defaultaction = default_action |
|
381 onceregistry = once_registry |
|
382 _warnings_defaults = True |
|
383 except ImportError: |
|
384 filters = [] |
|
385 defaultaction = "default" |
|
386 onceregistry = {} |
|
387 |
|
388 |
|
389 # Module initialization |
|
390 _processoptions(sys.warnoptions) |
|
391 if not _warnings_defaults: |
|
392 simplefilter("ignore", category=PendingDeprecationWarning, append=1) |
|
393 simplefilter("ignore", category=ImportWarning, append=1) |
|
394 bytes_warning = sys.flags.bytes_warning |
|
395 if bytes_warning > 1: |
|
396 bytes_action = "error" |
|
397 elif bytes_warning: |
|
398 bytes_action = "default" |
|
399 else: |
|
400 bytes_action = "ignore" |
|
401 simplefilter(bytes_action, category=BytesWarning, append=1) |
|
402 del _warnings_defaults |