Move test.test_support.catch_warning() to the warnings module, rename it
[python.git] / Lib / warnings.py
blobb699c439cf7c972ab1e0fca78aa992c10fa38bff
1 """Python part of the warnings subsystem."""
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
10 __all__ = ["warn", "showwarning", "formatwarning", "filterwarnings",
11 "resetwarnings"]
14 def warnpy3k(message, category=None, stacklevel=1):
15 """Issue a deprecation warning for Python 3.x related changes.
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)
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
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
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).
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)
67 def simplefilter(action, category=Warning, lineno=0, append=0):
68 """Insert a simple entry into the list of warnings filters (at the front).
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)
82 def resetwarnings():
83 """Clear the list of warning filters, so that no filters are active."""
84 filters[:] = []
86 class _OptionError(Exception):
87 """Exception used by option processing helpers."""
88 pass
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
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)
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,))
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
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)
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
237 # Prime the linecache for formatting, in case the
238 # "file" is actually in a zipfile or something.
239 linecache.getlines(filename, module_globals)
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)
287 class WarningMessage(object):
289 """Holds the result of a single showwarning() call."""
291 _WARNING_DETAILS = ("message", "category", "filename", "lineno", "file",
292 "line")
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
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))
307 class WarningsRecorder(list):
309 """Record the result of various showwarning() calls."""
311 # Explicitly stated arguments so as to not trigger DeprecationWarning
312 # about adding 'line'.
313 def showwarning(self, *args, **kwargs):
314 self.append(WarningMessage(*args, **kwargs))
316 def __getattr__(self, attr):
317 return getattr(self[-1], attr)
319 def reset(self):
320 del self[:]
323 class catch_warnings(object):
325 """Guard the warnings filter from being permanently changed and optionally
326 record the details of any warnings that are issued.
328 Context manager returns an instance of warnings.WarningRecorder which is a
329 list of WarningMessage instances. Attributes on WarningRecorder are
330 redirected to the last created WarningMessage instance.
334 def __init__(self, record=False, module=None):
335 """Specify whether to record warnings and if an alternative module
336 should be used other than sys.modules['warnings'].
338 For compatibility with Python 3.0, please consider all arguments to be
339 keyword-only.
342 self._recorder = WarningsRecorder() if record else None
343 self._module = sys.modules['warnings'] if module is None else module
345 def __enter__(self):
346 self._filters = self._module.filters
347 self._module.filters = self._filters[:]
348 self._showwarning = self._module.showwarning
349 if self._recorder is not None:
350 self._recorder.reset() # In case the instance is being reused.
351 self._module.showwarning = self._recorder.showwarning
352 return self._recorder
354 def __exit__(self, *exc_info):
355 self._module.filters = self._filters
356 self._module.showwarning = self._showwarning
359 # filters contains a sequence of filter 5-tuples
360 # The components of the 5-tuple are:
361 # - an action: error, ignore, always, default, module, or once
362 # - a compiled regex that must match the warning message
363 # - a class representing the warning category
364 # - a compiled regex that must match the module that is being warned
365 # - a line number for the line being warning, or 0 to mean any line
366 # If either if the compiled regexs are None, match anything.
367 _warnings_defaults = False
368 try:
369 from _warnings import (filters, default_action, once_registry,
370 warn, warn_explicit)
371 defaultaction = default_action
372 onceregistry = once_registry
373 _warnings_defaults = True
374 except ImportError:
375 filters = []
376 defaultaction = "default"
377 onceregistry = {}
380 # Module initialization
381 _processoptions(sys.warnoptions)
382 if not _warnings_defaults:
383 simplefilter("ignore", category=PendingDeprecationWarning, append=1)
384 simplefilter("ignore", category=ImportWarning, append=1)
385 bytes_warning = sys.flags.bytes_warning
386 if bytes_warning > 1:
387 bytes_action = "error"
388 elif bytes_warning:
389 bytes_action = "default"
390 else:
391 bytes_action = "ignore"
392 simplefilter(bytes_action, category=BytesWarning, append=1)
393 del _warnings_defaults