diff options
Diffstat (limited to 'Lib/test/test_optparse.py')
| -rw-r--r-- | Lib/test/test_optparse.py | 406 | 
1 files changed, 214 insertions, 192 deletions
diff --git a/Lib/test/test_optparse.py b/Lib/test/test_optparse.py index 883194e..c02dd10 100644 --- a/Lib/test/test_optparse.py +++ b/Lib/test/test_optparse.py @@ -17,10 +17,37 @@ from cStringIO import StringIO  from pprint import pprint  from test import test_support -from optparse import (make_option, Option, IndentedHelpFormatter, -     TitledHelpFormatter, OptionParser, OptionContainer, OptionGroup, -     SUPPRESS_HELP, SUPPRESS_USAGE, OptionError, OptionConflictError, -     BadOptionError, OptionValueError, _match_abbrev) +from optparse import make_option, Option, IndentedHelpFormatter, \ +     TitledHelpFormatter, OptionParser, OptionContainer, OptionGroup, \ +     SUPPRESS_HELP, SUPPRESS_USAGE, OptionError, OptionConflictError, \ +     BadOptionError, OptionValueError, Values, _match_abbrev + +# Do the right thing with boolean values for all known Python versions. +try: +    True, False +except NameError: +    (True, False) = (1, 0) + + +class InterceptedError(Exception): +    def __init__(self, +                 error_message=None, +                 exit_status=None, +                 exit_message=None): +        self.error_message = error_message +        self.exit_status = exit_status +        self.exit_message = exit_message + +    def __str__(self): +        return self.error_message or self.exit_message or "intercepted error" + +class InterceptingOptionParser(OptionParser): +    def exit(self, status=0, msg=None): +        raise InterceptedError(exit_status=status, exit_message=msg) + +    def error(self, msg): +        raise InterceptedError(error_message=msg) +  class BaseTest(unittest.TestCase):      def assertParseOK(self, args, expected_opts, expected_positional_args): @@ -58,12 +85,11 @@ Args were %(args)s.""" % locals ())                       args,                       kwargs,                       expected_exception, -                     expected_output, -                     get_output=None, -                     exact_match=False): -        """Assert the expected exception is raised when calling a function. - -        Also check whether the right error message is given for a given error. +                     expected_message): +        """ +        Assert that the expected exception is raised when calling a +        function, and that the right error message is included with +        that exception.          Arguments:            func -- the function to call @@ -71,9 +97,6 @@ Args were %(args)s.""" % locals ())            kwargs -- keyword arguments to `func`            expected_exception -- exception that should be raised            expected_output -- output we expect to see -          get_output -- function to call to get the output -          exact_match -- whether output must exactly match expected output, -            or merely contain it          Returns the exception raised for further testing.          """ @@ -81,25 +104,18 @@ Args were %(args)s.""" % locals ())              args = ()          if kwargs is None:              kwargs = {} -        if get_output is None: -            get_output = self.exception          try: -            out = func(*args, **kwargs) +            func(*args, **kwargs)          except expected_exception, err: -            actual_output = get_output(err) - -            if exact_match: -                match = actual_output == expected_exception -            else: -                match = actual_output.find(expected_output) != -1 - -            self.assert_(match, -                         """mismatched output -expected output: -'''%(expected_output)s''' -actual output: -'''%(actual_output)s''' +            actual_message = str(err) +            self.assertEqual(actual_message, +                             expected_message, +                             """\ +expected exception message: +'''%(expected_message)s''' +actual exception message: +'''%(actual_message)s'''  """ % locals())              return err @@ -110,44 +126,46 @@ with args %(args)r  and kwargs %(kwargs)r  """ % locals ()) -    # -- Functions to be used as the get_output argument to assertRaises ------ - -    def exception(self, err): -        return str(err) - -    def redirected_stdout(self, err): -        return sys.stdout.getvalue() - -    def redirected_stderr(self, err): -        return sys.stderr.getvalue()      # -- Assertions used in more than one class --------------------      def assertParseFail(self, cmdline_args, expected_output): -        """Assert the parser fails with the expected message.""" -        save_stderr = sys.stderr +        """ +        Assert the parser fails with the expected message.  Caller +        must ensure that self.parser is an InterceptingOptionParser. +        """          try: -            sys.stderr = StringIO() -            self.assertRaises(self.parser.parse_args, (cmdline_args,), None, -                              SystemExit, expected_output, -                              self.redirected_stderr) -        finally: -            sys.stderr = save_stderr +            self.parser.parse_args(cmdline_args) +        except InterceptedError, err: +            self.assertEqual(err.error_message, expected_output) +        else: +            self.assertFalse("expected parse failure") -    def assertStdoutEquals(self, cmdline_args, expected_output): +    def assertOutput(self, +                     cmdline_args, +                     expected_output, +                     expected_status=0, +                     expected_error=None):          """Assert the parser prints the expected output on stdout."""          save_stdout = sys.stdout          try: -            sys.stdout = StringIO() -            self.assertRaises(self.parser.parse_args, (cmdline_args,), None, -                              SystemExit, expected_output, -                              self.redirected_stdout) -        finally: -            sys.stdout = save_stdout +            try: +                sys.stdout = StringIO() +                self.parser.parse_args(cmdline_args) +            finally: +                output = sys.stdout.getvalue() +                sys.stdout = save_stdout + +        except InterceptedError, err: +            self.assertEqual(output, expected_output) +            self.assertEqual(err.exit_status, expected_status) +            self.assertEqual(err.exit_message, expected_error) +        else: +            self.assertFalse("expected parser.exit()") -    def assertTypeError(self, func, expected_output, *args): -        """Assert a TypeError is raised when executing func.""" -        self.assertRaises(func, args, None, TypeError, expected_output) +    def assertTypeError(self, func, expected_message, *args): +        """Assert that TypeError is raised when executing func.""" +        self.assertRaises(func, args, None, TypeError, expected_message)      def assertHelp(self, parser, expected_help):          actual_help = parser.format_help() @@ -159,120 +177,133 @@ and kwargs %(kwargs)r  # -- Test make_option() aka Option ------------------------------------- -# It's not necessary to test correct options here. All the tests in the +# It's not necessary to test correct options here.  All the tests in the  # parser.parse_args() section deal with those, because they're needed -# there. Duplication makes no sense to me. +# there.  class TestOptionChecks(BaseTest):      def setUp(self):          self.parser = OptionParser(usage=SUPPRESS_USAGE) -    def assertOptionError(self, expected_output, args=[], kwargs={}): +    def assertOptionError(self, expected_message, args=[], kwargs={}):          self.assertRaises(make_option, args, kwargs, -                          OptionError, expected_output) +                          OptionError, expected_message)      def test_opt_string_empty(self):          self.assertTypeError(make_option,                               "at least one option string must be supplied")      def test_opt_string_too_short(self): -        self.assertOptionError("invalid option string 'b': " -                               "must be at least two characters long", -                               ["b"]) +        self.assertOptionError( +            "invalid option string 'b': must be at least two characters long", +            ["b"])      def test_opt_string_short_invalid(self): -        self.assertOptionError("invalid short option string '--': must be " -                               "of the form -x, (x any non-dash char)", -                               ["--"]) +        self.assertOptionError( +            "invalid short option string '--': must be " +            "of the form -x, (x any non-dash char)", +            ["--"])      def test_opt_string_long_invalid(self): -        self.assertOptionError("invalid long option string '---': " -                               "must start with --, followed by non-dash", -                               ["---"]) +        self.assertOptionError( +            "invalid long option string '---': " +            "must start with --, followed by non-dash", +            ["---"])      def test_attr_invalid(self): -        self.assertOptionError("invalid keyword arguments: foo, bar", -                               ["-b"], {'foo': None, 'bar': None}) +        self.assertOptionError( +            "option -b: invalid keyword arguments: foo, bar", +            ["-b"], {'foo': None, 'bar': None})      def test_action_invalid(self): -        self.assertOptionError("invalid action: 'foo'", -                               ["-b"], {'action': 'foo'}) +        self.assertOptionError( +            "option -b: invalid action: 'foo'", +            ["-b"], {'action': 'foo'})      def test_type_invalid(self): -        self.assertOptionError("invalid option type: 'foo'", -                               ["-b"], {'type': 'foo'}) -        self.assertOptionError("invalid option type: 'tuple'", -                               ["-b"], {'type': tuple}) +        self.assertOptionError( +            "option -b: invalid option type: 'foo'", +            ["-b"], {'type': 'foo'}) +        self.assertOptionError( +            "option -b: invalid option type: 'tuple'", +            ["-b"], {'type': tuple})      def test_no_type_for_action(self): -        self.assertOptionError("must not supply a type for action 'count'", -                               ["-b"], {'action': 'count', 'type': 'int'}) +        self.assertOptionError( +            "option -b: must not supply a type for action 'count'", +            ["-b"], {'action': 'count', 'type': 'int'})      def test_no_choices_list(self): -        self.assertOptionError("must supply a list of " -                               "choices for type 'choice'", -                               ["-b", "--bad"], {'type': "choice"}) +        self.assertOptionError( +            "option -b/--bad: must supply a list of " +            "choices for type 'choice'", +            ["-b", "--bad"], {'type': "choice"})      def test_bad_choices_list(self):          typename = type('').__name__ -        self.assertOptionError("choices must be a list of " -                               "strings ('%s' supplied)" % typename, -                               ["-b", "--bad"], -                               {'type': "choice", 'choices':"bad choices"}) +        self.assertOptionError( +            "option -b/--bad: choices must be a list of " +            "strings ('%s' supplied)" % typename, +            ["-b", "--bad"], +            {'type': "choice", 'choices':"bad choices"})      def test_no_choices_for_type(self): -        self.assertOptionError("must not supply choices for type 'int'", -                               ["-b"], {'type': 'int', 'choices':"bad"}) +        self.assertOptionError( +            "option -b: must not supply choices for type 'int'", +            ["-b"], {'type': 'int', 'choices':"bad"})      def test_no_const_for_action(self): -        self.assertOptionError("'const' must not be supplied for action " -                               "'store'", -                               ["-b"], {'action': 'store', 'const': 1}) +        self.assertOptionError( +            "option -b: 'const' must not be supplied for action 'store'", +            ["-b"], {'action': 'store', 'const': 1})      def test_no_nargs_for_action(self): -        self.assertOptionError("'nargs' must not be supplied for action " -                               "'count'", -                               ["-b"], {'action': 'count', 'nargs': 2}) +        self.assertOptionError( +            "option -b: 'nargs' must not be supplied for action 'count'", +            ["-b"], {'action': 'count', 'nargs': 2})      def test_callback_not_callable(self): -        self.assertOptionError("callback not callable: 'foo'", -                               ["-b"], {'action': 'callback', -                                        'callback': 'foo'}) +        self.assertOptionError( +            "option -b: callback not callable: 'foo'", +            ["-b"], {'action': 'callback', +                     'callback': 'foo'})      def dummy(self):          pass      def test_callback_args_no_tuple(self): -        self.assertOptionError("callback_args, if supplied, must be a tuple: " -                               "not 'foo'", -                               ["-b"], {'action': 'callback', -                                        'callback': self.dummy, -                                        'callback_args': 'foo'}) +        self.assertOptionError( +            "option -b: callback_args, if supplied, " +            "must be a tuple: not 'foo'", +            ["-b"], {'action': 'callback', +                     'callback': self.dummy, +                     'callback_args': 'foo'})      def test_callback_kwargs_no_dict(self): -        self.assertOptionError("callback_kwargs, if supplied, must be a dict: " -                               "not 'foo'", -                               ["-b"], {'action': 'callback', -                                        'callback': self.dummy, -                                        'callback_kwargs': 'foo'}) +        self.assertOptionError( +            "option -b: callback_kwargs, if supplied, " +            "must be a dict: not 'foo'", +            ["-b"], {'action': 'callback', +                     'callback': self.dummy, +                     'callback_kwargs': 'foo'})      def test_no_callback_for_action(self): -        self.assertOptionError("callback supplied ('foo') for " -                               "non-callback option", -                               ["-b"], {'action': 'store', -                                        'callback': 'foo'}) +        self.assertOptionError( +            "option -b: callback supplied ('foo') for non-callback option", +            ["-b"], {'action': 'store', +                     'callback': 'foo'})      def test_no_callback_args_for_action(self): -        self.assertOptionError("callback_args supplied for non-callback " -                               "option", -                               ["-b"], {'action': 'store', -                                        'callback_args': 'foo'}) +        self.assertOptionError( +            "option -b: callback_args supplied for non-callback option", +            ["-b"], {'action': 'store', +                     'callback_args': 'foo'})      def test_no_callback_kwargs_for_action(self): -        self.assertOptionError("callback_kwargs supplied for non-callback " -                               "option", -                               ["-b"], {'action': 'store', -                                        'callback_kwargs': 'foo'}) +        self.assertOptionError( +            "option -b: callback_kwargs supplied for non-callback option", +            ["-b"], {'action': 'store', +                     'callback_kwargs': 'foo'})  class TestOptionParser(BaseTest):      def setUp(self): @@ -335,6 +366,27 @@ class TestOptionParser(BaseTest):          self.assertRaises(self.parser.remove_option, ('foo',), None,                            ValueError, "no such option 'foo'") +class TestOptionValues(BaseTest): +    def setUp(self): +        pass + +    def test_basics(self): +        values = Values() +        self.assertEqual(vars(values), {}) +        self.assertEqual(values, {}) +        self.assertNotEqual(values, {"foo": "bar"}) +        self.assertNotEqual(values, "") + +        dict = {"foo": "bar", "baz": 42} +        values = Values(defaults=dict) +        self.assertEqual(vars(values), dict) +        self.assertEqual(values, dict) +        self.assertNotEqual(values, {"foo": "bar"}) +        self.assertNotEqual(values, {}) +        self.assertNotEqual(values, "") +        self.assertNotEqual(values, []) + +  class TestTypeAliases(BaseTest):      def setUp(self):          self.parser = OptionParser() @@ -346,7 +398,7 @@ class TestTypeAliases(BaseTest):          self.assertEquals(self.parser.get_option("-x").type, "int")          self.assertEquals(self.parser.get_option("-s").type, "string")          self.assertEquals(self.parser.get_option("-t").type, "string") - +          # Custom type for testing processing of default values.  _time_units = { 's' : 1, 'm' : 60, 'h' : 60*60, 'd' : 60*60*24 } @@ -434,17 +486,16 @@ class TestProgName(BaseTest):          # Make sure that program name taken from sys.argv[0] by default.          save_argv = sys.argv[:]          try: -            # XXX Should the path be hard-coding forward-slashes? -            sys.argv[0] = "/foo/bar/baz.py" +            sys.argv[0] = os.path.join("foo", "bar", "baz.py")              parser = OptionParser("usage: %prog ...", version="%prog 1.2")              expected_usage = "usage: baz.py ...\n"              self.assertUsage(parser, expected_usage)              self.assertVersion(parser, "baz.py 1.2")              self.assertHelp(parser, -                expected_usage + "\n" + -                "options:\n" -                "  --version   show program's version number and exit\n" -                "  -h, --help  show this help message and exit\n") +                            expected_usage + "\n" + +                            "options:\n" +                            "  --version   show program's version number and exit\n" +                            "  -h, --help  show this help message and exit\n")          finally:              sys.argv[:] = save_argv @@ -503,7 +554,7 @@ options:                                 default=None,                                 help=self.file_help)          self.assertHelp(self.parser, self.expected_help_none) - +              def test_default_none_2(self):          self.parser.add_option("-f", "--file",                                 help=self.file_help) @@ -544,7 +595,8 @@ class TestStandard(BaseTest):                     make_option("-b", "--boo", type="int", dest='boo'),                     make_option("--foo", action="append")] -        self.parser = OptionParser(usage=SUPPRESS_USAGE, option_list=options) +        self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE, +                                               option_list=options)      def test_required_value(self):          self.assertParseFail(["-a"], "-a option requires an argument") @@ -707,7 +759,7 @@ class TestBool(BaseTest):  class TestChoice(BaseTest):      def setUp(self): -        self.parser = OptionParser(usage=SUPPRESS_USAGE) +        self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE)          self.parser.add_option("-c", action="store", type="choice",                                 dest="choice", choices=["one", "two", "three"]) @@ -730,7 +782,7 @@ class TestChoice(BaseTest):  class TestCount(BaseTest):      def setUp(self): -        self.parser = OptionParser(usage=SUPPRESS_USAGE) +        self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE)          self.v_opt = make_option("-v", action="count", dest="verbose")          self.parser.add_option(self.v_opt)          self.parser.add_option("--verbose", type="int", dest="verbose") @@ -786,9 +838,9 @@ class TestCount(BaseTest):          self.assertParseOK(["-vvv", "--verbose=2", "-q", "-v"],                             {'verbose': 1}, []) -class TestNArgs(BaseTest): +class TestMultipleArgs(BaseTest):      def setUp(self): -        self.parser = OptionParser(usage=SUPPRESS_USAGE) +        self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE)          self.parser.add_option("-p", "--point",                                 action="store", nargs=3, type="float", dest="point") @@ -811,9 +863,9 @@ class TestNArgs(BaseTest):          self.assertParseFail(["--point", "1.0", "3.5"],                               "--point option requires 3 arguments") -class TestNArgsAppend(BaseTest): +class TestMultipleArgsAppend(BaseTest):      def setUp(self): -        self.parser = OptionParser(usage=SUPPRESS_USAGE) +        self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE)          self.parser.add_option("-p", "--point", action="store", nargs=3,                                 type="float", dest="point")          self.parser.add_option("-f", "--foo", action="append", nargs=2, @@ -835,14 +887,17 @@ class TestNArgsAppend(BaseTest):  class TestVersion(BaseTest):      def test_version(self): -        oldargv = sys.argv[0] -        sys.argv[0] = "./foo/bar" -        self.parser = OptionParser(usage=SUPPRESS_USAGE, version="%prog 0.1") -        self.assertStdoutEquals(["--version"], "bar 0.1\n") -        sys.argv[0] = oldargv +        self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE, +                                               version="%prog 0.1") +        save_argv = sys.argv[:] +        try: +            sys.argv[0] = os.path.join(os.curdir, "foo", "bar") +            self.assertOutput(["--version"], "bar 0.1\n") +        finally: +            sys.argv[:] = save_argv      def test_no_version(self): -        self.parser = OptionParser(usage=SUPPRESS_USAGE) +        self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE)          self.assertParseFail(["--version"],                               "no such option: --version") @@ -900,8 +955,8 @@ class TestOptionGroup(BaseTest):  class TestExtendAddTypes(BaseTest):      def setUp(self): -        self.parser = OptionParser(usage=SUPPRESS_USAGE, -                                   option_class=self.MyOption) +        self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE, +                                               option_class=self.MyOption)          self.parser.add_option("-a", None, type="string", dest="a")          self.parser.add_option("-f", "--file", type="file", dest="file") @@ -1119,7 +1174,8 @@ class TestCallbackVarArgs(BaseTest):                     make_option("-b", action="store_true", dest="b"),                     make_option("-c", "--callback", action="callback",                                 callback=self.variable_args, dest="c")] -        self.parser = OptionParser(usage=SUPPRESS_USAGE, option_list=options) +        self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE, +                                               option_list=options)      def variable_args (self, option, opt, value, parser):          self.assert_(value is None) @@ -1170,7 +1226,8 @@ class ConflictBase(BaseTest):      def setUp(self):          options = [make_option("-v", "--verbose", action="count",                                 dest="verbose", help="increment verbosity")] -        self.parser = OptionParser(usage=SUPPRESS_USAGE, option_list=options) +        self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE, +                                               option_list=options)      def show_version (self, option, opt, value, parser):          parser.values.show_version = 1 @@ -1201,41 +1258,6 @@ class TestConflict(ConflictBase):              ValueError, "invalid conflict_resolution value 'foo'") -class TestConflictIgnore(ConflictBase): -    """Test the old (Optik <= 1.1 behaviour) -- arguably broken, but -    still available so should be tested. -    """ - -    def setUp(self): -        ConflictBase.setUp(self) -        self.parser.set_conflict_handler("ignore") -        self.parser.add_option("-v", "--version", action="callback", -                          callback=self.show_version, help="show version") - -    def test_conflict_ignore(self): -        v_opt = self.parser.get_option("-v") -        verbose_opt = self.parser.get_option("--verbose") -        version_opt = self.parser.get_option("--version") - -        self.assert_(v_opt is version_opt) -        self.assert_(v_opt is not verbose_opt) -        self.assertEqual(v_opt._long_opts, ["--version"]) -        self.assertEqual(version_opt._short_opts, ["-v"]) -        self.assertEqual(verbose_opt._short_opts, ["-v"]) - -    def test_conflict_ignore_help(self): -        self.assertStdoutEquals(["-h"], """\ -options: -  -v, --verbose  increment verbosity -  -h, --help     show this help message and exit -  -v, --version  show version -""") - -    def test_conflict_ignore_short_opt(self): -        self.assertParseOK(["-v"], -                           {'show_version': 1, 'verbose': None}, -                           []) -  class TestConflictResolve(ConflictBase):      def setUp(self):          ConflictBase.setUp(self) @@ -1257,7 +1279,7 @@ class TestConflictResolve(ConflictBase):          self.assertEqual(verbose_opt._long_opts, ["--verbose"])      def test_conflict_resolve_help(self): -        self.assertStdoutEquals(["-h"], """\ +        self.assertOutput(["-h"], """\  options:    --verbose      increment verbosity    -h, --help     show this help message and exit @@ -1281,7 +1303,7 @@ options:  class TestConflictOverride(BaseTest):      def setUp(self): -        self.parser = OptionParser(usage=SUPPRESS_USAGE) +        self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE)          self.parser.set_conflict_handler("resolve")          self.parser.add_option("-n", "--dry-run",                                 action="store_true", dest="dry_run", @@ -1296,7 +1318,7 @@ class TestConflictOverride(BaseTest):          self.assertEqual(opt._long_opts, ["--dry-run"])      def test_conflict_override_help(self): -        self.assertStdoutEquals(["-h"], """\ +        self.assertOutput(["-h"], """\  options:    -h, --help     show this help message and exit    -n, --dry-run  dry run mode @@ -1375,16 +1397,16 @@ class TestHelp(BaseTest):                          help="store FOO in the foo list for later fooing"),              ]          os.environ['COLUMNS'] = str(columns) -        return OptionParser(option_list=options) +        return InterceptingOptionParser(option_list=options)              def assertHelpEquals(self, expected_output): -        # This trick is used to make optparse believe bar.py is being executed. -        oldargv = sys.argv[0] -        sys.argv[0] = "./foo/bar.py" - -        self.assertStdoutEquals(["-h"], expected_output) - -        sys.argv[0] = oldargv +        save_argv = sys.argv[:] +        try: +            # Make optparse believe bar.py is being executed. +            sys.argv[0] = os.path.join("foo", "bar.py") +            self.assertOutput(["-h"], expected_output) +        finally: +            sys.argv[:] = save_argv      def test_help(self):          self.assertHelpEquals(_expected_help_basic) @@ -1441,7 +1463,7 @@ options:  """) - +          class TestMatchAbbrev(BaseTest):      def test_match_abbrev(self):  | 
