Home | History | Annotate | Download | only in python2.7
      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", "showwarning", "formatwarning", "filterwarnings",
     11            "resetwarnings", "catch_warnings"]
     12 
     13 
     14 def 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 
     24 def _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.
     34 showwarning = _show_warning
     35 
     36 def 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 
     45 def 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 
     74 def 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 
     94 def resetwarnings():
     95     """Clear the list of warning filters, so that no filters are active."""
     96     filters[:] = []
     97 
     98 class _OptionError(Exception):
     99     """Exception used by option processing helpers."""
    100     pass
    101 
    102 # Helper to process -W options passed via sys.warnoptions
    103 def _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()
    111 def _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()
    138 def _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()
    148 def _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
    175 def 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 
    215 def 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 
    281 class 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 
    301 class 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
    371 try:
    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
    377 except ImportError:
    378     filters = []
    379     defaultaction = "default"
    380     onceregistry = {}
    381 
    382 
    383 # Module initialization
    384 _processoptions(sys.warnoptions)
    385 if 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)
    400 del _warnings_defaults
    401