Home | History | Annotate | Download | only in test
      1 #

      2 # Test suite for Optik.  Supplied by Johannes Gijsbers

      3 # (taradino (at] softhome.net) -- translated from the original Optik

      4 # test suite to this PyUnit-based version.

      5 #

      6 # $Id$

      7 #

      8 
      9 import sys
     10 import os
     11 import re
     12 import copy
     13 import types
     14 import unittest
     15 
     16 from StringIO import StringIO
     17 from test import test_support
     18 
     19 
     20 from optparse import make_option, Option, \
     21      TitledHelpFormatter, OptionParser, OptionGroup, \
     22      SUPPRESS_USAGE, OptionError, OptionConflictError, \
     23      BadOptionError, OptionValueError, Values
     24 from optparse import _match_abbrev
     25 from optparse import _parse_num
     26 
     27 retype = type(re.compile(''))
     28 
     29 class InterceptedError(Exception):
     30     def __init__(self,
     31                  error_message=None,
     32                  exit_status=None,
     33                  exit_message=None):
     34         self.error_message = error_message
     35         self.exit_status = exit_status
     36         self.exit_message = exit_message
     37 
     38     def __str__(self):
     39         return self.error_message or self.exit_message or "intercepted error"
     40 
     41 class InterceptingOptionParser(OptionParser):
     42     def exit(self, status=0, msg=None):
     43         raise InterceptedError(exit_status=status, exit_message=msg)
     44 
     45     def error(self, msg):
     46         raise InterceptedError(error_message=msg)
     47 
     48 
     49 class BaseTest(unittest.TestCase):
     50     def assertParseOK(self, args, expected_opts, expected_positional_args):
     51         """Assert the options are what we expected when parsing arguments.
     52 
     53         Otherwise, fail with a nicely formatted message.
     54 
     55         Keyword arguments:
     56         args -- A list of arguments to parse with OptionParser.
     57         expected_opts -- The options expected.
     58         expected_positional_args -- The positional arguments expected.
     59 
     60         Returns the options and positional args for further testing.
     61         """
     62 
     63         (options, positional_args) = self.parser.parse_args(args)
     64         optdict = vars(options)
     65 
     66         self.assertEqual(optdict, expected_opts,
     67                          """
     68 Options are %(optdict)s.
     69 Should be %(expected_opts)s.
     70 Args were %(args)s.""" % locals())
     71 
     72         self.assertEqual(positional_args, expected_positional_args,
     73                          """
     74 Positional arguments are %(positional_args)s.
     75 Should be %(expected_positional_args)s.
     76 Args were %(args)s.""" % locals ())
     77 
     78         return (options, positional_args)
     79 
     80     def assertRaises(self,
     81                      func,
     82                      args,
     83                      kwargs,
     84                      expected_exception,
     85                      expected_message):
     86         """
     87         Assert that the expected exception is raised when calling a
     88         function, and that the right error message is included with
     89         that exception.
     90 
     91         Arguments:
     92           func -- the function to call
     93           args -- positional arguments to `func`
     94           kwargs -- keyword arguments to `func`
     95           expected_exception -- exception that should be raised
     96           expected_message -- expected exception message (or pattern
     97             if a compiled regex object)
     98 
     99         Returns the exception raised for further testing.
    100         """
    101         if args is None:
    102             args = ()
    103         if kwargs is None:
    104             kwargs = {}
    105 
    106         try:
    107             func(*args, **kwargs)
    108         except expected_exception, err:
    109             actual_message = str(err)
    110             if isinstance(expected_message, retype):
    111                 self.assertTrue(expected_message.search(actual_message),
    112                              """\
    113 expected exception message pattern:
    114 /%s/
    115 actual exception message:
    116 '''%s'''
    117 """ % (expected_message.pattern, actual_message))
    118             else:
    119                 self.assertEqual(actual_message,
    120                                  expected_message,
    121                                  """\
    122 expected exception message:
    123 '''%s'''
    124 actual exception message:
    125 '''%s'''
    126 """ % (expected_message, actual_message))
    127 
    128             return err
    129         else:
    130             self.fail("""expected exception %(expected_exception)s not raised
    131 called %(func)r
    132 with args %(args)r
    133 and kwargs %(kwargs)r
    134 """ % locals ())
    135 
    136 
    137     # -- Assertions used in more than one class --------------------

    138 
    139     def assertParseFail(self, cmdline_args, expected_output):
    140         """
    141         Assert the parser fails with the expected message.  Caller
    142         must ensure that self.parser is an InterceptingOptionParser.
    143         """
    144         try:
    145             self.parser.parse_args(cmdline_args)
    146         except InterceptedError, err:
    147             self.assertEqual(err.error_message, expected_output)
    148         else:
    149             self.assertFalse("expected parse failure")
    150 
    151     def assertOutput(self,
    152                      cmdline_args,
    153                      expected_output,
    154                      expected_status=0,
    155                      expected_error=None):
    156         """Assert the parser prints the expected output on stdout."""
    157         save_stdout = sys.stdout
    158         encoding = getattr(save_stdout, 'encoding', None)
    159         try:
    160             try:
    161                 sys.stdout = StringIO()
    162                 if encoding:
    163                     sys.stdout.encoding = encoding
    164                 self.parser.parse_args(cmdline_args)
    165             finally:
    166                 output = sys.stdout.getvalue()
    167                 sys.stdout = save_stdout
    168 
    169         except InterceptedError, err:
    170             self.assertTrue(
    171                 type(output) is types.StringType,
    172                 "expected output to be an ordinary string, not %r"
    173                 % type(output))
    174 
    175             if output != expected_output:
    176                 self.fail("expected: \n'''\n" + expected_output +
    177                           "'''\nbut got \n'''\n" + output + "'''")
    178             self.assertEqual(err.exit_status, expected_status)
    179             self.assertEqual(err.exit_message, expected_error)
    180         else:
    181             self.assertFalse("expected parser.exit()")
    182 
    183     def assertTypeError(self, func, expected_message, *args):
    184         """Assert that TypeError is raised when executing func."""
    185         self.assertRaises(func, args, None, TypeError, expected_message)
    186 
    187     def assertHelp(self, parser, expected_help):
    188         actual_help = parser.format_help()
    189         if actual_help != expected_help:
    190             raise self.failureException(
    191                 'help text failure; expected:\n"' +
    192                 expected_help + '"; got:\n"' +
    193                 actual_help + '"\n')
    194 
    195 # -- Test make_option() aka Option -------------------------------------

    196 
    197 # It's not necessary to test correct options here.  All the tests in the

    198 # parser.parse_args() section deal with those, because they're needed

    199 # there.

    200 
    201 class TestOptionChecks(BaseTest):
    202     def setUp(self):
    203         self.parser = OptionParser(usage=SUPPRESS_USAGE)
    204 
    205     def assertOptionError(self, expected_message, args=[], kwargs={}):
    206         self.assertRaises(make_option, args, kwargs,
    207                           OptionError, expected_message)
    208 
    209     def test_opt_string_empty(self):
    210         self.assertTypeError(make_option,
    211                              "at least one option string must be supplied")
    212 
    213     def test_opt_string_too_short(self):
    214         self.assertOptionError(
    215             "invalid option string 'b': must be at least two characters long",
    216             ["b"])
    217 
    218     def test_opt_string_short_invalid(self):
    219         self.assertOptionError(
    220             "invalid short option string '--': must be "
    221             "of the form -x, (x any non-dash char)",
    222             ["--"])
    223 
    224     def test_opt_string_long_invalid(self):
    225         self.assertOptionError(
    226             "invalid long option string '---': "
    227             "must start with --, followed by non-dash",
    228             ["---"])
    229 
    230     def test_attr_invalid(self):
    231         self.assertOptionError(
    232             "option -b: invalid keyword arguments: bar, foo",
    233             ["-b"], {'foo': None, 'bar': None})
    234 
    235     def test_action_invalid(self):
    236         self.assertOptionError(
    237             "option -b: invalid action: 'foo'",
    238             ["-b"], {'action': 'foo'})
    239 
    240     def test_type_invalid(self):
    241         self.assertOptionError(
    242             "option -b: invalid option type: 'foo'",
    243             ["-b"], {'type': 'foo'})
    244         self.assertOptionError(
    245             "option -b: invalid option type: 'tuple'",
    246             ["-b"], {'type': tuple})
    247 
    248     def test_no_type_for_action(self):
    249         self.assertOptionError(
    250             "option -b: must not supply a type for action 'count'",
    251             ["-b"], {'action': 'count', 'type': 'int'})
    252 
    253     def test_no_choices_list(self):
    254         self.assertOptionError(
    255             "option -b/--bad: must supply a list of "
    256             "choices for type 'choice'",
    257             ["-b", "--bad"], {'type': "choice"})
    258 
    259     def test_bad_choices_list(self):
    260         typename = type('').__name__
    261         self.assertOptionError(
    262             "option -b/--bad: choices must be a list of "
    263             "strings ('%s' supplied)" % typename,
    264             ["-b", "--bad"],
    265             {'type': "choice", 'choices':"bad choices"})
    266 
    267     def test_no_choices_for_type(self):
    268         self.assertOptionError(
    269             "option -b: must not supply choices for type 'int'",
    270             ["-b"], {'type': 'int', 'choices':"bad"})
    271 
    272     def test_no_const_for_action(self):
    273         self.assertOptionError(
    274             "option -b: 'const' must not be supplied for action 'store'",
    275             ["-b"], {'action': 'store', 'const': 1})
    276 
    277     def test_no_nargs_for_action(self):
    278         self.assertOptionError(
    279             "option -b: 'nargs' must not be supplied for action 'count'",
    280             ["-b"], {'action': 'count', 'nargs': 2})
    281 
    282     def test_callback_not_callable(self):
    283         self.assertOptionError(
    284             "option -b: callback not callable: 'foo'",
    285             ["-b"], {'action': 'callback',
    286                      'callback': 'foo'})
    287 
    288     def dummy(self):
    289         pass
    290 
    291     def test_callback_args_no_tuple(self):
    292         self.assertOptionError(
    293             "option -b: callback_args, if supplied, "
    294             "must be a tuple: not 'foo'",
    295             ["-b"], {'action': 'callback',
    296                      'callback': self.dummy,
    297                      'callback_args': 'foo'})
    298 
    299     def test_callback_kwargs_no_dict(self):
    300         self.assertOptionError(
    301             "option -b: callback_kwargs, if supplied, "
    302             "must be a dict: not 'foo'",
    303             ["-b"], {'action': 'callback',
    304                      'callback': self.dummy,
    305                      'callback_kwargs': 'foo'})
    306 
    307     def test_no_callback_for_action(self):
    308         self.assertOptionError(
    309             "option -b: callback supplied ('foo') for non-callback option",
    310             ["-b"], {'action': 'store',
    311                      'callback': 'foo'})
    312 
    313     def test_no_callback_args_for_action(self):
    314         self.assertOptionError(
    315             "option -b: callback_args supplied for non-callback option",
    316             ["-b"], {'action': 'store',
    317                      'callback_args': 'foo'})
    318 
    319     def test_no_callback_kwargs_for_action(self):
    320         self.assertOptionError(
    321             "option -b: callback_kwargs supplied for non-callback option",
    322             ["-b"], {'action': 'store',
    323                      'callback_kwargs': 'foo'})
    324 
    325 class TestOptionParser(BaseTest):
    326     def setUp(self):
    327         self.parser = OptionParser()
    328         self.parser.add_option("-v", "--verbose", "-n", "--noisy",
    329                           action="store_true", dest="verbose")
    330         self.parser.add_option("-q", "--quiet", "--silent",
    331                           action="store_false", dest="verbose")
    332 
    333     def test_add_option_no_Option(self):
    334         self.assertTypeError(self.parser.add_option,
    335                              "not an Option instance: None", None)
    336 
    337     def test_add_option_invalid_arguments(self):
    338         self.assertTypeError(self.parser.add_option,
    339                              "invalid arguments", None, None)
    340 
    341     def test_get_option(self):
    342         opt1 = self.parser.get_option("-v")
    343         self.assertIsInstance(opt1, Option)
    344         self.assertEqual(opt1._short_opts, ["-v", "-n"])
    345         self.assertEqual(opt1._long_opts, ["--verbose", "--noisy"])
    346         self.assertEqual(opt1.action, "store_true")
    347         self.assertEqual(opt1.dest, "verbose")
    348 
    349     def test_get_option_equals(self):
    350         opt1 = self.parser.get_option("-v")
    351         opt2 = self.parser.get_option("--verbose")
    352         opt3 = self.parser.get_option("-n")
    353         opt4 = self.parser.get_option("--noisy")
    354         self.assertTrue(opt1 is opt2 is opt3 is opt4)
    355 
    356     def test_has_option(self):
    357         self.assertTrue(self.parser.has_option("-v"))
    358         self.assertTrue(self.parser.has_option("--verbose"))
    359 
    360     def assertTrueremoved(self):
    361         self.assertTrue(self.parser.get_option("-v") is None)
    362         self.assertTrue(self.parser.get_option("--verbose") is None)
    363         self.assertTrue(self.parser.get_option("-n") is None)
    364         self.assertTrue(self.parser.get_option("--noisy") is None)
    365 
    366         self.assertFalse(self.parser.has_option("-v"))
    367         self.assertFalse(self.parser.has_option("--verbose"))
    368         self.assertFalse(self.parser.has_option("-n"))
    369         self.assertFalse(self.parser.has_option("--noisy"))
    370 
    371         self.assertTrue(self.parser.has_option("-q"))
    372         self.assertTrue(self.parser.has_option("--silent"))
    373 
    374     def test_remove_short_opt(self):
    375         self.parser.remove_option("-n")
    376         self.assertTrueremoved()
    377 
    378     def test_remove_long_opt(self):
    379         self.parser.remove_option("--verbose")
    380         self.assertTrueremoved()
    381 
    382     def test_remove_nonexistent(self):
    383         self.assertRaises(self.parser.remove_option, ('foo',), None,
    384                           ValueError, "no such option 'foo'")
    385 
    386     def test_refleak(self):
    387         # If an OptionParser is carrying around a reference to a large

    388         # object, various cycles can prevent it from being GC'd in

    389         # a timely fashion.  destroy() breaks the cycles to ensure stuff

    390         # can be cleaned up.

    391         big_thing = [42]
    392         refcount = sys.getrefcount(big_thing)
    393         parser = OptionParser()
    394         parser.add_option("-a", "--aaarggh")
    395         parser.big_thing = big_thing
    396 
    397         parser.destroy()
    398         #self.assertEqual(refcount, sys.getrefcount(big_thing))

    399         del parser
    400         self.assertEqual(refcount, sys.getrefcount(big_thing))
    401 
    402 
    403 class TestOptionValues(BaseTest):
    404     def setUp(self):
    405         pass
    406 
    407     def test_basics(self):
    408         values = Values()
    409         self.assertEqual(vars(values), {})
    410         self.assertEqual(values, {})
    411         self.assertNotEqual(values, {"foo": "bar"})
    412         self.assertNotEqual(values, "")
    413 
    414         dict = {"foo": "bar", "baz": 42}
    415         values = Values(defaults=dict)
    416         self.assertEqual(vars(values), dict)
    417         self.assertEqual(values, dict)
    418         self.assertNotEqual(values, {"foo": "bar"})
    419         self.assertNotEqual(values, {})
    420         self.assertNotEqual(values, "")
    421         self.assertNotEqual(values, [])
    422 
    423 
    424 class TestTypeAliases(BaseTest):
    425     def setUp(self):
    426         self.parser = OptionParser()
    427 
    428     def test_str_aliases_string(self):
    429         self.parser.add_option("-s", type="str")
    430         self.assertEqual(self.parser.get_option("-s").type, "string")
    431 
    432     def test_new_type_object(self):
    433         self.parser.add_option("-s", type=str)
    434         self.assertEqual(self.parser.get_option("-s").type, "string")
    435         self.parser.add_option("-x", type=int)
    436         self.assertEqual(self.parser.get_option("-x").type, "int")
    437 
    438     def test_old_type_object(self):
    439         self.parser.add_option("-s", type=types.StringType)
    440         self.assertEqual(self.parser.get_option("-s").type, "string")
    441         self.parser.add_option("-x", type=types.IntType)
    442         self.assertEqual(self.parser.get_option("-x").type, "int")
    443 
    444 
    445 # Custom type for testing processing of default values.

    446 _time_units = { 's' : 1, 'm' : 60, 'h' : 60*60, 'd' : 60*60*24 }
    447 
    448 def _check_duration(option, opt, value):
    449     try:
    450         if value[-1].isdigit():
    451             return int(value)
    452         else:
    453             return int(value[:-1]) * _time_units[value[-1]]
    454     except (ValueError, IndexError):
    455         raise OptionValueError(
    456             'option %s: invalid duration: %r' % (opt, value))
    457 
    458 class DurationOption(Option):
    459     TYPES = Option.TYPES + ('duration',)
    460     TYPE_CHECKER = copy.copy(Option.TYPE_CHECKER)
    461     TYPE_CHECKER['duration'] = _check_duration
    462 
    463 class TestDefaultValues(BaseTest):
    464     def setUp(self):
    465         self.parser = OptionParser()
    466         self.parser.add_option("-v", "--verbose", default=True)
    467         self.parser.add_option("-q", "--quiet", dest='verbose')
    468         self.parser.add_option("-n", type="int", default=37)
    469         self.parser.add_option("-m", type="int")
    470         self.parser.add_option("-s", default="foo")
    471         self.parser.add_option("-t")
    472         self.parser.add_option("-u", default=None)
    473         self.expected = { 'verbose': True,
    474                           'n': 37,
    475                           'm': None,
    476                           's': "foo",
    477                           't': None,
    478                           'u': None }
    479 
    480     def test_basic_defaults(self):
    481         self.assertEqual(self.parser.get_default_values(), self.expected)
    482 
    483     def test_mixed_defaults_post(self):
    484         self.parser.set_defaults(n=42, m=-100)
    485         self.expected.update({'n': 42, 'm': -100})
    486         self.assertEqual(self.parser.get_default_values(), self.expected)
    487 
    488     def test_mixed_defaults_pre(self):
    489         self.parser.set_defaults(x="barf", y="blah")
    490         self.parser.add_option("-x", default="frob")
    491         self.parser.add_option("-y")
    492 
    493         self.expected.update({'x': "frob", 'y': "blah"})
    494         self.assertEqual(self.parser.get_default_values(), self.expected)
    495 
    496         self.parser.remove_option("-y")
    497         self.parser.add_option("-y", default=None)
    498         self.expected.update({'y': None})
    499         self.assertEqual(self.parser.get_default_values(), self.expected)
    500 
    501     def test_process_default(self):
    502         self.parser.option_class = DurationOption
    503         self.parser.add_option("-d", type="duration", default=300)
    504         self.parser.add_option("-e", type="duration", default="6m")
    505         self.parser.set_defaults(n="42")
    506         self.expected.update({'d': 300, 'e': 360, 'n': 42})
    507         self.assertEqual(self.parser.get_default_values(), self.expected)
    508 
    509         self.parser.set_process_default_values(False)
    510         self.expected.update({'d': 300, 'e': "6m", 'n': "42"})
    511         self.assertEqual(self.parser.get_default_values(), self.expected)
    512 
    513 
    514 class TestProgName(BaseTest):
    515     """
    516     Test that %prog expands to the right thing in usage, version,
    517     and help strings.
    518     """
    519 
    520     def assertUsage(self, parser, expected_usage):
    521         self.assertEqual(parser.get_usage(), expected_usage)
    522 
    523     def assertVersion(self, parser, expected_version):
    524         self.assertEqual(parser.get_version(), expected_version)
    525 
    526 
    527     def test_default_progname(self):
    528         # Make sure that program name taken from sys.argv[0] by default.

    529         save_argv = sys.argv[:]
    530         try:
    531             sys.argv[0] = os.path.join("foo", "bar", "baz.py")
    532             parser = OptionParser("%prog ...", version="%prog 1.2")
    533             expected_usage = "Usage: baz.py ...\n"
    534             self.assertUsage(parser, expected_usage)
    535             self.assertVersion(parser, "baz.py 1.2")
    536             self.assertHelp(parser,
    537                             expected_usage + "\n" +
    538                             "Options:\n"
    539                             "  --version   show program's version number and exit\n"
    540                             "  -h, --help  show this help message and exit\n")
    541         finally:
    542             sys.argv[:] = save_argv
    543 
    544     def test_custom_progname(self):
    545         parser = OptionParser(prog="thingy",
    546                               version="%prog 0.1",
    547                               usage="%prog arg arg")
    548         parser.remove_option("-h")
    549         parser.remove_option("--version")
    550         expected_usage = "Usage: thingy arg arg\n"
    551         self.assertUsage(parser, expected_usage)
    552         self.assertVersion(parser, "thingy 0.1")
    553         self.assertHelp(parser, expected_usage + "\n")
    554 
    555 
    556 class TestExpandDefaults(BaseTest):
    557     def setUp(self):
    558         self.parser = OptionParser(prog="test")
    559         self.help_prefix = """\
    560 Usage: test [options]
    561 
    562 Options:
    563   -h, --help            show this help message and exit
    564 """
    565         self.file_help = "read from FILE [default: %default]"
    566         self.expected_help_file = self.help_prefix + \
    567             "  -f FILE, --file=FILE  read from FILE [default: foo.txt]\n"
    568         self.expected_help_none = self.help_prefix + \
    569             "  -f FILE, --file=FILE  read from FILE [default: none]\n"
    570 
    571     def test_option_default(self):
    572         self.parser.add_option("-f", "--file",
    573                                default="foo.txt",
    574                                help=self.file_help)
    575         self.assertHelp(self.parser, self.expected_help_file)
    576 
    577     def test_parser_default_1(self):
    578         self.parser.add_option("-f", "--file",
    579                                help=self.file_help)
    580         self.parser.set_default('file', "foo.txt")
    581         self.assertHelp(self.parser, self.expected_help_file)
    582 
    583     def test_parser_default_2(self):
    584         self.parser.add_option("-f", "--file",
    585                                help=self.file_help)
    586         self.parser.set_defaults(file="foo.txt")
    587         self.assertHelp(self.parser, self.expected_help_file)
    588 
    589     def test_no_default(self):
    590         self.parser.add_option("-f", "--file",
    591                                help=self.file_help)
    592         self.assertHelp(self.parser, self.expected_help_none)
    593 
    594     def test_default_none_1(self):
    595         self.parser.add_option("-f", "--file",
    596                                default=None,
    597                                help=self.file_help)
    598         self.assertHelp(self.parser, self.expected_help_none)
    599 
    600     def test_default_none_2(self):
    601         self.parser.add_option("-f", "--file",
    602                                help=self.file_help)
    603         self.parser.set_defaults(file=None)
    604         self.assertHelp(self.parser, self.expected_help_none)
    605 
    606     def test_float_default(self):
    607         self.parser.add_option(
    608             "-p", "--prob",
    609             help="blow up with probability PROB [default: %default]")
    610         self.parser.set_defaults(prob=0.43)
    611         expected_help = self.help_prefix + \
    612             "  -p PROB, --prob=PROB  blow up with probability PROB [default: 0.43]\n"
    613         self.assertHelp(self.parser, expected_help)
    614 
    615     def test_alt_expand(self):
    616         self.parser.add_option("-f", "--file",
    617                                default="foo.txt",
    618                                help="read from FILE [default: *DEFAULT*]")
    619         self.parser.formatter.default_tag = "*DEFAULT*"
    620         self.assertHelp(self.parser, self.expected_help_file)
    621 
    622     def test_no_expand(self):
    623         self.parser.add_option("-f", "--file",
    624                                default="foo.txt",
    625                                help="read from %default file")
    626         self.parser.formatter.default_tag = None
    627         expected_help = self.help_prefix + \
    628             "  -f FILE, --file=FILE  read from %default file\n"
    629         self.assertHelp(self.parser, expected_help)
    630 
    631 
    632 # -- Test parser.parse_args() ------------------------------------------

    633 
    634 class TestStandard(BaseTest):
    635     def setUp(self):
    636         options = [make_option("-a", type="string"),
    637                    make_option("-b", "--boo", type="int", dest='boo'),
    638                    make_option("--foo", action="append")]
    639 
    640         self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE,
    641                                                option_list=options)
    642 
    643     def test_required_value(self):
    644         self.assertParseFail(["-a"], "-a option requires an argument")
    645 
    646     def test_invalid_integer(self):
    647         self.assertParseFail(["-b", "5x"],
    648                              "option -b: invalid integer value: '5x'")
    649 
    650     def test_no_such_option(self):
    651         self.assertParseFail(["--boo13"], "no such option: --boo13")
    652 
    653     def test_long_invalid_integer(self):
    654         self.assertParseFail(["--boo=x5"],
    655                              "option --boo: invalid integer value: 'x5'")
    656 
    657     def test_empty(self):
    658         self.assertParseOK([], {'a': None, 'boo': None, 'foo': None}, [])
    659 
    660     def test_shortopt_empty_longopt_append(self):
    661         self.assertParseOK(["-a", "", "--foo=blah", "--foo="],
    662                            {'a': "", 'boo': None, 'foo': ["blah", ""]},
    663                            [])
    664 
    665     def test_long_option_append(self):
    666         self.assertParseOK(["--foo", "bar", "--foo", "", "--foo=x"],
    667                            {'a': None,
    668                             'boo': None,
    669                             'foo': ["bar", "", "x"]},
    670                            [])
    671 
    672     def test_option_argument_joined(self):
    673         self.assertParseOK(["-abc"],
    674                            {'a': "bc", 'boo': None, 'foo': None},
    675                            [])
    676 
    677     def test_option_argument_split(self):
    678         self.assertParseOK(["-a", "34"],
    679                            {'a': "34", 'boo': None, 'foo': None},
    680                            [])
    681 
    682     def test_option_argument_joined_integer(self):
    683         self.assertParseOK(["-b34"],
    684                            {'a': None, 'boo': 34, 'foo': None},
    685                            [])
    686 
    687     def test_option_argument_split_negative_integer(self):
    688         self.assertParseOK(["-b", "-5"],
    689                            {'a': None, 'boo': -5, 'foo': None},
    690                            [])
    691 
    692     def test_long_option_argument_joined(self):
    693         self.assertParseOK(["--boo=13"],
    694                            {'a': None, 'boo': 13, 'foo': None},
    695                            [])
    696 
    697     def test_long_option_argument_split(self):
    698         self.assertParseOK(["--boo", "111"],
    699                            {'a': None, 'boo': 111, 'foo': None},
    700                            [])
    701 
    702     def test_long_option_short_option(self):
    703         self.assertParseOK(["--foo=bar", "-axyz"],
    704                            {'a': 'xyz', 'boo': None, 'foo': ["bar"]},
    705                            [])
    706 
    707     def test_abbrev_long_option(self):
    708         self.assertParseOK(["--f=bar", "-axyz"],
    709                            {'a': 'xyz', 'boo': None, 'foo': ["bar"]},
    710                            [])
    711 
    712     def test_defaults(self):
    713         (options, args) = self.parser.parse_args([])
    714         defaults = self.parser.get_default_values()
    715         self.assertEqual(vars(defaults), vars(options))
    716 
    717     def test_ambiguous_option(self):
    718         self.parser.add_option("--foz", action="store",
    719                                type="string", dest="foo")
    720         self.assertParseFail(["--f=bar"],
    721                              "ambiguous option: --f (--foo, --foz?)")
    722 
    723 
    724     def test_short_and_long_option_split(self):
    725         self.assertParseOK(["-a", "xyz", "--foo", "bar"],
    726                            {'a': 'xyz', 'boo': None, 'foo': ["bar"]},
    727                            []),
    728 
    729     def test_short_option_split_long_option_append(self):
    730         self.assertParseOK(["--foo=bar", "-b", "123", "--foo", "baz"],
    731                            {'a': None, 'boo': 123, 'foo': ["bar", "baz"]},
    732                            [])
    733 
    734     def test_short_option_split_one_positional_arg(self):
    735         self.assertParseOK(["-a", "foo", "bar"],
    736                            {'a': "foo", 'boo': None, 'foo': None},
    737                            ["bar"]),
    738 
    739     def test_short_option_consumes_separator(self):
    740         self.assertParseOK(["-a", "--", "foo", "bar"],
    741                            {'a': "--", 'boo': None, 'foo': None},
    742                            ["foo", "bar"]),
    743         self.assertParseOK(["-a", "--", "--foo", "bar"],
    744                            {'a': "--", 'boo': None, 'foo': ["bar"]},
    745                            []),
    746 
    747     def test_short_option_joined_and_separator(self):
    748         self.assertParseOK(["-ab", "--", "--foo", "bar"],
    749                            {'a': "b", 'boo': None, 'foo': None},
    750                            ["--foo", "bar"]),
    751 
    752     def test_hyphen_becomes_positional_arg(self):
    753         self.assertParseOK(["-ab", "-", "--foo", "bar"],
    754                            {'a': "b", 'boo': None, 'foo': ["bar"]},
    755                            ["-"])
    756 
    757     def test_no_append_versus_append(self):
    758         self.assertParseOK(["-b3", "-b", "5", "--foo=bar", "--foo", "baz"],
    759                            {'a': None, 'boo': 5, 'foo': ["bar", "baz"]},
    760                            [])
    761 
    762     def test_option_consumes_optionlike_string(self):
    763         self.assertParseOK(["-a", "-b3"],
    764                            {'a': "-b3", 'boo': None, 'foo': None},
    765                            [])
    766 
    767     def test_combined_single_invalid_option(self):
    768         self.parser.add_option("-t", action="store_true")
    769         self.assertParseFail(["-test"],
    770                              "no such option: -e")
    771 
    772 class TestBool(BaseTest):
    773     def setUp(self):
    774         options = [make_option("-v",
    775                                "--verbose",
    776                                action="store_true",
    777                                dest="verbose",
    778                                default=''),
    779                    make_option("-q",
    780                                "--quiet",
    781                                action="store_false",
    782                                dest="verbose")]
    783         self.parser = OptionParser(option_list = options)
    784 
    785     def test_bool_default(self):
    786         self.assertParseOK([],
    787                            {'verbose': ''},
    788                            [])
    789 
    790     def test_bool_false(self):
    791         (options, args) = self.assertParseOK(["-q"],
    792                                              {'verbose': 0},
    793                                              [])
    794         self.assertTrue(options.verbose is False)
    795 
    796     def test_bool_true(self):
    797         (options, args) = self.assertParseOK(["-v"],
    798                                              {'verbose': 1},
    799                                              [])
    800         self.assertTrue(options.verbose is True)
    801 
    802     def test_bool_flicker_on_and_off(self):
    803         self.assertParseOK(["-qvq", "-q", "-v"],
    804                            {'verbose': 1},
    805                            [])
    806 
    807 class TestChoice(BaseTest):
    808     def setUp(self):
    809         self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE)
    810         self.parser.add_option("-c", action="store", type="choice",
    811                                dest="choice", choices=["one", "two", "three"])
    812 
    813     def test_valid_choice(self):
    814         self.assertParseOK(["-c", "one", "xyz"],
    815                            {'choice': 'one'},
    816                            ["xyz"])
    817 
    818     def test_invalid_choice(self):
    819         self.assertParseFail(["-c", "four", "abc"],
    820                              "option -c: invalid choice: 'four' "
    821                              "(choose from 'one', 'two', 'three')")
    822 
    823     def test_add_choice_option(self):
    824         self.parser.add_option("-d", "--default",
    825                                choices=["four", "five", "six"])
    826         opt = self.parser.get_option("-d")
    827         self.assertEqual(opt.type, "choice")
    828         self.assertEqual(opt.action, "store")
    829 
    830 class TestCount(BaseTest):
    831     def setUp(self):
    832         self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE)
    833         self.v_opt = make_option("-v", action="count", dest="verbose")
    834         self.parser.add_option(self.v_opt)
    835         self.parser.add_option("--verbose", type="int", dest="verbose")
    836         self.parser.add_option("-q", "--quiet",
    837                                action="store_const", dest="verbose", const=0)
    838 
    839     def test_empty(self):
    840         self.assertParseOK([], {'verbose': None}, [])
    841 
    842     def test_count_one(self):
    843         self.assertParseOK(["-v"], {'verbose': 1}, [])
    844 
    845     def test_count_three(self):
    846         self.assertParseOK(["-vvv"], {'verbose': 3}, [])
    847 
    848     def test_count_three_apart(self):
    849         self.assertParseOK(["-v", "-v", "-v"], {'verbose': 3}, [])
    850 
    851     def test_count_override_amount(self):
    852         self.assertParseOK(["-vvv", "--verbose=2"], {'verbose': 2}, [])
    853 
    854     def test_count_override_quiet(self):
    855         self.assertParseOK(["-vvv", "--verbose=2", "-q"], {'verbose': 0}, [])
    856 
    857     def test_count_overriding(self):
    858         self.assertParseOK(["-vvv", "--verbose=2", "-q", "-v"],
    859                            {'verbose': 1}, [])
    860 
    861     def test_count_interspersed_args(self):
    862         self.assertParseOK(["--quiet", "3", "-v"],
    863                            {'verbose': 1},
    864                            ["3"])
    865 
    866     def test_count_no_interspersed_args(self):
    867         self.parser.disable_interspersed_args()
    868         self.assertParseOK(["--quiet", "3", "-v"],
    869                            {'verbose': 0},
    870                            ["3", "-v"])
    871 
    872     def test_count_no_such_option(self):
    873         self.assertParseFail(["-q3", "-v"], "no such option: -3")
    874 
    875     def test_count_option_no_value(self):
    876         self.assertParseFail(["--quiet=3", "-v"],
    877                              "--quiet option does not take a value")
    878 
    879     def test_count_with_default(self):
    880         self.parser.set_default('verbose', 0)
    881         self.assertParseOK([], {'verbose':0}, [])
    882 
    883     def test_count_overriding_default(self):
    884         self.parser.set_default('verbose', 0)
    885         self.assertParseOK(["-vvv", "--verbose=2", "-q", "-v"],
    886                            {'verbose': 1}, [])
    887 
    888 class TestMultipleArgs(BaseTest):
    889     def setUp(self):
    890         self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE)
    891         self.parser.add_option("-p", "--point",
    892                                action="store", nargs=3, type="float", dest="point")
    893 
    894     def test_nargs_with_positional_args(self):
    895         self.assertParseOK(["foo", "-p", "1", "2.5", "-4.3", "xyz"],
    896                            {'point': (1.0, 2.5, -4.3)},
    897                            ["foo", "xyz"])
    898 
    899     def test_nargs_long_opt(self):
    900         self.assertParseOK(["--point", "-1", "2.5", "-0", "xyz"],
    901                            {'point': (-1.0, 2.5, -0.0)},
    902                            ["xyz"])
    903 
    904     def test_nargs_invalid_float_value(self):
    905         self.assertParseFail(["-p", "1.0", "2x", "3.5"],
    906                              "option -p: "
    907                              "invalid floating-point value: '2x'")
    908 
    909     def test_nargs_required_values(self):
    910         self.assertParseFail(["--point", "1.0", "3.5"],
    911                              "--point option requires 3 arguments")
    912 
    913 class TestMultipleArgsAppend(BaseTest):
    914     def setUp(self):
    915         self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE)
    916         self.parser.add_option("-p", "--point", action="store", nargs=3,
    917                                type="float", dest="point")
    918         self.parser.add_option("-f", "--foo", action="append", nargs=2,
    919                                type="int", dest="foo")
    920         self.parser.add_option("-z", "--zero", action="append_const",
    921                                dest="foo", const=(0, 0))
    922 
    923     def test_nargs_append(self):
    924         self.assertParseOK(["-f", "4", "-3", "blah", "--foo", "1", "666"],
    925                            {'point': None, 'foo': [(4, -3), (1, 666)]},
    926                            ["blah"])
    927 
    928     def test_nargs_append_required_values(self):
    929         self.assertParseFail(["-f4,3"],
    930                              "-f option requires 2 arguments")
    931 
    932     def test_nargs_append_simple(self):
    933         self.assertParseOK(["--foo=3", "4"],
    934                            {'point': None, 'foo':[(3, 4)]},
    935                            [])
    936 
    937     def test_nargs_append_const(self):
    938         self.assertParseOK(["--zero", "--foo", "3", "4", "-z"],
    939                            {'point': None, 'foo':[(0, 0), (3, 4), (0, 0)]},
    940                            [])
    941 
    942 class TestVersion(BaseTest):
    943     def test_version(self):
    944         self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE,
    945                                                version="%prog 0.1")
    946         save_argv = sys.argv[:]
    947         try:
    948             sys.argv[0] = os.path.join(os.curdir, "foo", "bar")
    949             self.assertOutput(["--version"], "bar 0.1\n")
    950         finally:
    951             sys.argv[:] = save_argv
    952 
    953     def test_no_version(self):
    954         self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE)
    955         self.assertParseFail(["--version"],
    956                              "no such option: --version")
    957 
    958 # -- Test conflicting default values and parser.parse_args() -----------

    959 
    960 class TestConflictingDefaults(BaseTest):
    961     """Conflicting default values: the last one should win."""
    962     def setUp(self):
    963         self.parser = OptionParser(option_list=[
    964             make_option("-v", action="store_true", dest="verbose", default=1)])
    965 
    966     def test_conflict_default(self):
    967         self.parser.add_option("-q", action="store_false", dest="verbose",
    968                                default=0)
    969         self.assertParseOK([], {'verbose': 0}, [])
    970 
    971     def test_conflict_default_none(self):
    972         self.parser.add_option("-q", action="store_false", dest="verbose",
    973                                default=None)
    974         self.assertParseOK([], {'verbose': None}, [])
    975 
    976 class TestOptionGroup(BaseTest):
    977     def setUp(self):
    978         self.parser = OptionParser(usage=SUPPRESS_USAGE)
    979 
    980     def test_option_group_create_instance(self):
    981         group = OptionGroup(self.parser, "Spam")
    982         self.parser.add_option_group(group)
    983         group.add_option("--spam", action="store_true",
    984                          help="spam spam spam spam")
    985         self.assertParseOK(["--spam"], {'spam': 1}, [])
    986 
    987     def test_add_group_no_group(self):
    988         self.assertTypeError(self.parser.add_option_group,
    989                              "not an OptionGroup instance: None", None)
    990 
    991     def test_add_group_invalid_arguments(self):
    992         self.assertTypeError(self.parser.add_option_group,
    993                              "invalid arguments", None, None)
    994 
    995     def test_add_group_wrong_parser(self):
    996         group = OptionGroup(self.parser, "Spam")
    997         group.parser = OptionParser()
    998         self.assertRaises(self.parser.add_option_group, (group,), None,
    999                           ValueError, "invalid OptionGroup (wrong parser)")
   1000 
   1001     def test_group_manipulate(self):
   1002         group = self.parser.add_option_group("Group 2",
   1003                                              description="Some more options")
   1004         group.set_title("Bacon")
   1005         group.add_option("--bacon", type="int")
   1006         self.assertTrue(self.parser.get_option_group("--bacon"), group)
   1007 
   1008 # -- Test extending and parser.parse_args() ----------------------------

   1009 
   1010 class TestExtendAddTypes(BaseTest):
   1011     def setUp(self):
   1012         self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE,
   1013                                                option_class=self.MyOption)
   1014         self.parser.add_option("-a", None, type="string", dest="a")
   1015         self.parser.add_option("-f", "--file", type="file", dest="file")
   1016 
   1017     def tearDown(self):
   1018         if os.path.isdir(test_support.TESTFN):
   1019             os.rmdir(test_support.TESTFN)
   1020         elif os.path.isfile(test_support.TESTFN):
   1021             os.unlink(test_support.TESTFN)
   1022 
   1023     class MyOption (Option):
   1024         def check_file(option, opt, value):
   1025             if not os.path.exists(value):
   1026                 raise OptionValueError("%s: file does not exist" % value)
   1027             elif not os.path.isfile(value):
   1028                 raise OptionValueError("%s: not a regular file" % value)
   1029             return value
   1030 
   1031         TYPES = Option.TYPES + ("file",)
   1032         TYPE_CHECKER = copy.copy(Option.TYPE_CHECKER)
   1033         TYPE_CHECKER["file"] = check_file
   1034 
   1035     def test_filetype_ok(self):
   1036         open(test_support.TESTFN, "w").close()
   1037         self.assertParseOK(["--file", test_support.TESTFN, "-afoo"],
   1038                            {'file': test_support.TESTFN, 'a': 'foo'},
   1039                            [])
   1040 
   1041     def test_filetype_noexist(self):
   1042         self.assertParseFail(["--file", test_support.TESTFN, "-afoo"],
   1043                              "%s: file does not exist" %
   1044                              test_support.TESTFN)
   1045 
   1046     def test_filetype_notfile(self):
   1047         os.mkdir(test_support.TESTFN)
   1048         self.assertParseFail(["--file", test_support.TESTFN, "-afoo"],
   1049                              "%s: not a regular file" %
   1050                              test_support.TESTFN)
   1051 
   1052 
   1053 class TestExtendAddActions(BaseTest):
   1054     def setUp(self):
   1055         options = [self.MyOption("-a", "--apple", action="extend",
   1056                                  type="string", dest="apple")]
   1057         self.parser = OptionParser(option_list=options)
   1058 
   1059     class MyOption (Option):
   1060         ACTIONS = Option.ACTIONS + ("extend",)
   1061         STORE_ACTIONS = Option.STORE_ACTIONS + ("extend",)
   1062         TYPED_ACTIONS = Option.TYPED_ACTIONS + ("extend",)
   1063 
   1064         def take_action(self, action, dest, opt, value, values, parser):
   1065             if action == "extend":
   1066                 lvalue = value.split(",")
   1067                 values.ensure_value(dest, []).extend(lvalue)
   1068             else:
   1069                 Option.take_action(self, action, dest, opt, parser, value,
   1070                                    values)
   1071 
   1072     def test_extend_add_action(self):
   1073         self.assertParseOK(["-afoo,bar", "--apple=blah"],
   1074                            {'apple': ["foo", "bar", "blah"]},
   1075                            [])
   1076 
   1077     def test_extend_add_action_normal(self):
   1078         self.assertParseOK(["-a", "foo", "-abar", "--apple=x,y"],
   1079                            {'apple': ["foo", "bar", "x", "y"]},
   1080                            [])
   1081 
   1082 # -- Test callbacks and parser.parse_args() ----------------------------

   1083 
   1084 class TestCallback(BaseTest):
   1085     def setUp(self):
   1086         options = [make_option("-x",
   1087                                None,
   1088                                action="callback",
   1089                                callback=self.process_opt),
   1090                    make_option("-f",
   1091                                "--file",
   1092                                action="callback",
   1093                                callback=self.process_opt,
   1094                                type="string",
   1095                                dest="filename")]
   1096         self.parser = OptionParser(option_list=options)
   1097 
   1098     def process_opt(self, option, opt, value, parser_):
   1099         if opt == "-x":
   1100             self.assertEqual(option._short_opts, ["-x"])
   1101             self.assertEqual(option._long_opts, [])
   1102             self.assertTrue(parser_ is self.parser)
   1103             self.assertTrue(value is None)
   1104             self.assertEqual(vars(parser_.values), {'filename': None})
   1105 
   1106             parser_.values.x = 42
   1107         elif opt == "--file":
   1108             self.assertEqual(option._short_opts, ["-f"])
   1109             self.assertEqual(option._long_opts, ["--file"])
   1110             self.assertTrue(parser_ is self.parser)
   1111             self.assertEqual(value, "foo")
   1112             self.assertEqual(vars(parser_.values), {'filename': None, 'x': 42})
   1113 
   1114             setattr(parser_.values, option.dest, value)
   1115         else:
   1116             self.fail("Unknown option %r in process_opt." % opt)
   1117 
   1118     def test_callback(self):
   1119         self.assertParseOK(["-x", "--file=foo"],
   1120                            {'filename': "foo", 'x': 42},
   1121                            [])
   1122 
   1123     def test_callback_help(self):
   1124         # This test was prompted by SF bug #960515 -- the point is

   1125         # not to inspect the help text, just to make sure that

   1126         # format_help() doesn't crash.

   1127         parser = OptionParser(usage=SUPPRESS_USAGE)
   1128         parser.remove_option("-h")
   1129         parser.add_option("-t", "--test", action="callback",
   1130                           callback=lambda: None, type="string",
   1131                           help="foo")
   1132 
   1133         expected_help = ("Options:\n"
   1134                          "  -t TEST, --test=TEST  foo\n")
   1135         self.assertHelp(parser, expected_help)
   1136 
   1137 
   1138 class TestCallbackExtraArgs(BaseTest):
   1139     def setUp(self):
   1140         options = [make_option("-p", "--point", action="callback",
   1141                                callback=self.process_tuple,
   1142                                callback_args=(3, int), type="string",
   1143                                dest="points", default=[])]
   1144         self.parser = OptionParser(option_list=options)
   1145 
   1146     def process_tuple(self, option, opt, value, parser_, len, type):
   1147         self.assertEqual(len, 3)
   1148         self.assertTrue(type is int)
   1149 
   1150         if opt == "-p":
   1151             self.assertEqual(value, "1,2,3")
   1152         elif opt == "--point":
   1153             self.assertEqual(value, "4,5,6")
   1154 
   1155         value = tuple(map(type, value.split(",")))
   1156         getattr(parser_.values, option.dest).append(value)
   1157 
   1158     def test_callback_extra_args(self):
   1159         self.assertParseOK(["-p1,2,3", "--point", "4,5,6"],
   1160                            {'points': [(1,2,3), (4,5,6)]},
   1161                            [])
   1162 
   1163 class TestCallbackMeddleArgs(BaseTest):
   1164     def setUp(self):
   1165         options = [make_option(str(x), action="callback",
   1166                                callback=self.process_n, dest='things')
   1167                    for x in range(-1, -6, -1)]
   1168         self.parser = OptionParser(option_list=options)
   1169 
   1170     # Callback that meddles in rargs, largs

   1171     def process_n(self, option, opt, value, parser_):
   1172         # option is -3, -5, etc.

   1173         nargs = int(opt[1:])
   1174         rargs = parser_.rargs
   1175         if len(rargs) < nargs:
   1176             self.fail("Expected %d arguments for %s option." % (nargs, opt))
   1177         dest = parser_.values.ensure_value(option.dest, [])
   1178         dest.append(tuple(rargs[0:nargs]))
   1179         parser_.largs.append(nargs)
   1180         del rargs[0:nargs]
   1181 
   1182     def test_callback_meddle_args(self):
   1183         self.assertParseOK(["-1", "foo", "-3", "bar", "baz", "qux"],
   1184                            {'things': [("foo",), ("bar", "baz", "qux")]},
   1185                            [1, 3])
   1186 
   1187     def test_callback_meddle_args_separator(self):
   1188         self.assertParseOK(["-2", "foo", "--"],
   1189                            {'things': [('foo', '--')]},
   1190                            [2])
   1191 
   1192 class TestCallbackManyArgs(BaseTest):
   1193     def setUp(self):
   1194         options = [make_option("-a", "--apple", action="callback", nargs=2,
   1195                                callback=self.process_many, type="string"),
   1196                    make_option("-b", "--bob", action="callback", nargs=3,
   1197                                callback=self.process_many, type="int")]
   1198         self.parser = OptionParser(option_list=options)
   1199 
   1200     def process_many(self, option, opt, value, parser_):
   1201         if opt == "-a":
   1202             self.assertEqual(value, ("foo", "bar"))
   1203         elif opt == "--apple":
   1204             self.assertEqual(value, ("ding", "dong"))
   1205         elif opt == "-b":
   1206             self.assertEqual(value, (1, 2, 3))
   1207         elif opt == "--bob":
   1208             self.assertEqual(value, (-666, 42, 0))
   1209 
   1210     def test_many_args(self):
   1211         self.assertParseOK(["-a", "foo", "bar", "--apple", "ding", "dong",
   1212                             "-b", "1", "2", "3", "--bob", "-666", "42",
   1213                             "0"],
   1214                            {"apple": None, "bob": None},
   1215                            [])
   1216 
   1217 class TestCallbackCheckAbbrev(BaseTest):
   1218     def setUp(self):
   1219         self.parser = OptionParser()
   1220         self.parser.add_option("--foo-bar", action="callback",
   1221                                callback=self.check_abbrev)
   1222 
   1223     def check_abbrev(self, option, opt, value, parser):
   1224         self.assertEqual(opt, "--foo-bar")
   1225 
   1226     def test_abbrev_callback_expansion(self):
   1227         self.assertParseOK(["--foo"], {}, [])
   1228 
   1229 class TestCallbackVarArgs(BaseTest):
   1230     def setUp(self):
   1231         options = [make_option("-a", type="int", nargs=2, dest="a"),
   1232                    make_option("-b", action="store_true", dest="b"),
   1233                    make_option("-c", "--callback", action="callback",
   1234                                callback=self.variable_args, dest="c")]
   1235         self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE,
   1236                                                option_list=options)
   1237 
   1238     def variable_args(self, option, opt, value, parser):
   1239         self.assertTrue(value is None)
   1240         value = []
   1241         rargs = parser.rargs
   1242         while rargs:
   1243             arg = rargs[0]
   1244             if ((arg[:2] == "--" and len(arg) > 2) or
   1245                 (arg[:1] == "-" and len(arg) > 1 and arg[1] != "-")):
   1246                 break
   1247             else:
   1248                 value.append(arg)
   1249                 del rargs[0]
   1250         setattr(parser.values, option.dest, value)
   1251 
   1252     def test_variable_args(self):
   1253         self.assertParseOK(["-a3", "-5", "--callback", "foo", "bar"],
   1254                            {'a': (3, -5), 'b': None, 'c': ["foo", "bar"]},
   1255                            [])
   1256 
   1257     def test_consume_separator_stop_at_option(self):
   1258         self.assertParseOK(["-c", "37", "--", "xxx", "-b", "hello"],
   1259                            {'a': None,
   1260                             'b': True,
   1261                             'c': ["37", "--", "xxx"]},
   1262                            ["hello"])
   1263 
   1264     def test_positional_arg_and_variable_args(self):
   1265         self.assertParseOK(["hello", "-c", "foo", "-", "bar"],
   1266                            {'a': None,
   1267                             'b': None,
   1268                             'c':["foo", "-", "bar"]},
   1269                            ["hello"])
   1270 
   1271     def test_stop_at_option(self):
   1272         self.assertParseOK(["-c", "foo", "-b"],
   1273                            {'a': None, 'b': True, 'c': ["foo"]},
   1274                            [])
   1275 
   1276     def test_stop_at_invalid_option(self):
   1277         self.assertParseFail(["-c", "3", "-5", "-a"], "no such option: -5")
   1278 
   1279 
   1280 # -- Test conflict handling and parser.parse_args() --------------------

   1281 
   1282 class ConflictBase(BaseTest):
   1283     def setUp(self):
   1284         options = [make_option("-v", "--verbose", action="count",
   1285                                dest="verbose", help="increment verbosity")]
   1286         self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE,
   1287                                                option_list=options)
   1288 
   1289     def show_version(self, option, opt, value, parser):
   1290         parser.values.show_version = 1
   1291 
   1292 class TestConflict(ConflictBase):
   1293     """Use the default conflict resolution for Optik 1.2: error."""
   1294     def assertTrueconflict_error(self, func):
   1295         err = self.assertRaises(
   1296             func, ("-v", "--version"), {'action' : "callback",
   1297                                         'callback' : self.show_version,
   1298                                         'help' : "show version"},
   1299             OptionConflictError,
   1300             "option -v/--version: conflicting option string(s): -v")
   1301 
   1302         self.assertEqual(err.msg, "conflicting option string(s): -v")
   1303         self.assertEqual(err.option_id, "-v/--version")
   1304 
   1305     def test_conflict_error(self):
   1306         self.assertTrueconflict_error(self.parser.add_option)
   1307 
   1308     def test_conflict_error_group(self):
   1309         group = OptionGroup(self.parser, "Group 1")
   1310         self.assertTrueconflict_error(group.add_option)
   1311 
   1312     def test_no_such_conflict_handler(self):
   1313         self.assertRaises(
   1314             self.parser.set_conflict_handler, ('foo',), None,
   1315             ValueError, "invalid conflict_resolution value 'foo'")
   1316 
   1317 
   1318 class TestConflictResolve(ConflictBase):
   1319     def setUp(self):
   1320         ConflictBase.setUp(self)
   1321         self.parser.set_conflict_handler("resolve")
   1322         self.parser.add_option("-v", "--version", action="callback",
   1323                                callback=self.show_version, help="show version")
   1324 
   1325     def test_conflict_resolve(self):
   1326         v_opt = self.parser.get_option("-v")
   1327         verbose_opt = self.parser.get_option("--verbose")
   1328         version_opt = self.parser.get_option("--version")
   1329 
   1330         self.assertTrue(v_opt is version_opt)
   1331         self.assertTrue(v_opt is not verbose_opt)
   1332         self.assertEqual(v_opt._long_opts, ["--version"])
   1333         self.assertEqual(version_opt._short_opts, ["-v"])
   1334         self.assertEqual(version_opt._long_opts, ["--version"])
   1335         self.assertEqual(verbose_opt._short_opts, [])
   1336         self.assertEqual(verbose_opt._long_opts, ["--verbose"])
   1337 
   1338     def test_conflict_resolve_help(self):
   1339         self.assertOutput(["-h"], """\
   1340 Options:
   1341   --verbose      increment verbosity
   1342   -h, --help     show this help message and exit
   1343   -v, --version  show version
   1344 """)
   1345 
   1346     def test_conflict_resolve_short_opt(self):
   1347         self.assertParseOK(["-v"],
   1348                            {'verbose': None, 'show_version': 1},
   1349                            [])
   1350 
   1351     def test_conflict_resolve_long_opt(self):
   1352         self.assertParseOK(["--verbose"],
   1353                            {'verbose': 1},
   1354                            [])
   1355 
   1356     def test_conflict_resolve_long_opts(self):
   1357         self.assertParseOK(["--verbose", "--version"],
   1358                            {'verbose': 1, 'show_version': 1},
   1359                            [])
   1360 
   1361 class TestConflictOverride(BaseTest):
   1362     def setUp(self):
   1363         self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE)
   1364         self.parser.set_conflict_handler("resolve")
   1365         self.parser.add_option("-n", "--dry-run",
   1366                                action="store_true", dest="dry_run",
   1367                                help="don't do anything")
   1368         self.parser.add_option("--dry-run", "-n",
   1369                                action="store_const", const=42, dest="dry_run",
   1370                                help="dry run mode")
   1371 
   1372     def test_conflict_override_opts(self):
   1373         opt = self.parser.get_option("--dry-run")
   1374         self.assertEqual(opt._short_opts, ["-n"])
   1375         self.assertEqual(opt._long_opts, ["--dry-run"])
   1376 
   1377     def test_conflict_override_help(self):
   1378         self.assertOutput(["-h"], """\
   1379 Options:
   1380   -h, --help     show this help message and exit
   1381   -n, --dry-run  dry run mode
   1382 """)
   1383 
   1384     def test_conflict_override_args(self):
   1385         self.assertParseOK(["-n"],
   1386                            {'dry_run': 42},
   1387                            [])
   1388 
   1389 # -- Other testing. ----------------------------------------------------

   1390 
   1391 _expected_help_basic = """\
   1392 Usage: bar.py [options]
   1393 
   1394 Options:
   1395   -a APPLE           throw APPLEs at basket
   1396   -b NUM, --boo=NUM  shout "boo!" NUM times (in order to frighten away all the
   1397                      evil spirits that cause trouble and mayhem)
   1398   --foo=FOO          store FOO in the foo list for later fooing
   1399   -h, --help         show this help message and exit
   1400 """
   1401 
   1402 _expected_help_long_opts_first = """\
   1403 Usage: bar.py [options]
   1404 
   1405 Options:
   1406   -a APPLE           throw APPLEs at basket
   1407   --boo=NUM, -b NUM  shout "boo!" NUM times (in order to frighten away all the
   1408                      evil spirits that cause trouble and mayhem)
   1409   --foo=FOO          store FOO in the foo list for later fooing
   1410   --help, -h         show this help message and exit
   1411 """
   1412 
   1413 _expected_help_title_formatter = """\
   1414 Usage
   1415 =====
   1416   bar.py [options]
   1417 
   1418 Options
   1419 =======
   1420 -a APPLE           throw APPLEs at basket
   1421 --boo=NUM, -b NUM  shout "boo!" NUM times (in order to frighten away all the
   1422                    evil spirits that cause trouble and mayhem)
   1423 --foo=FOO          store FOO in the foo list for later fooing
   1424 --help, -h         show this help message and exit
   1425 """
   1426 
   1427 _expected_help_short_lines = """\
   1428 Usage: bar.py [options]
   1429 
   1430 Options:
   1431   -a APPLE           throw APPLEs at basket
   1432   -b NUM, --boo=NUM  shout "boo!" NUM times (in order to
   1433                      frighten away all the evil spirits
   1434                      that cause trouble and mayhem)
   1435   --foo=FOO          store FOO in the foo list for later
   1436                      fooing
   1437   -h, --help         show this help message and exit
   1438 """
   1439 
   1440 class TestHelp(BaseTest):
   1441     def setUp(self):
   1442         self.parser = self.make_parser(80)
   1443 
   1444     def make_parser(self, columns):
   1445         options = [
   1446             make_option("-a", type="string", dest='a',
   1447                         metavar="APPLE", help="throw APPLEs at basket"),
   1448             make_option("-b", "--boo", type="int", dest='boo',
   1449                         metavar="NUM",
   1450                         help=
   1451                         "shout \"boo!\" NUM times (in order to frighten away "
   1452                         "all the evil spirits that cause trouble and mayhem)"),
   1453             make_option("--foo", action="append", type="string", dest='foo',
   1454                         help="store FOO in the foo list for later fooing"),
   1455             ]
   1456 
   1457         # We need to set COLUMNS for the OptionParser constructor, but

   1458         # we must restore its original value -- otherwise, this test

   1459         # screws things up for other tests when it's part of the Python

   1460         # test suite.

   1461         with test_support.EnvironmentVarGuard() as env:
   1462             env['COLUMNS'] = str(columns)
   1463             return InterceptingOptionParser(option_list=options)
   1464 
   1465     def assertHelpEquals(self, expected_output):
   1466         if type(expected_output) is types.UnicodeType:
   1467             encoding = self.parser._get_encoding(sys.stdout)
   1468             expected_output = expected_output.encode(encoding, "replace")
   1469 
   1470         save_argv = sys.argv[:]
   1471         try:
   1472             # Make optparse believe bar.py is being executed.

   1473             sys.argv[0] = os.path.join("foo", "bar.py")
   1474             self.assertOutput(["-h"], expected_output)
   1475         finally:
   1476             sys.argv[:] = save_argv
   1477 
   1478     def test_help(self):
   1479         self.assertHelpEquals(_expected_help_basic)
   1480 
   1481     def test_help_old_usage(self):
   1482         self.parser.set_usage("Usage: %prog [options]")
   1483         self.assertHelpEquals(_expected_help_basic)
   1484 
   1485     def test_help_long_opts_first(self):
   1486         self.parser.formatter.short_first = 0
   1487         self.assertHelpEquals(_expected_help_long_opts_first)
   1488 
   1489     def test_help_title_formatter(self):
   1490         with test_support.EnvironmentVarGuard() as env:
   1491             env["COLUMNS"] = "80"
   1492             self.parser.formatter = TitledHelpFormatter()
   1493             self.assertHelpEquals(_expected_help_title_formatter)
   1494 
   1495     def test_wrap_columns(self):
   1496         # Ensure that wrapping respects $COLUMNS environment variable.

   1497         # Need to reconstruct the parser, since that's the only time

   1498         # we look at $COLUMNS.

   1499         self.parser = self.make_parser(60)
   1500         self.assertHelpEquals(_expected_help_short_lines)
   1501 
   1502     def test_help_unicode(self):
   1503         self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE)
   1504         self.parser.add_option("-a", action="store_true", help=u"ol\u00E9!")
   1505         expect = u"""\
   1506 Options:
   1507   -h, --help  show this help message and exit
   1508   -a          ol\u00E9!
   1509 """
   1510         self.assertHelpEquals(expect)
   1511 
   1512     def test_help_unicode_description(self):
   1513         self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE,
   1514                                                description=u"ol\u00E9!")
   1515         expect = u"""\
   1516 ol\u00E9!
   1517 
   1518 Options:
   1519   -h, --help  show this help message and exit
   1520 """
   1521         self.assertHelpEquals(expect)
   1522 
   1523     def test_help_description_groups(self):
   1524         self.parser.set_description(
   1525             "This is the program description for %prog.  %prog has "
   1526             "an option group as well as single options.")
   1527 
   1528         group = OptionGroup(
   1529             self.parser, "Dangerous Options",
   1530             "Caution: use of these options is at your own risk.  "
   1531             "It is believed that some of them bite.")
   1532         group.add_option("-g", action="store_true", help="Group option.")
   1533         self.parser.add_option_group(group)
   1534 
   1535         expect = """\
   1536 Usage: bar.py [options]
   1537 
   1538 This is the program description for bar.py.  bar.py has an option group as
   1539 well as single options.
   1540 
   1541 Options:
   1542   -a APPLE           throw APPLEs at basket
   1543   -b NUM, --boo=NUM  shout "boo!" NUM times (in order to frighten away all the
   1544                      evil spirits that cause trouble and mayhem)
   1545   --foo=FOO          store FOO in the foo list for later fooing
   1546   -h, --help         show this help message and exit
   1547 
   1548   Dangerous Options:
   1549     Caution: use of these options is at your own risk.  It is believed
   1550     that some of them bite.
   1551 
   1552     -g               Group option.
   1553 """
   1554 
   1555         self.assertHelpEquals(expect)
   1556 
   1557         self.parser.epilog = "Please report bugs to /dev/null."
   1558         self.assertHelpEquals(expect + "\nPlease report bugs to /dev/null.\n")
   1559 
   1560 
   1561 class TestMatchAbbrev(BaseTest):
   1562     def test_match_abbrev(self):
   1563         self.assertEqual(_match_abbrev("--f",
   1564                                        {"--foz": None,
   1565                                         "--foo": None,
   1566                                         "--fie": None,
   1567                                         "--f": None}),
   1568                          "--f")
   1569 
   1570     def test_match_abbrev_error(self):
   1571         s = "--f"
   1572         wordmap = {"--foz": None, "--foo": None, "--fie": None}
   1573         self.assertRaises(
   1574             _match_abbrev, (s, wordmap), None,
   1575             BadOptionError, "ambiguous option: --f (--fie, --foo, --foz?)")
   1576 
   1577 
   1578 class TestParseNumber(BaseTest):
   1579     def setUp(self):
   1580         self.parser = InterceptingOptionParser()
   1581         self.parser.add_option("-n", type=int)
   1582         self.parser.add_option("-l", type=long)
   1583 
   1584     def test_parse_num_fail(self):
   1585         self.assertRaises(
   1586             _parse_num, ("", int), {},
   1587             ValueError,
   1588             re.compile(r"invalid literal for int().*: '?'?"))
   1589         self.assertRaises(
   1590             _parse_num, ("0xOoops", long), {},
   1591             ValueError,
   1592             re.compile(r"invalid literal for long().*: '?0xOoops'?"))
   1593 
   1594     def test_parse_num_ok(self):
   1595         self.assertEqual(_parse_num("0", int), 0)
   1596         self.assertEqual(_parse_num("0x10", int), 16)
   1597         self.assertEqual(_parse_num("0XA", long), 10L)
   1598         self.assertEqual(_parse_num("010", long), 8L)
   1599         self.assertEqual(_parse_num("0b11", int), 3)
   1600         self.assertEqual(_parse_num("0b", long), 0L)
   1601 
   1602     def test_numeric_options(self):
   1603         self.assertParseOK(["-n", "42", "-l", "0x20"],
   1604                            { "n": 42, "l": 0x20 }, [])
   1605         self.assertParseOK(["-n", "0b0101", "-l010"],
   1606                            { "n": 5, "l": 8 }, [])
   1607         self.assertParseFail(["-n008"],
   1608                              "option -n: invalid integer value: '008'")
   1609         self.assertParseFail(["-l0b0123"],
   1610                              "option -l: invalid long integer value: '0b0123'")
   1611         self.assertParseFail(["-l", "0x12x"],
   1612                              "option -l: invalid long integer value: '0x12x'")
   1613 
   1614 
   1615 def test_main():
   1616     test_support.run_unittest(__name__)
   1617 
   1618 if __name__ == '__main__':
   1619     test_main()
   1620