| """Python part of the warnings subsystem.""" |
| |
| import sys, re, types |
| |
| __all__ = ["warn", "showwarning", "formatwarning", "filterwarnings", |
| "resetwarnings"] |
| |
| defaultaction = "default" |
| filters = [] |
| onceregistry = {} |
| |
| def warn(message, category=None, stacklevel=1): |
| """Issue a warning, or maybe ignore it or raise an exception.""" |
| # Check category argument |
| if category is None: |
| category = UserWarning |
| assert issubclass(category, Warning) |
| # Get context information |
| try: |
| caller = sys._getframe(stacklevel) |
| except ValueError: |
| globals = sys.__dict__ |
| lineno = 1 |
| else: |
| globals = caller.f_globals |
| lineno = caller.f_lineno |
| module = globals['__name__'] |
| filename = globals.get('__file__') |
| if filename: |
| fnl = filename.lower() |
| if fnl.endswith(".pyc") or fnl.endswith(".pyo"): |
| filename = filename[:-1] |
| else: |
| if module == "__main__": |
| filename = sys.argv[0] |
| if not filename: |
| filename = module |
| registry = globals.setdefault("__warningregistry__", {}) |
| warn_explicit(message, category, filename, lineno, module, registry) |
| |
| def warn_explicit(message, category, filename, lineno, |
| module=None, registry=None): |
| if module is None: |
| module = filename |
| if module[-3:].lower() == ".py": |
| module = module[:-3] # XXX What about leading pathname? |
| if registry is None: |
| registry = {} |
| key = (message, category, lineno) |
| # Quick test for common case |
| if registry.get(key): |
| return |
| # Search the filters |
| for item in filters: |
| action, msg, cat, mod, ln = item |
| if (msg.match(message) and |
| issubclass(category, cat) and |
| mod.match(module) and |
| (ln == 0 or lineno == ln)): |
| break |
| else: |
| action = defaultaction |
| # Early exit actions |
| if action == "ignore": |
| registry[key] = 1 |
| return |
| if action == "error": |
| raise category(message) |
| # Other actions |
| if action == "once": |
| registry[key] = 1 |
| oncekey = (message, category) |
| if onceregistry.get(oncekey): |
| return |
| onceregistry[oncekey] = 1 |
| elif action == "always": |
| pass |
| elif action == "module": |
| registry[key] = 1 |
| altkey = (message, category, 0) |
| if registry.get(altkey): |
| return |
| registry[altkey] = 1 |
| elif action == "default": |
| registry[key] = 1 |
| else: |
| # Unrecognized actions are errors |
| raise RuntimeError( |
| "Unrecognized action (%s) in warnings.filters:\n %s" % |
| (`action`, str(item))) |
| # Print message and context |
| showwarning(message, category, filename, lineno) |
| |
| def showwarning(message, category, filename, lineno, file=None): |
| """Hook to write a warning to a file; replace if you like.""" |
| if file is None: |
| file = sys.stderr |
| file.write(formatwarning(message, category, filename, lineno)) |
| |
| def formatwarning(message, category, filename, lineno): |
| """Function to format a warning the standard way.""" |
| import linecache |
| s = "%s:%s: %s: %s\n" % (filename, lineno, category.__name__, message) |
| line = linecache.getline(filename, lineno).strip() |
| if line: |
| s = s + " " + line + "\n" |
| return s |
| |
| def filterwarnings(action, message="", category=Warning, module="", lineno=0, |
| append=0): |
| """Insert an entry into the list of warnings filters (at the front). |
| |
| Use assertions to check that all arguments have the right type.""" |
| assert action in ("error", "ignore", "always", "default", "module", |
| "once"), "invalid action: %s" % `action` |
| assert isinstance(message, types.StringType), "message must be a string" |
| assert isinstance(category, types.ClassType), "category must be a class" |
| assert issubclass(category, Warning), "category must be a Warning subclass" |
| assert type(module) is types.StringType, "module must be a string" |
| assert type(lineno) is types.IntType and lineno >= 0, \ |
| "lineno must be an int >= 0" |
| item = (action, re.compile(message, re.I), category, |
| re.compile(module), lineno) |
| if append: |
| filters.append(item) |
| else: |
| filters.insert(0, item) |
| |
| def resetwarnings(): |
| """Reset the list of warnings filters to its default state.""" |
| filters[:] = [] |
| |
| class _OptionError(Exception): |
| """Exception used by option processing helpers.""" |
| pass |
| |
| # Helper to process -W options passed via sys.warnoptions |
| def _processoptions(args): |
| for arg in args: |
| try: |
| _setoption(arg) |
| except _OptionError, msg: |
| print >>sys.stderr, "Invalid -W option ignored:", msg |
| |
| # Helper for _processoptions() |
| def _setoption(arg): |
| parts = arg.split(':') |
| if len(parts) > 5: |
| raise _OptionError("too many fields (max 5): %s" % `arg`) |
| while len(parts) < 5: |
| parts.append('') |
| action, message, category, module, lineno = [s.strip() |
| for s in parts] |
| action = _getaction(action) |
| message = re.escape(message) |
| category = _getcategory(category) |
| module = re.escape(module) |
| if module: |
| module = module + '$' |
| if lineno: |
| try: |
| lineno = int(lineno) |
| if lineno < 0: |
| raise ValueError |
| except (ValueError, OverflowError): |
| raise _OptionError("invalid lineno %s" % `lineno`) |
| else: |
| lineno = 0 |
| filterwarnings(action, message, category, module, lineno) |
| |
| # Helper for _setoption() |
| def _getaction(action): |
| if not action: |
| return "default" |
| if action == "all": return "always" # Alias |
| for a in ['default', 'always', 'ignore', 'module', 'once', 'error']: |
| if a.startswith(action): |
| return a |
| raise _OptionError("invalid action: %s" % `action`) |
| |
| # Helper for _setoption() |
| def _getcategory(category): |
| if not category: |
| return Warning |
| if re.match("^[a-zA-Z0-9_]+$", category): |
| try: |
| cat = eval(category) |
| except NameError: |
| raise _OptionError("unknown warning category: %s" % `category`) |
| else: |
| i = category.rfind(".") |
| module = category[:i] |
| klass = category[i+1:] |
| try: |
| m = __import__(module, None, None, [klass]) |
| except ImportError: |
| raise _OptionError("invalid module name: %s" % `module`) |
| try: |
| cat = getattr(m, klass) |
| except AttributeError: |
| raise _OptionError("unknown warning category: %s" % `category`) |
| if (not isinstance(cat, types.ClassType) or |
| not issubclass(cat, Warning)): |
| raise _OptionError("invalid warning category: %s" % `category`) |
| return cat |
| |
| # Self-test |
| def _test(): |
| import getopt |
| testoptions = [] |
| try: |
| opts, args = getopt.getopt(sys.argv[1:], "W:") |
| except getopt.error, msg: |
| print >>sys.stderr, msg |
| return |
| for o, a in opts: |
| testoptions.append(a) |
| try: |
| _processoptions(testoptions) |
| except _OptionError, msg: |
| print >>sys.stderr, msg |
| return |
| for item in filters: print item |
| hello = "hello world" |
| warn(hello); warn(hello); warn(hello); warn(hello) |
| warn(hello, UserWarning) |
| warn(hello, DeprecationWarning) |
| for i in range(3): |
| warn(hello) |
| filterwarnings("error", "", Warning, "", 0) |
| try: |
| warn(hello) |
| except Exception, msg: |
| print "Caught", msg.__class__.__name__ + ":", msg |
| else: |
| print "No exception" |
| resetwarnings() |
| try: |
| filterwarnings("booh", "", Warning, "", 0) |
| except Exception, msg: |
| print "Caught", msg.__class__.__name__ + ":", msg |
| else: |
| print "No exception" |
| |
| # Module initialization |
| if __name__ == "__main__": |
| import __main__ |
| sys.modules['warnings'] = __main__ |
| _test() |
| else: |
| _processoptions(sys.warnoptions) |