Home | History | Annotate | Download | only in Lib
      1 """Python part of the warnings subsystem."""
      2 
      3 import sys
      4 
      5 
      6 __all__ = ["warn", "warn_explicit", "showwarning",
      7            "formatwarning", "filterwarnings", "simplefilter",
      8            "resetwarnings", "catch_warnings"]
      9 
     10 def showwarning(message, category, filename, lineno, file=None, line=None):
     11     """Hook to write a warning to a file; replace if you like."""
     12     msg = WarningMessage(message, category, filename, lineno, file, line)
     13     _showwarnmsg_impl(msg)
     14 
     15 def formatwarning(message, category, filename, lineno, line=None):
     16     """Function to format a warning the standard way."""
     17     msg = WarningMessage(message, category, filename, lineno, None, line)
     18     return _formatwarnmsg_impl(msg)
     19 
     20 def _showwarnmsg_impl(msg):
     21     file = msg.file
     22     if file is None:
     23         file = sys.stderr
     24         if file is None:
     25             # sys.stderr is None when run with pythonw.exe:
     26             # warnings get lost
     27             return
     28     text = _formatwarnmsg(msg)
     29     try:
     30         file.write(text)
     31     except OSError:
     32         # the file (probably stderr) is invalid - this warning gets lost.
     33         pass
     34 
     35 def _formatwarnmsg_impl(msg):
     36     s =  ("%s:%s: %s: %s\n"
     37           % (msg.filename, msg.lineno, msg.category.__name__,
     38              msg.message))
     39 
     40     if msg.line is None:
     41         try:
     42             import linecache
     43             line = linecache.getline(msg.filename, msg.lineno)
     44         except Exception:
     45             # When a warning is logged during Python shutdown, linecache
     46             # and the import machinery don't work anymore
     47             line = None
     48             linecache = None
     49     else:
     50         line = msg.line
     51     if line:
     52         line = line.strip()
     53         s += "  %s\n" % line
     54 
     55     if msg.source is not None:
     56         try:
     57             import tracemalloc
     58             tb = tracemalloc.get_object_traceback(msg.source)
     59         except Exception:
     60             # When a warning is logged during Python shutdown, tracemalloc
     61             # and the import machinery don't work anymore
     62             tb = None
     63 
     64         if tb is not None:
     65             s += 'Object allocated at (most recent call first):\n'
     66             for frame in tb:
     67                 s += ('  File "%s", lineno %s\n'
     68                       % (frame.filename, frame.lineno))
     69 
     70                 try:
     71                     if linecache is not None:
     72                         line = linecache.getline(frame.filename, frame.lineno)
     73                     else:
     74                         line = None
     75                 except Exception:
     76                     line = None
     77                 if line:
     78                     line = line.strip()
     79                     s += '    %s\n' % line
     80     return s
     81 
     82 # Keep a reference to check if the function was replaced
     83 _showwarning_orig = showwarning
     84 
     85 def _showwarnmsg(msg):
     86     """Hook to write a warning to a file; replace if you like."""
     87     try:
     88         sw = showwarning
     89     except NameError:
     90         pass
     91     else:
     92         if sw is not _showwarning_orig:
     93             # warnings.showwarning() was replaced
     94             if not callable(sw):
     95                 raise TypeError("warnings.showwarning() must be set to a "
     96                                 "function or method")
     97 
     98             sw(msg.message, msg.category, msg.filename, msg.lineno,
     99                msg.file, msg.line)
    100             return
    101     _showwarnmsg_impl(msg)
    102 
    103 # Keep a reference to check if the function was replaced
    104 _formatwarning_orig = formatwarning
    105 
    106 def _formatwarnmsg(msg):
    107     """Function to format a warning the standard way."""
    108     try:
    109         fw = formatwarning
    110     except NameError:
    111         pass
    112     else:
    113         if fw is not _formatwarning_orig:
    114             # warnings.formatwarning() was replaced
    115             return fw(msg.message, msg.category,
    116                       msg.filename, msg.lineno, line=msg.line)
    117     return _formatwarnmsg_impl(msg)
    118 
    119 def filterwarnings(action, message="", category=Warning, module="", lineno=0,
    120                    append=False):
    121     """Insert an entry into the list of warnings filters (at the front).
    122 
    123     'action' -- one of "error", "ignore", "always", "default", "module",
    124                 or "once"
    125     'message' -- a regex that the warning message must match
    126     'category' -- a class that the warning must be a subclass of
    127     'module' -- a regex that the module name must match
    128     'lineno' -- an integer line number, 0 matches all warnings
    129     'append' -- if true, append to the list of filters
    130     """
    131     import re
    132     assert action in ("error", "ignore", "always", "default", "module",
    133                       "once"), "invalid action: %r" % (action,)
    134     assert isinstance(message, str), "message must be a string"
    135     assert isinstance(category, type), "category must be a class"
    136     assert issubclass(category, Warning), "category must be a Warning subclass"
    137     assert isinstance(module, str), "module must be a string"
    138     assert isinstance(lineno, int) and lineno >= 0, \
    139            "lineno must be an int >= 0"
    140     _add_filter(action, re.compile(message, re.I), category,
    141             re.compile(module), lineno, append=append)
    142 
    143 def simplefilter(action, category=Warning, lineno=0, append=False):
    144     """Insert a simple entry into the list of warnings filters (at the front).
    145 
    146     A simple filter matches all modules and messages.
    147     'action' -- one of "error", "ignore", "always", "default", "module",
    148                 or "once"
    149     'category' -- a class that the warning must be a subclass of
    150     'lineno' -- an integer line number, 0 matches all warnings
    151     'append' -- if true, append to the list of filters
    152     """
    153     assert action in ("error", "ignore", "always", "default", "module",
    154                       "once"), "invalid action: %r" % (action,)
    155     assert isinstance(lineno, int) and lineno >= 0, \
    156            "lineno must be an int >= 0"
    157     _add_filter(action, None, category, None, lineno, append=append)
    158 
    159 def _add_filter(*item, append):
    160     # Remove possible duplicate filters, so new one will be placed
    161     # in correct place. If append=True and duplicate exists, do nothing.
    162     if not append:
    163         try:
    164             filters.remove(item)
    165         except ValueError:
    166             pass
    167         filters.insert(0, item)
    168     else:
    169         if item not in filters:
    170             filters.append(item)
    171     _filters_mutated()
    172 
    173 def resetwarnings():
    174     """Clear the list of warning filters, so that no filters are active."""
    175     filters[:] = []
    176     _filters_mutated()
    177 
    178 class _OptionError(Exception):
    179     """Exception used by option processing helpers."""
    180     pass
    181 
    182 # Helper to process -W options passed via sys.warnoptions
    183 def _processoptions(args):
    184     for arg in args:
    185         try:
    186             _setoption(arg)
    187         except _OptionError as msg:
    188             print("Invalid -W option ignored:", msg, file=sys.stderr)
    189 
    190 # Helper for _processoptions()
    191 def _setoption(arg):
    192     import re
    193     parts = arg.split(':')
    194     if len(parts) > 5:
    195         raise _OptionError("too many fields (max 5): %r" % (arg,))
    196     while len(parts) < 5:
    197         parts.append('')
    198     action, message, category, module, lineno = [s.strip()
    199                                                  for s in parts]
    200     action = _getaction(action)
    201     message = re.escape(message)
    202     category = _getcategory(category)
    203     module = re.escape(module)
    204     if module:
    205         module = module + '$'
    206     if lineno:
    207         try:
    208             lineno = int(lineno)
    209             if lineno < 0:
    210                 raise ValueError
    211         except (ValueError, OverflowError):
    212             raise _OptionError("invalid lineno %r" % (lineno,))
    213     else:
    214         lineno = 0
    215     filterwarnings(action, message, category, module, lineno)
    216 
    217 # Helper for _setoption()
    218 def _getaction(action):
    219     if not action:
    220         return "default"
    221     if action == "all": return "always" # Alias
    222     for a in ('default', 'always', 'ignore', 'module', 'once', 'error'):
    223         if a.startswith(action):
    224             return a
    225     raise _OptionError("invalid action: %r" % (action,))
    226 
    227 # Helper for _setoption()
    228 def _getcategory(category):
    229     import re
    230     if not category:
    231         return Warning
    232     if re.match("^[a-zA-Z0-9_]+$", category):
    233         try:
    234             cat = eval(category)
    235         except NameError:
    236             raise _OptionError("unknown warning category: %r" % (category,))
    237     else:
    238         i = category.rfind(".")
    239         module = category[:i]
    240         klass = category[i+1:]
    241         try:
    242             m = __import__(module, None, None, [klass])
    243         except ImportError:
    244             raise _OptionError("invalid module name: %r" % (module,))
    245         try:
    246             cat = getattr(m, klass)
    247         except AttributeError:
    248             raise _OptionError("unknown warning category: %r" % (category,))
    249     if not issubclass(cat, Warning):
    250         raise _OptionError("invalid warning category: %r" % (category,))
    251     return cat
    252 
    253 
    254 def _is_internal_frame(frame):
    255     """Signal whether the frame is an internal CPython implementation detail."""
    256     filename = frame.f_code.co_filename
    257     return 'importlib' in filename and '_bootstrap' in filename
    258 
    259 
    260 def _next_external_frame(frame):
    261     """Find the next frame that doesn't involve CPython internals."""
    262     frame = frame.f_back
    263     while frame is not None and _is_internal_frame(frame):
    264         frame = frame.f_back
    265     return frame
    266 
    267 
    268 # Code typically replaced by _warnings
    269 def warn(message, category=None, stacklevel=1, source=None):
    270     """Issue a warning, or maybe ignore it or raise an exception."""
    271     # Check if message is already a Warning object
    272     if isinstance(message, Warning):
    273         category = message.__class__
    274     # Check category argument
    275     if category is None:
    276         category = UserWarning
    277     if not (isinstance(category, type) and issubclass(category, Warning)):
    278         raise TypeError("category must be a Warning subclass, "
    279                         "not '{:s}'".format(type(category).__name__))
    280     # Get context information
    281     try:
    282         if stacklevel <= 1 or _is_internal_frame(sys._getframe(1)):
    283             # If frame is too small to care or if the warning originated in
    284             # internal code, then do not try to hide any frames.
    285             frame = sys._getframe(stacklevel)
    286         else:
    287             frame = sys._getframe(1)
    288             # Look for one frame less since the above line starts us off.
    289             for x in range(stacklevel-1):
    290                 frame = _next_external_frame(frame)
    291                 if frame is None:
    292                     raise ValueError
    293     except ValueError:
    294         globals = sys.__dict__
    295         lineno = 1
    296     else:
    297         globals = frame.f_globals
    298         lineno = frame.f_lineno
    299     if '__name__' in globals:
    300         module = globals['__name__']
    301     else:
    302         module = "<string>"
    303     filename = globals.get('__file__')
    304     if filename:
    305         fnl = filename.lower()
    306         if fnl.endswith(".pyc"):
    307             filename = filename[:-1]
    308     else:
    309         if module == "__main__":
    310             try:
    311                 filename = sys.argv[0]
    312             except AttributeError:
    313                 # embedded interpreters don't have sys.argv, see bug #839151
    314                 filename = '__main__'
    315         if not filename:
    316             filename = module
    317     registry = globals.setdefault("__warningregistry__", {})
    318     warn_explicit(message, category, filename, lineno, module, registry,
    319                   globals, source)
    320 
    321 def warn_explicit(message, category, filename, lineno,
    322                   module=None, registry=None, module_globals=None,
    323                   source=None):
    324     lineno = int(lineno)
    325     if module is None:
    326         module = filename or "<unknown>"
    327         if module[-3:].lower() == ".py":
    328             module = module[:-3] # XXX What about leading pathname?
    329     if registry is None:
    330         registry = {}
    331     if registry.get('version', 0) != _filters_version:
    332         registry.clear()
    333         registry['version'] = _filters_version
    334     if isinstance(message, Warning):
    335         text = str(message)
    336         category = message.__class__
    337     else:
    338         text = message
    339         message = category(message)
    340     key = (text, category, lineno)
    341     # Quick test for common case
    342     if registry.get(key):
    343         return
    344     # Search the filters
    345     for item in filters:
    346         action, msg, cat, mod, ln = item
    347         if ((msg is None or msg.match(text)) and
    348             issubclass(category, cat) and
    349             (mod is None or mod.match(module)) and
    350             (ln == 0 or lineno == ln)):
    351             break
    352     else:
    353         action = defaultaction
    354     # Early exit actions
    355     if action == "ignore":
    356         registry[key] = 1
    357         return
    358 
    359     # Prime the linecache for formatting, in case the
    360     # "file" is actually in a zipfile or something.
    361     import linecache
    362     linecache.getlines(filename, module_globals)
    363 
    364     if action == "error":
    365         raise message
    366     # Other actions
    367     if action == "once":
    368         registry[key] = 1
    369         oncekey = (text, category)
    370         if onceregistry.get(oncekey):
    371             return
    372         onceregistry[oncekey] = 1
    373     elif action == "always":
    374         pass
    375     elif action == "module":
    376         registry[key] = 1
    377         altkey = (text, category, 0)
    378         if registry.get(altkey):
    379             return
    380         registry[altkey] = 1
    381     elif action == "default":
    382         registry[key] = 1
    383     else:
    384         # Unrecognized actions are errors
    385         raise RuntimeError(
    386               "Unrecognized action (%r) in warnings.filters:\n %s" %
    387               (action, item))
    388     # Print message and context
    389     msg = WarningMessage(message, category, filename, lineno, source)
    390     _showwarnmsg(msg)
    391 
    392 
    393 class WarningMessage(object):
    394 
    395     _WARNING_DETAILS = ("message", "category", "filename", "lineno", "file",
    396                         "line", "source")
    397 
    398     def __init__(self, message, category, filename, lineno, file=None,
    399                  line=None, source=None):
    400         local_values = locals()
    401         for attr in self._WARNING_DETAILS:
    402             setattr(self, attr, local_values[attr])
    403         self._category_name = category.__name__ if category else None
    404 
    405     def __str__(self):
    406         return ("{message : %r, category : %r, filename : %r, lineno : %s, "
    407                     "line : %r}" % (self.message, self._category_name,
    408                                     self.filename, self.lineno, self.line))
    409 
    410 
    411 class catch_warnings(object):
    412 
    413     """A context manager that copies and restores the warnings filter upon
    414     exiting the context.
    415 
    416     The 'record' argument specifies whether warnings should be captured by a
    417     custom implementation of warnings.showwarning() and be appended to a list
    418     returned by the context manager. Otherwise None is returned by the context
    419     manager. The objects appended to the list are arguments whose attributes
    420     mirror the arguments to showwarning().
    421 
    422     The 'module' argument is to specify an alternative module to the module
    423     named 'warnings' and imported under that name. This argument is only useful
    424     when testing the warnings module itself.
    425 
    426     """
    427 
    428     def __init__(self, *, record=False, module=None):
    429         """Specify whether to record warnings and if an alternative module
    430         should be used other than sys.modules['warnings'].
    431 
    432         For compatibility with Python 3.0, please consider all arguments to be
    433         keyword-only.
    434 
    435         """
    436         self._record = record
    437         self._module = sys.modules['warnings'] if module is None else module
    438         self._entered = False
    439 
    440     def __repr__(self):
    441         args = []
    442         if self._record:
    443             args.append("record=True")
    444         if self._module is not sys.modules['warnings']:
    445             args.append("module=%r" % self._module)
    446         name = type(self).__name__
    447         return "%s(%s)" % (name, ", ".join(args))
    448 
    449     def __enter__(self):
    450         if self._entered:
    451             raise RuntimeError("Cannot enter %r twice" % self)
    452         self._entered = True
    453         self._filters = self._module.filters
    454         self._module.filters = self._filters[:]
    455         self._module._filters_mutated()
    456         self._showwarning = self._module.showwarning
    457         self._showwarnmsg_impl = self._module._showwarnmsg_impl
    458         if self._record:
    459             log = []
    460             self._module._showwarnmsg_impl = log.append
    461             # Reset showwarning() to the default implementation to make sure
    462             # that _showwarnmsg() calls _showwarnmsg_impl()
    463             self._module.showwarning = self._module._showwarning_orig
    464             return log
    465         else:
    466             return None
    467 
    468     def __exit__(self, *exc_info):
    469         if not self._entered:
    470             raise RuntimeError("Cannot exit %r without entering first" % self)
    471         self._module.filters = self._filters
    472         self._module._filters_mutated()
    473         self._module.showwarning = self._showwarning
    474         self._module._showwarnmsg_impl = self._showwarnmsg_impl
    475 
    476 
    477 # filters contains a sequence of filter 5-tuples
    478 # The components of the 5-tuple are:
    479 # - an action: error, ignore, always, default, module, or once
    480 # - a compiled regex that must match the warning message
    481 # - a class representing the warning category
    482 # - a compiled regex that must match the module that is being warned
    483 # - a line number for the line being warning, or 0 to mean any line
    484 # If either if the compiled regexs are None, match anything.
    485 _warnings_defaults = False
    486 try:
    487     from _warnings import (filters, _defaultaction, _onceregistry,
    488                            warn, warn_explicit, _filters_mutated)
    489     defaultaction = _defaultaction
    490     onceregistry = _onceregistry
    491     _warnings_defaults = True
    492 except ImportError:
    493     filters = []
    494     defaultaction = "default"
    495     onceregistry = {}
    496 
    497     _filters_version = 1
    498 
    499     def _filters_mutated():
    500         global _filters_version
    501         _filters_version += 1
    502 
    503 
    504 # Module initialization
    505 _processoptions(sys.warnoptions)
    506 if not _warnings_defaults:
    507     silence = [ImportWarning, PendingDeprecationWarning]
    508     silence.append(DeprecationWarning)
    509     for cls in silence:
    510         simplefilter("ignore", category=cls)
    511     bytes_warning = sys.flags.bytes_warning
    512     if bytes_warning > 1:
    513         bytes_action = "error"
    514     elif bytes_warning:
    515         bytes_action = "default"
    516     else:
    517         bytes_action = "ignore"
    518     simplefilter(bytes_action, category=BytesWarning, append=1)
    519     # resource usage warnings are enabled by default in pydebug mode
    520     if hasattr(sys, 'gettotalrefcount'):
    521         resource_action = "always"
    522     else:
    523         resource_action = "ignore"
    524     simplefilter(resource_action, category=ResourceWarning, append=1)
    525 
    526 del _warnings_defaults
    527