diff -r 6fdc5e3daa8c -r af0017b39039 Doc/library/warnings.rst --- a/Doc/library/warnings.rst Fri Nov 30 16:15:24 2012 -0500 +++ b/Doc/library/warnings.rst Sat Dec 01 02:01:32 2012 +0100 @@ -382,6 +382,31 @@ and calls to :func:`simplefilter`. +.. function:: parse_option(option) + + Parse a colon-separated filter definition, as described for command line + option :option:`-W`, and insert the filter. If the filter definition is + malformed, a :exc:`WarningsOptionParsingException` is raised. Along with + :mod:`argparse`, it is useful to control warnings from the command line + of a program.:: + + import argparse, warnings + + parser = argparse.ArgumentParser(description='Mimic python -W behaviour.') + parser.add_argument('-W', dest='warnings', action='append', metavar='arg', + help='warning control; arg is action:message:category:module:lineno') + + args = parser.parse_args() + + if args.warnings: + for arg in args.warnings: + try: + warnings.parse_option(arg) + except WarningsOptionParseError as msg: + print("Invalid -W option ignored:", msg, file=sys.stderr) + + + Available Context Managers -------------------------- @@ -408,3 +433,10 @@ :func:`showwarning` function and internal list of filter specifications. This means the context manager is modifying global state and therefore is not thread-safe. + +Exceptions +---------- + +.. exception:: WarningsOptionParsingError + + Raised by :func:`parse_option` if the option is malformed. diff -r 6fdc5e3daa8c -r af0017b39039 Lib/test/test_warnings.py --- a/Lib/test/test_warnings.py Fri Nov 30 16:15:24 2012 -0500 +++ b/Lib/test/test_warnings.py Sat Dec 01 02:01:32 2012 +0100 @@ -381,17 +381,17 @@ class WCmdLineTests(unittest.TestCase): - def test_improper_input(self): - # Uses the private _setoption() function to test the parsing - # of command-line warning arguments + def test_option_parsing(self): + # check if the opt args are properly parsed + self.module.resetwarnings() with original_warnings.catch_warnings(module=self.module): - self.assertRaises(self.module._OptionError, - self.module._setoption, '1:2:3:4:5:6') - self.assertRaises(self.module._OptionError, - self.module._setoption, 'bogus::Warning') - self.assertRaises(self.module._OptionError, - self.module._setoption, 'ignore:2::4:-5') - self.module._setoption('error::Warning::0') + self.assertRaises(self.module.WarningsOptionParsingError, + self.module.parse_option, '1:2:3:4:5:6') + self.assertRaises(self.module.WarningsOptionParsingError, + self.module.parse_option, 'bogus::Warning') + self.assertRaises(self.module.WarningsOptionParsingError, + self.module.parse_option, 'ignore:2::4:-5') + self.module.parse_option('error::Warning::0') self.assertRaises(UserWarning, self.module.warn, 'convert to error') def test_improper_option(self): @@ -410,6 +410,7 @@ self.assertFalse(out.strip()) self.assertNotIn(b'RuntimeWarning', err) + class CWCmdLineTests(BaseTest, WCmdLineTests): module = c_warnings diff -r 6fdc5e3daa8c -r af0017b39039 Lib/warnings.py --- a/Lib/warnings.py Fri Nov 30 16:15:24 2012 -0500 +++ b/Lib/warnings.py Sat Dec 01 02:01:32 2012 +0100 @@ -80,24 +80,31 @@ """Clear the list of warning filters, so that no filters are active.""" filters[:] = [] -class _OptionError(Exception): + +class WarningsOptionParsingError(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 as msg: + parse_option(arg) + except WarningsOptionParsingError as msg: print("Invalid -W option ignored:", msg, file=sys.stderr) -# Helper for _processoptions() -def _setoption(arg): +def parse_option(option): + """Parse a colon-separated warning filter definition and insert it. + + The filter definition must match python -W option format. + + """ import re - parts = arg.split(':') + parts = option.split(':') if len(parts) > 5: - raise _OptionError("too many fields (max 5): %r" % (arg,)) + raise WarningsOptionParsingError("too many fields (max 5): %r" % + (option,)) while len(parts) < 5: parts.append('') action, message, category, module, lineno = [s.strip() @@ -114,12 +121,12 @@ if lineno < 0: raise ValueError except (ValueError, OverflowError): - raise _OptionError("invalid lineno %r" % (lineno,)) + raise WarningsOptionParsingError("invalid lineno %r" % (lineno,)) else: lineno = 0 filterwarnings(action, message, category, module, lineno) -# Helper for _setoption() +# Helper for parse_option() def _getaction(action): if not action: return "default" @@ -127,9 +134,9 @@ for a in ('default', 'always', 'ignore', 'module', 'once', 'error'): if a.startswith(action): return a - raise _OptionError("invalid action: %r" % (action,)) + raise WarningsOptionParsingError("invalid action: %r" % (action,)) -# Helper for _setoption() +# Helper for parse_option() def _getcategory(category): import re if not category: @@ -138,7 +145,8 @@ try: cat = eval(category) except NameError: - raise _OptionError("unknown warning category: %r" % (category,)) + raise WarningsOptionParsingError("unknown warning category: %r" % + (category,)) else: i = category.rfind(".") module = category[:i] @@ -146,13 +154,16 @@ try: m = __import__(module, None, None, [klass]) except ImportError: - raise _OptionError("invalid module name: %r" % (module,)) + raise WarningsOptionParsingError("invalid module name: %r" % + (module,)) try: cat = getattr(m, klass) except AttributeError: - raise _OptionError("unknown warning category: %r" % (category,)) + raise WarningsOptionParsingError("unknown warning category: %r" % + (category,)) if not issubclass(cat, Warning): - raise _OptionError("invalid warning category: %r" % (category,)) + raise WarningsOptionParsingError("invalid warning category: %r" % + (category,)) return cat