• 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, long)) and lineno >= 0, \
88           "lineno must be an int >= 0"
89    item = (action, re.compile(message, re.I), category,
90            re.compile(module), int(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, long)) and lineno >= 0, \
109           "lineno must be an int >= 0"
110    item = (action, None, category, None, int(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        self.message = message
313        self.category = category
314        self.filename = filename
315        self.lineno = lineno
316        self.file = file
317        self.line = line
318        self._category_name = category.__name__ if category else None
319
320    def __str__(self):
321        return ("{message : %r, category : %r, filename : %r, lineno : %s, "
322                    "line : %r}" % (self.message, self._category_name,
323                                    self.filename, self.lineno, self.line))
324
325
326class catch_warnings(object):
327
328    """A context manager that copies and restores the warnings filter upon
329    exiting the context.
330
331    The 'record' argument specifies whether warnings should be captured by a
332    custom implementation of warnings.showwarning() and be appended to a list
333    returned by the context manager. Otherwise None is returned by the context
334    manager. The objects appended to the list are arguments whose attributes
335    mirror the arguments to showwarning().
336
337    The 'module' argument is to specify an alternative module to the module
338    named 'warnings' and imported under that name. This argument is only useful
339    when testing the warnings module itself.
340
341    """
342
343    def __init__(self, record=False, module=None):
344        """Specify whether to record warnings and if an alternative module
345        should be used other than sys.modules['warnings'].
346
347        For compatibility with Python 3.0, please consider all arguments to be
348        keyword-only.
349
350        """
351        self._record = record
352        self._module = sys.modules['warnings'] if module is None else module
353        self._entered = False
354
355    def __repr__(self):
356        args = []
357        if self._record:
358            args.append("record=True")
359        if self._module is not sys.modules['warnings']:
360            args.append("module=%r" % self._module)
361        name = type(self).__name__
362        return "%s(%s)" % (name, ", ".join(args))
363
364    def __enter__(self):
365        if self._entered:
366            raise RuntimeError("Cannot enter %r twice" % self)
367        self._entered = True
368        self._filters = self._module.filters
369        self._module.filters = self._filters[:]
370        self._showwarning = self._module.showwarning
371        if self._record:
372            log = []
373            def showwarning(*args, **kwargs):
374                log.append(WarningMessage(*args, **kwargs))
375            self._module.showwarning = showwarning
376            return log
377        else:
378            return None
379
380    def __exit__(self, *exc_info):
381        if not self._entered:
382            raise RuntimeError("Cannot exit %r without entering first" % self)
383        self._module.filters = self._filters
384        self._module.showwarning = self._showwarning
385
386
387# filters contains a sequence of filter 5-tuples
388# The components of the 5-tuple are:
389# - an action: error, ignore, always, default, module, or once
390# - a compiled regex that must match the warning message
391# - a class representing the warning category
392# - a compiled regex that must match the module that is being warned
393# - a line number for the line being warning, or 0 to mean any line
394# If either if the compiled regexs are None, match anything.
395_warnings_defaults = False
396try:
397    from _warnings import (filters, default_action, once_registry,
398                            warn, warn_explicit)
399    defaultaction = default_action
400    onceregistry = once_registry
401    _warnings_defaults = True
402except ImportError:
403    filters = []
404    defaultaction = "default"
405    onceregistry = {}
406
407
408# Module initialization
409_processoptions(sys.warnoptions)
410if not _warnings_defaults:
411    silence = [ImportWarning, PendingDeprecationWarning]
412    # Don't silence DeprecationWarning if -3 or -Q was used.
413    if not sys.py3kwarning and not sys.flags.division_warning:
414        silence.append(DeprecationWarning)
415    for cls in silence:
416        simplefilter("ignore", category=cls)
417    bytes_warning = sys.flags.bytes_warning
418    if bytes_warning > 1:
419        bytes_action = "error"
420    elif bytes_warning:
421        bytes_action = "default"
422    else:
423        bytes_action = "ignore"
424    simplefilter(bytes_action, category=BytesWarning, append=1)
425del _warnings_defaults
426