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