PageRenderTime 388ms CodeModel.GetById 141ms app.highlight 98ms RepoModel.GetById 143ms app.codeStats 0ms

/Lib/warnings.py

http://unladen-swallow.googlecode.com/
Python | 402 lines | 383 code | 7 blank | 12 comment | 7 complexity | 76268b537a7101a7d65d02e2277f3f99 MD5 | raw file
  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.
  6import linecache
  7import sys
  8import types
  9
 10__all__ = ["warn", "showwarning", "formatwarning", "filterwarnings",
 11           "resetwarnings", "catch_warnings"]
 12
 13
 14def 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
 24def _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.
 34showwarning = _show_warning
 35
 36def 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
 45def 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
 67def 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
 82def resetwarnings():
 83    """Clear the list of warning filters, so that no filters are active."""
 84    filters[:] = []
 85
 86class _OptionError(Exception):
 87    """Exception used by option processing helpers."""
 88    pass
 89
 90# Helper to process -W options passed via sys.warnoptions
 91def _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()
 99def _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()
126def _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()
136def _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
163def 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
203def 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
287class 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
307class 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
377try:
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
383except ImportError:
384    filters = []
385    defaultaction = "default"
386    onceregistry = {}
387
388
389# Module initialization
390_processoptions(sys.warnoptions)
391if 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)
402del _warnings_defaults