Home | History | Annotate | Download | only in Lib
      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.

      6 import linecache
      7 import sys
      8 import types
      9 
     10 __all__ = ["warn", "warn_explicit", "showwarning",
     11            "formatwarning", "filterwarnings", "simplefilter",
     12            "resetwarnings", "catch_warnings"]
     13 
     14 
     15 def 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 
     25 def _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.

     38 showwarning = _show_warning
     39 
     40 def 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 
     49 def 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 
     78 def 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 
     98 def resetwarnings():
     99     """Clear the list of warning filters, so that no filters are active."""
    100     filters[:] = []
    101 
    102 class _OptionError(Exception):
    103     """Exception used by option processing helpers."""
    104     pass
    105 
    106 # Helper to process -W options passed via sys.warnoptions

    107 def _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()

    115 def _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()

    142 def _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()

    152 def _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

    179 def 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 
    219 def 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 
    285 class 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 
    305 class 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
    375 try:
    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
    381 except ImportError:
    382     filters = []
    383     defaultaction = "default"
    384     onceregistry = {}
    385 
    386 
    387 # Module initialization

    388 _processoptions(sys.warnoptions)
    389 if 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)
    404 del _warnings_defaults
    405