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