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