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