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     def test_add_option_accepts_unicode(self):
    773         self.parser.add_option(u"-u", u"--unicode", action="store_true")
    774         self.assertParseOK(["-u"],
    775                            {'a': None, 'boo': None, 'foo': None, 'unicode': True},
    776                            [])
    777 
    778 
    779 class TestBool(BaseTest):
    780     def setUp(self):
    781         options = [make_option("-v",
    782                                "--verbose",
    783                                action="store_true",
    784                                dest="verbose",
    785                                default=''),
    786                    make_option("-q",
    787                                "--quiet",
    788                                action="store_false",
    789                                dest="verbose")]
    790         self.parser = OptionParser(option_list = options)
    791 
    792     def test_bool_default(self):
    793         self.assertParseOK([],
    794                            {'verbose': ''},
    795                            [])
    796 
    797     def test_bool_false(self):
    798         (options, args) = self.assertParseOK(["-q"],
    799                                              {'verbose': 0},
    800                                              [])
    801         self.assertTrue(options.verbose is False)
    802 
    803     def test_bool_true(self):
    804         (options, args) = self.assertParseOK(["-v"],
    805                                              {'verbose': 1},
    806                                              [])
    807         self.assertTrue(options.verbose is True)
    808 
    809     def test_bool_flicker_on_and_off(self):
    810         self.assertParseOK(["-qvq", "-q", "-v"],
    811                            {'verbose': 1},
    812                            [])
    813 
    814 class TestChoice(BaseTest):
    815     def setUp(self):
    816         self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE)
    817         self.parser.add_option("-c", action="store", type="choice",
    818                                dest="choice", choices=["one", "two", "three"])
    819 
    820     def test_valid_choice(self):
    821         self.assertParseOK(["-c", "one", "xyz"],
    822                            {'choice': 'one'},
    823                            ["xyz"])
    824 
    825     def test_invalid_choice(self):
    826         self.assertParseFail(["-c", "four", "abc"],
    827                              "option -c: invalid choice: 'four' "
    828                              "(choose from 'one', 'two', 'three')")
    829 
    830     def test_add_choice_option(self):
    831         self.parser.add_option("-d", "--default",
    832                                choices=["four", "five", "six"])
    833         opt = self.parser.get_option("-d")
    834         self.assertEqual(opt.type, "choice")
    835         self.assertEqual(opt.action, "store")
    836 
    837 class TestCount(BaseTest):
    838     def setUp(self):
    839         self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE)
    840         self.v_opt = make_option("-v", action="count", dest="verbose")
    841         self.parser.add_option(self.v_opt)
    842         self.parser.add_option("--verbose", type="int", dest="verbose")
    843         self.parser.add_option("-q", "--quiet",
    844                                action="store_const", dest="verbose", const=0)
    845 
    846     def test_empty(self):
    847         self.assertParseOK([], {'verbose': None}, [])
    848 
    849     def test_count_one(self):
    850         self.assertParseOK(["-v"], {'verbose': 1}, [])
    851 
    852     def test_count_three(self):
    853         self.assertParseOK(["-vvv"], {'verbose': 3}, [])
    854 
    855     def test_count_three_apart(self):
    856         self.assertParseOK(["-v", "-v", "-v"], {'verbose': 3}, [])
    857 
    858     def test_count_override_amount(self):
    859         self.assertParseOK(["-vvv", "--verbose=2"], {'verbose': 2}, [])
    860 
    861     def test_count_override_quiet(self):
    862         self.assertParseOK(["-vvv", "--verbose=2", "-q"], {'verbose': 0}, [])
    863 
    864     def test_count_overriding(self):
    865         self.assertParseOK(["-vvv", "--verbose=2", "-q", "-v"],
    866                            {'verbose': 1}, [])
    867 
    868     def test_count_interspersed_args(self):
    869         self.assertParseOK(["--quiet", "3", "-v"],
    870                            {'verbose': 1},
    871                            ["3"])
    872 
    873     def test_count_no_interspersed_args(self):
    874         self.parser.disable_interspersed_args()
    875         self.assertParseOK(["--quiet", "3", "-v"],
    876                            {'verbose': 0},
    877                            ["3", "-v"])
    878 
    879     def test_count_no_such_option(self):
    880         self.assertParseFail(["-q3", "-v"], "no such option: -3")
    881 
    882     def test_count_option_no_value(self):
    883         self.assertParseFail(["--quiet=3", "-v"],
    884                              "--quiet option does not take a value")
    885 
    886     def test_count_with_default(self):
    887         self.parser.set_default('verbose', 0)
    888         self.assertParseOK([], {'verbose':0}, [])
    889 
    890     def test_count_overriding_default(self):
    891         self.parser.set_default('verbose', 0)
    892         self.assertParseOK(["-vvv", "--verbose=2", "-q", "-v"],
    893                            {'verbose': 1}, [])
    894 
    895 class TestMultipleArgs(BaseTest):
    896     def setUp(self):
    897         self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE)
    898         self.parser.add_option("-p", "--point",
    899                                action="store", nargs=3, type="float", dest="point")
    900 
    901     def test_nargs_with_positional_args(self):
    902         self.assertParseOK(["foo", "-p", "1", "2.5", "-4.3", "xyz"],
    903                            {'point': (1.0, 2.5, -4.3)},
    904                            ["foo", "xyz"])
    905 
    906     def test_nargs_long_opt(self):
    907         self.assertParseOK(["--point", "-1", "2.5", "-0", "xyz"],
    908                            {'point': (-1.0, 2.5, -0.0)},
    909                            ["xyz"])
    910 
    911     def test_nargs_invalid_float_value(self):
    912         self.assertParseFail(["-p", "1.0", "2x", "3.5"],
    913                              "option -p: "
    914                              "invalid floating-point value: '2x'")
    915 
    916     def test_nargs_required_values(self):
    917         self.assertParseFail(["--point", "1.0", "3.5"],
    918                              "--point option requires 3 arguments")
    919 
    920 class TestMultipleArgsAppend(BaseTest):
    921     def setUp(self):
    922         self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE)
    923         self.parser.add_option("-p", "--point", action="store", nargs=3,
    924                                type="float", dest="point")
    925         self.parser.add_option("-f", "--foo", action="append", nargs=2,
    926                                type="int", dest="foo")
    927         self.parser.add_option("-z", "--zero", action="append_const",
    928                                dest="foo", const=(0, 0))
    929 
    930     def test_nargs_append(self):
    931         self.assertParseOK(["-f", "4", "-3", "blah", "--foo", "1", "666"],
    932                            {'point': None, 'foo': [(4, -3), (1, 666)]},
    933                            ["blah"])
    934 
    935     def test_nargs_append_required_values(self):
    936         self.assertParseFail(["-f4,3"],
    937                              "-f option requires 2 arguments")
    938 
    939     def test_nargs_append_simple(self):
    940         self.assertParseOK(["--foo=3", "4"],
    941                            {'point': None, 'foo':[(3, 4)]},
    942                            [])
    943 
    944     def test_nargs_append_const(self):
    945         self.assertParseOK(["--zero", "--foo", "3", "4", "-z"],
    946                            {'point': None, 'foo':[(0, 0), (3, 4), (0, 0)]},
    947                            [])
    948 
    949 class TestVersion(BaseTest):
    950     def test_version(self):
    951         self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE,
    952                                                version="%prog 0.1")
    953         save_argv = sys.argv[:]
    954         try:
    955             sys.argv[0] = os.path.join(os.curdir, "foo", "bar")
    956             self.assertOutput(["--version"], "bar 0.1\n")
    957         finally:
    958             sys.argv[:] = save_argv
    959 
    960     def test_no_version(self):
    961         self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE)
    962         self.assertParseFail(["--version"],
    963                              "no such option: --version")
    964 
    965 # -- Test conflicting default values and parser.parse_args() -----------
    966 
    967 class TestConflictingDefaults(BaseTest):
    968     """Conflicting default values: the last one should win."""
    969     def setUp(self):
    970         self.parser = OptionParser(option_list=[
    971             make_option("-v", action="store_true", dest="verbose", default=1)])
    972 
    973     def test_conflict_default(self):
    974         self.parser.add_option("-q", action="store_false", dest="verbose",
    975                                default=0)
    976         self.assertParseOK([], {'verbose': 0}, [])
    977 
    978     def test_conflict_default_none(self):
    979         self.parser.add_option("-q", action="store_false", dest="verbose",
    980                                default=None)
    981         self.assertParseOK([], {'verbose': None}, [])
    982 
    983 class TestOptionGroup(BaseTest):
    984     def setUp(self):
    985         self.parser = OptionParser(usage=SUPPRESS_USAGE)
    986 
    987     def test_option_group_create_instance(self):
    988         group = OptionGroup(self.parser, "Spam")
    989         self.parser.add_option_group(group)
    990         group.add_option("--spam", action="store_true",
    991                          help="spam spam spam spam")
    992         self.assertParseOK(["--spam"], {'spam': 1}, [])
    993 
    994     def test_add_group_no_group(self):
    995         self.assertTypeError(self.parser.add_option_group,
    996                              "not an OptionGroup instance: None", None)
    997 
    998     def test_add_group_invalid_arguments(self):
    999         self.assertTypeError(self.parser.add_option_group,
   1000                              "invalid arguments", None, None)
   1001 
   1002     def test_add_group_wrong_parser(self):
   1003         group = OptionGroup(self.parser, "Spam")
   1004         group.parser = OptionParser()
   1005         self.assertRaises(self.parser.add_option_group, (group,), None,
   1006                           ValueError, "invalid OptionGroup (wrong parser)")
   1007 
   1008     def test_group_manipulate(self):
   1009         group = self.parser.add_option_group("Group 2",
   1010                                              description="Some more options")
   1011         group.set_title("Bacon")
   1012         group.add_option("--bacon", type="int")
   1013         self.assertTrue(self.parser.get_option_group("--bacon"), group)
   1014 
   1015 # -- Test extending and parser.parse_args() ----------------------------
   1016 
   1017 class TestExtendAddTypes(BaseTest):
   1018     def setUp(self):
   1019         self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE,
   1020                                                option_class=self.MyOption)
   1021         self.parser.add_option("-a", None, type="string", dest="a")
   1022         self.parser.add_option("-f", "--file", type="file", dest="file")
   1023 
   1024     def tearDown(self):
   1025         if os.path.isdir(test_support.TESTFN):
   1026             os.rmdir(test_support.TESTFN)
   1027         elif os.path.isfile(test_support.TESTFN):
   1028             os.unlink(test_support.TESTFN)
   1029 
   1030     class MyOption (Option):
   1031         def check_file(option, opt, value):
   1032             if not os.path.exists(value):
   1033                 raise OptionValueError("%s: file does not exist" % value)
   1034             elif not os.path.isfile(value):
   1035                 raise OptionValueError("%s: not a regular file" % value)
   1036             return value
   1037 
   1038         TYPES = Option.TYPES + ("file",)
   1039         TYPE_CHECKER = copy.copy(Option.TYPE_CHECKER)
   1040         TYPE_CHECKER["file"] = check_file
   1041 
   1042     def test_filetype_ok(self):
   1043         open(test_support.TESTFN, "w").close()
   1044         self.assertParseOK(["--file", test_support.TESTFN, "-afoo"],
   1045                            {'file': test_support.TESTFN, 'a': 'foo'},
   1046                            [])
   1047 
   1048     def test_filetype_noexist(self):
   1049         self.assertParseFail(["--file", test_support.TESTFN, "-afoo"],
   1050                              "%s: file does not exist" %
   1051                              test_support.TESTFN)
   1052 
   1053     def test_filetype_notfile(self):
   1054         os.mkdir(test_support.TESTFN)
   1055         self.assertParseFail(["--file", test_support.TESTFN, "-afoo"],
   1056                              "%s: not a regular file" %
   1057                              test_support.TESTFN)
   1058 
   1059 
   1060 class TestExtendAddActions(BaseTest):
   1061     def setUp(self):
   1062         options = [self.MyOption("-a", "--apple", action="extend",
   1063                                  type="string", dest="apple")]
   1064         self.parser = OptionParser(option_list=options)
   1065 
   1066     class MyOption (Option):
   1067         ACTIONS = Option.ACTIONS + ("extend",)
   1068         STORE_ACTIONS = Option.STORE_ACTIONS + ("extend",)
   1069         TYPED_ACTIONS = Option.TYPED_ACTIONS + ("extend",)
   1070 
   1071         def take_action(self, action, dest, opt, value, values, parser):
   1072             if action == "extend":
   1073                 lvalue = value.split(",")
   1074                 values.ensure_value(dest, []).extend(lvalue)
   1075             else:
   1076                 Option.take_action(self, action, dest, opt, parser, value,
   1077                                    values)
   1078 
   1079     def test_extend_add_action(self):
   1080         self.assertParseOK(["-afoo,bar", "--apple=blah"],
   1081                            {'apple': ["foo", "bar", "blah"]},
   1082                            [])
   1083 
   1084     def test_extend_add_action_normal(self):
   1085         self.assertParseOK(["-a", "foo", "-abar", "--apple=x,y"],
   1086                            {'apple': ["foo", "bar", "x", "y"]},
   1087                            [])
   1088 
   1089 # -- Test callbacks and parser.parse_args() ----------------------------
   1090 
   1091 class TestCallback(BaseTest):
   1092     def setUp(self):
   1093         options = [make_option("-x",
   1094                                None,
   1095                                action="callback",
   1096                                callback=self.process_opt),
   1097                    make_option("-f",
   1098                                "--file",
   1099                                action="callback",
   1100                                callback=self.process_opt,
   1101                                type="string",
   1102                                dest="filename")]
   1103         self.parser = OptionParser(option_list=options)
   1104 
   1105     def process_opt(self, option, opt, value, parser_):
   1106         if opt == "-x":
   1107             self.assertEqual(option._short_opts, ["-x"])
   1108             self.assertEqual(option._long_opts, [])
   1109             self.assertTrue(parser_ is self.parser)
   1110             self.assertTrue(value is None)
   1111             self.assertEqual(vars(parser_.values), {'filename': None})
   1112 
   1113             parser_.values.x = 42
   1114         elif opt == "--file":
   1115             self.assertEqual(option._short_opts, ["-f"])
   1116             self.assertEqual(option._long_opts, ["--file"])
   1117             self.assertTrue(parser_ is self.parser)
   1118             self.assertEqual(value, "foo")
   1119             self.assertEqual(vars(parser_.values), {'filename': None, 'x': 42})
   1120 
   1121             setattr(parser_.values, option.dest, value)
   1122         else:
   1123             self.fail("Unknown option %r in process_opt." % opt)
   1124 
   1125     def test_callback(self):
   1126         self.assertParseOK(["-x", "--file=foo"],
   1127                            {'filename': "foo", 'x': 42},
   1128                            [])
   1129 
   1130     def test_callback_help(self):
   1131         # This test was prompted by SF bug #960515 -- the point is
   1132         # not to inspect the help text, just to make sure that
   1133         # format_help() doesn't crash.
   1134         parser = OptionParser(usage=SUPPRESS_USAGE)
   1135         parser.remove_option("-h")
   1136         parser.add_option("-t", "--test", action="callback",
   1137                           callback=lambda: None, type="string",
   1138                           help="foo")
   1139 
   1140         expected_help = ("Options:\n"
   1141                          "  -t TEST, --test=TEST  foo\n")
   1142         self.assertHelp(parser, expected_help)
   1143 
   1144 
   1145 class TestCallbackExtraArgs(BaseTest):
   1146     def setUp(self):
   1147         options = [make_option("-p", "--point", action="callback",
   1148                                callback=self.process_tuple,
   1149                                callback_args=(3, int), type="string",
   1150                                dest="points", default=[])]
   1151         self.parser = OptionParser(option_list=options)
   1152 
   1153     def process_tuple(self, option, opt, value, parser_, len, type):
   1154         self.assertEqual(len, 3)
   1155         self.assertTrue(type is int)
   1156 
   1157         if opt == "-p":
   1158             self.assertEqual(value, "1,2,3")
   1159         elif opt == "--point":
   1160             self.assertEqual(value, "4,5,6")
   1161 
   1162         value = tuple(map(type, value.split(",")))
   1163         getattr(parser_.values, option.dest).append(value)
   1164 
   1165     def test_callback_extra_args(self):
   1166         self.assertParseOK(["-p1,2,3", "--point", "4,5,6"],
   1167                            {'points': [(1,2,3), (4,5,6)]},
   1168                            [])
   1169 
   1170 class TestCallbackMeddleArgs(BaseTest):
   1171     def setUp(self):
   1172         options = [make_option(str(x), action="callback",
   1173                                callback=self.process_n, dest='things')
   1174                    for x in range(-1, -6, -1)]
   1175         self.parser = OptionParser(option_list=options)
   1176 
   1177     # Callback that meddles in rargs, largs
   1178     def process_n(self, option, opt, value, parser_):
   1179         # option is -3, -5, etc.
   1180         nargs = int(opt[1:])
   1181         rargs = parser_.rargs
   1182         if len(rargs) < nargs:
   1183             self.fail("Expected %d arguments for %s option." % (nargs, opt))
   1184         dest = parser_.values.ensure_value(option.dest, [])
   1185         dest.append(tuple(rargs[0:nargs]))
   1186         parser_.largs.append(nargs)
   1187         del rargs[0:nargs]
   1188 
   1189     def test_callback_meddle_args(self):
   1190         self.assertParseOK(["-1", "foo", "-3", "bar", "baz", "qux"],
   1191                            {'things': [("foo",), ("bar", "baz", "qux")]},
   1192                            [1, 3])
   1193 
   1194     def test_callback_meddle_args_separator(self):
   1195         self.assertParseOK(["-2", "foo", "--"],
   1196                            {'things': [('foo', '--')]},
   1197                            [2])
   1198 
   1199 class TestCallbackManyArgs(BaseTest):
   1200     def setUp(self):
   1201         options = [make_option("-a", "--apple", action="callback", nargs=2,
   1202                                callback=self.process_many, type="string"),
   1203                    make_option("-b", "--bob", action="callback", nargs=3,
   1204                                callback=self.process_many, type="int")]
   1205         self.parser = OptionParser(option_list=options)
   1206 
   1207     def process_many(self, option, opt, value, parser_):
   1208         if opt == "-a":
   1209             self.assertEqual(value, ("foo", "bar"))
   1210         elif opt == "--apple":
   1211             self.assertEqual(value, ("ding", "dong"))
   1212         elif opt == "-b":
   1213             self.assertEqual(value, (1, 2, 3))
   1214         elif opt == "--bob":
   1215             self.assertEqual(value, (-666, 42, 0))
   1216 
   1217     def test_many_args(self):
   1218         self.assertParseOK(["-a", "foo", "bar", "--apple", "ding", "dong",
   1219                             "-b", "1", "2", "3", "--bob", "-666", "42",
   1220                             "0"],
   1221                            {"apple": None, "bob": None},
   1222                            [])
   1223 
   1224 class TestCallbackCheckAbbrev(BaseTest):
   1225     def setUp(self):
   1226         self.parser = OptionParser()
   1227         self.parser.add_option("--foo-bar", action="callback",
   1228                                callback=self.check_abbrev)
   1229 
   1230     def check_abbrev(self, option, opt, value, parser):
   1231         self.assertEqual(opt, "--foo-bar")
   1232 
   1233     def test_abbrev_callback_expansion(self):
   1234         self.assertParseOK(["--foo"], {}, [])
   1235 
   1236 class TestCallbackVarArgs(BaseTest):
   1237     def setUp(self):
   1238         options = [make_option("-a", type="int", nargs=2, dest="a"),
   1239                    make_option("-b", action="store_true", dest="b"),
   1240                    make_option("-c", "--callback", action="callback",
   1241                                callback=self.variable_args, dest="c")]
   1242         self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE,
   1243                                                option_list=options)
   1244 
   1245     def variable_args(self, option, opt, value, parser):
   1246         self.assertTrue(value is None)
   1247         value = []
   1248         rargs = parser.rargs
   1249         while rargs:
   1250             arg = rargs[0]
   1251             if ((arg[:2] == "--" and len(arg) > 2) or
   1252                 (arg[:1] == "-" and len(arg) > 1 and arg[1] != "-")):
   1253                 break
   1254             else:
   1255                 value.append(arg)
   1256                 del rargs[0]
   1257         setattr(parser.values, option.dest, value)
   1258 
   1259     def test_variable_args(self):
   1260         self.assertParseOK(["-a3", "-5", "--callback", "foo", "bar"],
   1261                            {'a': (3, -5), 'b': None, 'c': ["foo", "bar"]},
   1262                            [])
   1263 
   1264     def test_consume_separator_stop_at_option(self):
   1265         self.assertParseOK(["-c", "37", "--", "xxx", "-b", "hello"],
   1266                            {'a': None,
   1267                             'b': True,
   1268                             'c': ["37", "--", "xxx"]},
   1269                            ["hello"])
   1270 
   1271     def test_positional_arg_and_variable_args(self):
   1272         self.assertParseOK(["hello", "-c", "foo", "-", "bar"],
   1273                            {'a': None,
   1274                             'b': None,
   1275                             'c':["foo", "-", "bar"]},
   1276                            ["hello"])
   1277 
   1278     def test_stop_at_option(self):
   1279         self.assertParseOK(["-c", "foo", "-b"],
   1280                            {'a': None, 'b': True, 'c': ["foo"]},
   1281                            [])
   1282 
   1283     def test_stop_at_invalid_option(self):
   1284         self.assertParseFail(["-c", "3", "-5", "-a"], "no such option: -5")
   1285 
   1286 
   1287 # -- Test conflict handling and parser.parse_args() --------------------
   1288 
   1289 class ConflictBase(BaseTest):
   1290     def setUp(self):
   1291         options = [make_option("-v", "--verbose", action="count",
   1292                                dest="verbose", help="increment verbosity")]
   1293         self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE,
   1294                                                option_list=options)
   1295 
   1296     def show_version(self, option, opt, value, parser):
   1297         parser.values.show_version = 1
   1298 
   1299 class TestConflict(ConflictBase):
   1300     """Use the default conflict resolution for Optik 1.2: error."""
   1301     def assertTrueconflict_error(self, func):
   1302         err = self.assertRaises(
   1303             func, ("-v", "--version"), {'action' : "callback",
   1304                                         'callback' : self.show_version,
   1305                                         'help' : "show version"},
   1306             OptionConflictError,
   1307             "option -v/--version: conflicting option string(s): -v")
   1308 
   1309         self.assertEqual(err.msg, "conflicting option string(s): -v")
   1310         self.assertEqual(err.option_id, "-v/--version")
   1311 
   1312     def test_conflict_error(self):
   1313         self.assertTrueconflict_error(self.parser.add_option)
   1314 
   1315     def test_conflict_error_group(self):
   1316         group = OptionGroup(self.parser, "Group 1")
   1317         self.assertTrueconflict_error(group.add_option)
   1318 
   1319     def test_no_such_conflict_handler(self):
   1320         self.assertRaises(
   1321             self.parser.set_conflict_handler, ('foo',), None,
   1322             ValueError, "invalid conflict_resolution value 'foo'")
   1323 
   1324 
   1325 class TestConflictResolve(ConflictBase):
   1326     def setUp(self):
   1327         ConflictBase.setUp(self)
   1328         self.parser.set_conflict_handler("resolve")
   1329         self.parser.add_option("-v", "--version", action="callback",
   1330                                callback=self.show_version, help="show version")
   1331 
   1332     def test_conflict_resolve(self):
   1333         v_opt = self.parser.get_option("-v")
   1334         verbose_opt = self.parser.get_option("--verbose")
   1335         version_opt = self.parser.get_option("--version")
   1336 
   1337         self.assertTrue(v_opt is version_opt)
   1338         self.assertTrue(v_opt is not verbose_opt)
   1339         self.assertEqual(v_opt._long_opts, ["--version"])
   1340         self.assertEqual(version_opt._short_opts, ["-v"])
   1341         self.assertEqual(version_opt._long_opts, ["--version"])
   1342         self.assertEqual(verbose_opt._short_opts, [])
   1343         self.assertEqual(verbose_opt._long_opts, ["--verbose"])
   1344 
   1345     def test_conflict_resolve_help(self):
   1346         self.assertOutput(["-h"], """\
   1347 Options:
   1348   --verbose      increment verbosity
   1349   -h, --help     show this help message and exit
   1350   -v, --version  show version
   1351 """)
   1352 
   1353     def test_conflict_resolve_short_opt(self):
   1354         self.assertParseOK(["-v"],
   1355                            {'verbose': None, 'show_version': 1},
   1356                            [])
   1357 
   1358     def test_conflict_resolve_long_opt(self):
   1359         self.assertParseOK(["--verbose"],
   1360                            {'verbose': 1},
   1361                            [])
   1362 
   1363     def test_conflict_resolve_long_opts(self):
   1364         self.assertParseOK(["--verbose", "--version"],
   1365                            {'verbose': 1, 'show_version': 1},
   1366                            [])
   1367 
   1368 class TestConflictOverride(BaseTest):
   1369     def setUp(self):
   1370         self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE)
   1371         self.parser.set_conflict_handler("resolve")
   1372         self.parser.add_option("-n", "--dry-run",
   1373                                action="store_true", dest="dry_run",
   1374                                help="don't do anything")
   1375         self.parser.add_option("--dry-run", "-n",
   1376                                action="store_const", const=42, dest="dry_run",
   1377                                help="dry run mode")
   1378 
   1379     def test_conflict_override_opts(self):
   1380         opt = self.parser.get_option("--dry-run")
   1381         self.assertEqual(opt._short_opts, ["-n"])
   1382         self.assertEqual(opt._long_opts, ["--dry-run"])
   1383 
   1384     def test_conflict_override_help(self):
   1385         self.assertOutput(["-h"], """\
   1386 Options:
   1387   -h, --help     show this help message and exit
   1388   -n, --dry-run  dry run mode
   1389 """)
   1390 
   1391     def test_conflict_override_args(self):
   1392         self.assertParseOK(["-n"],
   1393                            {'dry_run': 42},
   1394                            [])
   1395 
   1396 # -- Other testing. ----------------------------------------------------
   1397 
   1398 _expected_help_basic = """\
   1399 Usage: bar.py [options]
   1400 
   1401 Options:
   1402   -a APPLE           throw APPLEs at basket
   1403   -b NUM, --boo=NUM  shout "boo!" NUM times (in order to frighten away all the
   1404                      evil spirits that cause trouble and mayhem)
   1405   --foo=FOO          store FOO in the foo list for later fooing
   1406   -h, --help         show this help message and exit
   1407 """
   1408 
   1409 _expected_help_long_opts_first = """\
   1410 Usage: bar.py [options]
   1411 
   1412 Options:
   1413   -a APPLE           throw APPLEs at basket
   1414   --boo=NUM, -b NUM  shout "boo!" NUM times (in order to frighten away all the
   1415                      evil spirits that cause trouble and mayhem)
   1416   --foo=FOO          store FOO in the foo list for later fooing
   1417   --help, -h         show this help message and exit
   1418 """
   1419 
   1420 _expected_help_title_formatter = """\
   1421 Usage
   1422 =====
   1423   bar.py [options]
   1424 
   1425 Options
   1426 =======
   1427 -a APPLE           throw APPLEs at basket
   1428 --boo=NUM, -b NUM  shout "boo!" NUM times (in order to frighten away all the
   1429                    evil spirits that cause trouble and mayhem)
   1430 --foo=FOO          store FOO in the foo list for later fooing
   1431 --help, -h         show this help message and exit
   1432 """
   1433 
   1434 _expected_help_short_lines = """\
   1435 Usage: bar.py [options]
   1436 
   1437 Options:
   1438   -a APPLE           throw APPLEs at basket
   1439   -b NUM, --boo=NUM  shout "boo!" NUM times (in order to
   1440                      frighten away all the evil spirits
   1441                      that cause trouble and mayhem)
   1442   --foo=FOO          store FOO in the foo list for later
   1443                      fooing
   1444   -h, --help         show this help message and exit
   1445 """
   1446 
   1447 class TestHelp(BaseTest):
   1448     def setUp(self):
   1449         self.parser = self.make_parser(80)
   1450 
   1451     def make_parser(self, columns):
   1452         options = [
   1453             make_option("-a", type="string", dest='a',
   1454                         metavar="APPLE", help="throw APPLEs at basket"),
   1455             make_option("-b", "--boo", type="int", dest='boo',
   1456                         metavar="NUM",
   1457                         help=
   1458                         "shout \"boo!\" NUM times (in order to frighten away "
   1459                         "all the evil spirits that cause trouble and mayhem)"),
   1460             make_option("--foo", action="append", type="string", dest='foo',
   1461                         help="store FOO in the foo list for later fooing"),
   1462             ]
   1463 
   1464         # We need to set COLUMNS for the OptionParser constructor, but
   1465         # we must restore its original value -- otherwise, this test
   1466         # screws things up for other tests when it's part of the Python
   1467         # test suite.
   1468         with test_support.EnvironmentVarGuard() as env:
   1469             env['COLUMNS'] = str(columns)
   1470             return InterceptingOptionParser(option_list=options)
   1471 
   1472     def assertHelpEquals(self, expected_output):
   1473         if type(expected_output) is types.UnicodeType:
   1474             encoding = self.parser._get_encoding(sys.stdout)
   1475             expected_output = expected_output.encode(encoding, "replace")
   1476 
   1477         save_argv = sys.argv[:]
   1478         try:
   1479             # Make optparse believe bar.py is being executed.
   1480             sys.argv[0] = os.path.join("foo", "bar.py")
   1481             self.assertOutput(["-h"], expected_output)
   1482         finally:
   1483             sys.argv[:] = save_argv
   1484 
   1485     def test_help(self):
   1486         self.assertHelpEquals(_expected_help_basic)
   1487 
   1488     def test_help_old_usage(self):
   1489         self.parser.set_usage("Usage: %prog [options]")
   1490         self.assertHelpEquals(_expected_help_basic)
   1491 
   1492     def test_help_long_opts_first(self):
   1493         self.parser.formatter.short_first = 0
   1494         self.assertHelpEquals(_expected_help_long_opts_first)
   1495 
   1496     def test_help_title_formatter(self):
   1497         with test_support.EnvironmentVarGuard() as env:
   1498             env["COLUMNS"] = "80"
   1499             self.parser.formatter = TitledHelpFormatter()
   1500             self.assertHelpEquals(_expected_help_title_formatter)
   1501 
   1502     def test_wrap_columns(self):
   1503         # Ensure that wrapping respects $COLUMNS environment variable.
   1504         # Need to reconstruct the parser, since that's the only time
   1505         # we look at $COLUMNS.
   1506         self.parser = self.make_parser(60)
   1507         self.assertHelpEquals(_expected_help_short_lines)
   1508 
   1509     def test_help_unicode(self):
   1510         self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE)
   1511         self.parser.add_option("-a", action="store_true", help=u"ol\u00E9!")
   1512         expect = u"""\
   1513 Options:
   1514   -h, --help  show this help message and exit
   1515   -a          ol\u00E9!
   1516 """
   1517         self.assertHelpEquals(expect)
   1518 
   1519     def test_help_unicode_description(self):
   1520         self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE,
   1521                                                description=u"ol\u00E9!")
   1522         expect = u"""\
   1523 ol\u00E9!
   1524 
   1525 Options:
   1526   -h, --help  show this help message and exit
   1527 """
   1528         self.assertHelpEquals(expect)
   1529 
   1530     def test_help_description_groups(self):
   1531         self.parser.set_description(
   1532             "This is the program description for %prog.  %prog has "
   1533             "an option group as well as single options.")
   1534 
   1535         group = OptionGroup(
   1536             self.parser, "Dangerous Options",
   1537             "Caution: use of these options is at your own risk.  "
   1538             "It is believed that some of them bite.")
   1539         group.add_option("-g", action="store_true", help="Group option.")
   1540         self.parser.add_option_group(group)
   1541 
   1542         expect = """\
   1543 Usage: bar.py [options]
   1544 
   1545 This is the program description for bar.py.  bar.py has an option group as
   1546 well as single options.
   1547 
   1548 Options:
   1549   -a APPLE           throw APPLEs at basket
   1550   -b NUM, --boo=NUM  shout "boo!" NUM times (in order to frighten away all the
   1551                      evil spirits that cause trouble and mayhem)
   1552   --foo=FOO          store FOO in the foo list for later fooing
   1553   -h, --help         show this help message and exit
   1554 
   1555   Dangerous Options:
   1556     Caution: use of these options is at your own risk.  It is believed
   1557     that some of them bite.
   1558 
   1559     -g               Group option.
   1560 """
   1561 
   1562         self.assertHelpEquals(expect)
   1563 
   1564         self.parser.epilog = "Please report bugs to /dev/null."
   1565         self.assertHelpEquals(expect + "\nPlease report bugs to /dev/null.\n")
   1566 
   1567 
   1568 class TestMatchAbbrev(BaseTest):
   1569     def test_match_abbrev(self):
   1570         self.assertEqual(_match_abbrev("--f",
   1571                                        {"--foz": None,
   1572                                         "--foo": None,
   1573                                         "--fie": None,
   1574                                         "--f": None}),
   1575                          "--f")
   1576 
   1577     def test_match_abbrev_error(self):
   1578         s = "--f"
   1579         wordmap = {"--foz": None, "--foo": None, "--fie": None}
   1580         self.assertRaises(
   1581             _match_abbrev, (s, wordmap), None,
   1582             BadOptionError, "ambiguous option: --f (--fie, --foo, --foz?)")
   1583 
   1584 
   1585 class TestParseNumber(BaseTest):
   1586     def setUp(self):
   1587         self.parser = InterceptingOptionParser()
   1588         self.parser.add_option("-n", type=int)
   1589         self.parser.add_option("-l", type=long)
   1590 
   1591     def test_parse_num_fail(self):
   1592         self.assertRaises(
   1593             _parse_num, ("", int), {},
   1594             ValueError,
   1595             re.compile(r"invalid literal for int().*: '?'?"))
   1596         self.assertRaises(
   1597             _parse_num, ("0xOoops", long), {},
   1598             ValueError,
   1599             re.compile(r"invalid literal for long().*: '?0xOoops'?"))
   1600 
   1601     def test_parse_num_ok(self):
   1602         self.assertEqual(_parse_num("0", int), 0)
   1603         self.assertEqual(_parse_num("0x10", int), 16)
   1604         self.assertEqual(_parse_num("0XA", long), 10L)
   1605         self.assertEqual(_parse_num("010", long), 8L)
   1606         self.assertEqual(_parse_num("0b11", int), 3)
   1607         self.assertEqual(_parse_num("0b", long), 0L)
   1608 
   1609     def test_numeric_options(self):
   1610         self.assertParseOK(["-n", "42", "-l", "0x20"],
   1611                            { "n": 42, "l": 0x20 }, [])
   1612         self.assertParseOK(["-n", "0b0101", "-l010"],
   1613                            { "n": 5, "l": 8 }, [])
   1614         self.assertParseFail(["-n008"],
   1615                              "option -n: invalid integer value: '008'")
   1616         self.assertParseFail(["-l0b0123"],
   1617                              "option -l: invalid long integer value: '0b0123'")
   1618         self.assertParseFail(["-l", "0x12x"],
   1619                              "option -l: invalid long integer value: '0x12x'")
   1620 
   1621 
   1622 def test_main():
   1623     test_support.run_unittest(__name__)
   1624 
   1625 if __name__ == '__main__':
   1626     test_main()
   1627