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