1#
2# Test suite for Optik.  Supplied by Johannes Gijsbers
3# (taradino@softhome.net) -- translated from the original Optik
4# test suite to this PyUnit-based version.
5#
6# $Id$
7#
8
9import sys
10import os
11import re
12import copy
13import unittest
14
15from io import StringIO
16from test import support
17from test.support import os_helper
18
19
20import optparse
21from optparse import make_option, Option, \
22     TitledHelpFormatter, OptionParser, OptionGroup, \
23     SUPPRESS_USAGE, OptionError, OptionConflictError, \
24     BadOptionError, OptionValueError, Values
25from optparse import _match_abbrev
26from optparse import _parse_num
27
28class InterceptedError(Exception):
29    def __init__(self,
30                 error_message=None,
31                 exit_status=None,
32                 exit_message=None):
33        self.error_message = error_message
34        self.exit_status = exit_status
35        self.exit_message = exit_message
36
37    def __str__(self):
38        return self.error_message or self.exit_message or "intercepted error"
39
40class InterceptingOptionParser(OptionParser):
41    def exit(self, status=0, msg=None):
42        raise InterceptedError(exit_status=status, exit_message=msg)
43
44    def error(self, msg):
45        raise InterceptedError(error_message=msg)
46
47
48class BaseTest(unittest.TestCase):
49    def assertParseOK(self, args, expected_opts, expected_positional_args):
50        """Assert the options are what we expected when parsing arguments.
51
52        Otherwise, fail with a nicely formatted message.
53
54        Keyword arguments:
55        args -- A list of arguments to parse with OptionParser.
56        expected_opts -- The options expected.
57        expected_positional_args -- The positional arguments expected.
58
59        Returns the options and positional args for further testing.
60        """
61
62        (options, positional_args) = self.parser.parse_args(args)
63        optdict = vars(options)
64
65        self.assertEqual(optdict, expected_opts,
66                         """
67Options are %(optdict)s.
68Should be %(expected_opts)s.
69Args were %(args)s.""" % locals())
70
71        self.assertEqual(positional_args, expected_positional_args,
72                         """
73Positional arguments are %(positional_args)s.
74Should be %(expected_positional_args)s.
75Args were %(args)s.""" % locals ())
76
77        return (options, positional_args)
78
79    def assertRaises(self,
80                     func,
81                     args,
82                     kwargs,
83                     expected_exception,
84                     expected_message):
85        """
86        Assert that the expected exception is raised when calling a
87        function, and that the right error message is included with
88        that exception.
89
90        Arguments:
91          func -- the function to call
92          args -- positional arguments to `func`
93          kwargs -- keyword arguments to `func`
94          expected_exception -- exception that should be raised
95          expected_message -- expected exception message (or pattern
96            if a compiled regex object)
97
98        Returns the exception raised for further testing.
99        """
100        if args is None:
101            args = ()
102        if kwargs is None:
103            kwargs = {}
104
105        try:
106            func(*args, **kwargs)
107        except expected_exception as err:
108            actual_message = str(err)
109            if isinstance(expected_message, re.Pattern):
110                self.assertTrue(expected_message.search(actual_message),
111                             """\
112expected exception message pattern:
113/%s/
114actual exception message:
115'''%s'''
116""" % (expected_message.pattern, actual_message))
117            else:
118                self.assertEqual(actual_message,
119                                 expected_message,
120                                 """\
121expected exception message:
122'''%s'''
123actual exception message:
124'''%s'''
125""" % (expected_message, actual_message))
126
127            return err
128        else:
129            self.fail("""expected exception %(expected_exception)s not raised
130called %(func)r
131with args %(args)r
132and kwargs %(kwargs)r
133""" % locals ())
134
135
136    # -- Assertions used in more than one class --------------------
137
138    def assertParseFail(self, cmdline_args, expected_output):
139        """
140        Assert the parser fails with the expected message.  Caller
141        must ensure that self.parser is an InterceptingOptionParser.
142        """
143        try:
144            self.parser.parse_args(cmdline_args)
145        except InterceptedError as err:
146            self.assertEqual(err.error_message, expected_output)
147        else:
148            self.assertFalse("expected parse failure")
149
150    def assertOutput(self,
151                     cmdline_args,
152                     expected_output,
153                     expected_status=0,
154                     expected_error=None):
155        """Assert the parser prints the expected output on stdout."""
156        save_stdout = sys.stdout
157        try:
158            try:
159                sys.stdout = StringIO()
160                self.parser.parse_args(cmdline_args)
161            finally:
162                output = sys.stdout.getvalue()
163                sys.stdout = save_stdout
164
165        except InterceptedError as err:
166            self.assertTrue(
167                isinstance(output, str),
168                "expected output to be an ordinary string, not %r"
169                % type(output))
170
171            if output != expected_output:
172                self.fail("expected: \n'''\n" + expected_output +
173                          "'''\nbut got \n'''\n" + output + "'''")
174            self.assertEqual(err.exit_status, expected_status)
175            self.assertEqual(err.exit_message, expected_error)
176        else:
177            self.assertFalse("expected parser.exit()")
178
179    def assertTypeError(self, func, expected_message, *args):
180        """Assert that TypeError is raised when executing func."""
181        self.assertRaises(func, args, None, TypeError, expected_message)
182
183    def assertHelp(self, parser, expected_help):
184        actual_help = parser.format_help()
185        if actual_help != expected_help:
186            raise self.failureException(
187                'help text failure; expected:\n"' +
188                expected_help + '"; got:\n"' +
189                actual_help + '"\n')
190
191# -- Test make_option() aka Option -------------------------------------
192
193# It's not necessary to test correct options here.  All the tests in the
194# parser.parse_args() section deal with those, because they're needed
195# there.
196
197class TestOptionChecks(BaseTest):
198    def setUp(self):
199        self.parser = OptionParser(usage=SUPPRESS_USAGE)
200
201    def assertOptionError(self, expected_message, args=[], kwargs={}):
202        self.assertRaises(make_option, args, kwargs,
203                          OptionError, expected_message)
204
205    def test_opt_string_empty(self):
206        self.assertTypeError(make_option,
207                             "at least one option string must be supplied")
208
209    def test_opt_string_too_short(self):
210        self.assertOptionError(
211            "invalid option string 'b': must be at least two characters long",
212            ["b"])
213
214    def test_opt_string_short_invalid(self):
215        self.assertOptionError(
216            "invalid short option string '--': must be "
217            "of the form -x, (x any non-dash char)",
218            ["--"])
219
220    def test_opt_string_long_invalid(self):
221        self.assertOptionError(
222            "invalid long option string '---': "
223            "must start with --, followed by non-dash",
224            ["---"])
225
226    def test_attr_invalid(self):
227        self.assertOptionError(
228            "option -b: invalid keyword arguments: bar, foo",
229            ["-b"], {'foo': None, 'bar': None})
230
231    def test_action_invalid(self):
232        self.assertOptionError(
233            "option -b: invalid action: 'foo'",
234            ["-b"], {'action': 'foo'})
235
236    def test_type_invalid(self):
237        self.assertOptionError(
238            "option -b: invalid option type: 'foo'",
239            ["-b"], {'type': 'foo'})
240        self.assertOptionError(
241            "option -b: invalid option type: 'tuple'",
242            ["-b"], {'type': tuple})
243
244    def test_no_type_for_action(self):
245        self.assertOptionError(
246            "option -b: must not supply a type for action 'count'",
247            ["-b"], {'action': 'count', 'type': 'int'})
248
249    def test_no_choices_list(self):
250        self.assertOptionError(
251            "option -b/--bad: must supply a list of "
252            "choices for type 'choice'",
253            ["-b", "--bad"], {'type': "choice"})
254
255    def test_bad_choices_list(self):
256        typename = type('').__name__
257        self.assertOptionError(
258            "option -b/--bad: choices must be a list of "
259            "strings ('%s' supplied)" % typename,
260            ["-b", "--bad"],
261            {'type': "choice", 'choices':"bad choices"})
262
263    def test_no_choices_for_type(self):
264        self.assertOptionError(
265            "option -b: must not supply choices for type 'int'",
266            ["-b"], {'type': 'int', 'choices':"bad"})
267
268    def test_no_const_for_action(self):
269        self.assertOptionError(
270            "option -b: 'const' must not be supplied for action 'store'",
271            ["-b"], {'action': 'store', 'const': 1})
272
273    def test_no_nargs_for_action(self):
274        self.assertOptionError(
275            "option -b: 'nargs' must not be supplied for action 'count'",
276            ["-b"], {'action': 'count', 'nargs': 2})
277
278    def test_callback_not_callable(self):
279        self.assertOptionError(
280            "option -b: callback not callable: 'foo'",
281            ["-b"], {'action': 'callback',
282                     'callback': 'foo'})
283
284    def dummy(self):
285        pass
286
287    def test_callback_args_no_tuple(self):
288        self.assertOptionError(
289            "option -b: callback_args, if supplied, "
290            "must be a tuple: not 'foo'",
291            ["-b"], {'action': 'callback',
292                     'callback': self.dummy,
293                     'callback_args': 'foo'})
294
295    def test_callback_kwargs_no_dict(self):
296        self.assertOptionError(
297            "option -b: callback_kwargs, if supplied, "
298            "must be a dict: not 'foo'",
299            ["-b"], {'action': 'callback',
300                     'callback': self.dummy,
301                     'callback_kwargs': 'foo'})
302
303    def test_no_callback_for_action(self):
304        self.assertOptionError(
305            "option -b: callback supplied ('foo') for non-callback option",
306            ["-b"], {'action': 'store',
307                     'callback': 'foo'})
308
309    def test_no_callback_args_for_action(self):
310        self.assertOptionError(
311            "option -b: callback_args supplied for non-callback option",
312            ["-b"], {'action': 'store',
313                     'callback_args': 'foo'})
314
315    def test_no_callback_kwargs_for_action(self):
316        self.assertOptionError(
317            "option -b: callback_kwargs supplied for non-callback option",
318            ["-b"], {'action': 'store',
319                     'callback_kwargs': 'foo'})
320
321    def test_no_single_dash(self):
322        self.assertOptionError(
323            "invalid long option string '-debug': "
324            "must start with --, followed by non-dash",
325            ["-debug"])
326
327        self.assertOptionError(
328            "option -d: invalid long option string '-debug': must start with"
329            " --, followed by non-dash",
330            ["-d", "-debug"])
331
332        self.assertOptionError(
333            "invalid long option string '-debug': "
334            "must start with --, followed by non-dash",
335            ["-debug", "--debug"])
336
337class TestOptionParser(BaseTest):
338    def setUp(self):
339        self.parser = OptionParser()
340        self.parser.add_option("-v", "--verbose", "-n", "--noisy",
341                          action="store_true", dest="verbose")
342        self.parser.add_option("-q", "--quiet", "--silent",
343                          action="store_false", dest="verbose")
344
345    def test_add_option_no_Option(self):
346        self.assertTypeError(self.parser.add_option,
347                             "not an Option instance: None", None)
348
349    def test_add_option_invalid_arguments(self):
350        self.assertTypeError(self.parser.add_option,
351                             "invalid arguments", None, None)
352
353    def test_get_option(self):
354        opt1 = self.parser.get_option("-v")
355        self.assertIsInstance(opt1, Option)
356        self.assertEqual(opt1._short_opts, ["-v", "-n"])
357        self.assertEqual(opt1._long_opts, ["--verbose", "--noisy"])
358        self.assertEqual(opt1.action, "store_true")
359        self.assertEqual(opt1.dest, "verbose")
360
361    def test_get_option_equals(self):
362        opt1 = self.parser.get_option("-v")
363        opt2 = self.parser.get_option("--verbose")
364        opt3 = self.parser.get_option("-n")
365        opt4 = self.parser.get_option("--noisy")
366        self.assertTrue(opt1 is opt2 is opt3 is opt4)
367
368    def test_has_option(self):
369        self.assertTrue(self.parser.has_option("-v"))
370        self.assertTrue(self.parser.has_option("--verbose"))
371
372    def assertTrueremoved(self):
373        self.assertTrue(self.parser.get_option("-v") is None)
374        self.assertTrue(self.parser.get_option("--verbose") is None)
375        self.assertTrue(self.parser.get_option("-n") is None)
376        self.assertTrue(self.parser.get_option("--noisy") is None)
377
378        self.assertFalse(self.parser.has_option("-v"))
379        self.assertFalse(self.parser.has_option("--verbose"))
380        self.assertFalse(self.parser.has_option("-n"))
381        self.assertFalse(self.parser.has_option("--noisy"))
382
383        self.assertTrue(self.parser.has_option("-q"))
384        self.assertTrue(self.parser.has_option("--silent"))
385
386    def test_remove_short_opt(self):
387        self.parser.remove_option("-n")
388        self.assertTrueremoved()
389
390    def test_remove_long_opt(self):
391        self.parser.remove_option("--verbose")
392        self.assertTrueremoved()
393
394    def test_remove_nonexistent(self):
395        self.assertRaises(self.parser.remove_option, ('foo',), None,
396                          ValueError, "no such option 'foo'")
397
398    @support.impl_detail('Relies on sys.getrefcount', cpython=True)
399    def test_refleak(self):
400        # If an OptionParser is carrying around a reference to a large
401        # object, various cycles can prevent it from being GC'd in
402        # a timely fashion.  destroy() breaks the cycles to ensure stuff
403        # can be cleaned up.
404        big_thing = [42]
405        refcount = sys.getrefcount(big_thing)
406        parser = OptionParser()
407        parser.add_option("-a", "--aaarggh")
408        parser.big_thing = big_thing
409
410        parser.destroy()
411        #self.assertEqual(refcount, sys.getrefcount(big_thing))
412        del parser
413        self.assertEqual(refcount, sys.getrefcount(big_thing))
414
415
416class TestOptionValues(BaseTest):
417    def setUp(self):
418        pass
419
420    def test_basics(self):
421        values = Values()
422        self.assertEqual(vars(values), {})
423        self.assertEqual(values, {})
424        self.assertNotEqual(values, {"foo": "bar"})
425        self.assertNotEqual(values, "")
426
427        dict = {"foo": "bar", "baz": 42}
428        values = Values(defaults=dict)
429        self.assertEqual(vars(values), dict)
430        self.assertEqual(values, dict)
431        self.assertNotEqual(values, {"foo": "bar"})
432        self.assertNotEqual(values, {})
433        self.assertNotEqual(values, "")
434        self.assertNotEqual(values, [])
435
436
437class TestTypeAliases(BaseTest):
438    def setUp(self):
439        self.parser = OptionParser()
440
441    def test_str_aliases_string(self):
442        self.parser.add_option("-s", type="str")
443        self.assertEqual(self.parser.get_option("-s").type, "string")
444
445    def test_type_object(self):
446        self.parser.add_option("-s", type=str)
447        self.assertEqual(self.parser.get_option("-s").type, "string")
448        self.parser.add_option("-x", type=int)
449        self.assertEqual(self.parser.get_option("-x").type, "int")
450
451
452# Custom type for testing processing of default values.
453_time_units = { 's' : 1, 'm' : 60, 'h' : 60*60, 'd' : 60*60*24 }
454
455def _check_duration(option, opt, value):
456    try:
457        if value[-1].isdigit():
458            return int(value)
459        else:
460            return int(value[:-1]) * _time_units[value[-1]]
461    except (ValueError, IndexError):
462        raise OptionValueError(
463            'option %s: invalid duration: %r' % (opt, value))
464
465class DurationOption(Option):
466    TYPES = Option.TYPES + ('duration',)
467    TYPE_CHECKER = copy.copy(Option.TYPE_CHECKER)
468    TYPE_CHECKER['duration'] = _check_duration
469
470class TestDefaultValues(BaseTest):
471    def setUp(self):
472        self.parser = OptionParser()
473        self.parser.add_option("-v", "--verbose", default=True)
474        self.parser.add_option("-q", "--quiet", dest='verbose')
475        self.parser.add_option("-n", type="int", default=37)
476        self.parser.add_option("-m", type="int")
477        self.parser.add_option("-s", default="foo")
478        self.parser.add_option("-t")
479        self.parser.add_option("-u", default=None)
480        self.expected = { 'verbose': True,
481                          'n': 37,
482                          'm': None,
483                          's': "foo",
484                          't': None,
485                          'u': None }
486
487    def test_basic_defaults(self):
488        self.assertEqual(self.parser.get_default_values(), self.expected)
489
490    def test_mixed_defaults_post(self):
491        self.parser.set_defaults(n=42, m=-100)
492        self.expected.update({'n': 42, 'm': -100})
493        self.assertEqual(self.parser.get_default_values(), self.expected)
494
495    def test_mixed_defaults_pre(self):
496        self.parser.set_defaults(x="barf", y="blah")
497        self.parser.add_option("-x", default="frob")
498        self.parser.add_option("-y")
499
500        self.expected.update({'x': "frob", 'y': "blah"})
501        self.assertEqual(self.parser.get_default_values(), self.expected)
502
503        self.parser.remove_option("-y")
504        self.parser.add_option("-y", default=None)
505        self.expected.update({'y': None})
506        self.assertEqual(self.parser.get_default_values(), self.expected)
507
508    def test_process_default(self):
509        self.parser.option_class = DurationOption
510        self.parser.add_option("-d", type="duration", default=300)
511        self.parser.add_option("-e", type="duration", default="6m")
512        self.parser.set_defaults(n="42")
513        self.expected.update({'d': 300, 'e': 360, 'n': 42})
514        self.assertEqual(self.parser.get_default_values(), self.expected)
515
516        self.parser.set_process_default_values(False)
517        self.expected.update({'d': 300, 'e': "6m", 'n': "42"})
518        self.assertEqual(self.parser.get_default_values(), self.expected)
519
520
521class TestProgName(BaseTest):
522    """
523    Test that %prog expands to the right thing in usage, version,
524    and help strings.
525    """
526
527    def assertUsage(self, parser, expected_usage):
528        self.assertEqual(parser.get_usage(), expected_usage)
529
530    def assertVersion(self, parser, expected_version):
531        self.assertEqual(parser.get_version(), expected_version)
532
533
534    def test_default_progname(self):
535        # Make sure that program name taken from sys.argv[0] by default.
536        save_argv = sys.argv[:]
537        try:
538            sys.argv[0] = os.path.join("foo", "bar", "baz.py")
539            parser = OptionParser("%prog ...", version="%prog 1.2")
540            expected_usage = "Usage: baz.py ...\n"
541            self.assertUsage(parser, expected_usage)
542            self.assertVersion(parser, "baz.py 1.2")
543            self.assertHelp(parser,
544                            expected_usage + "\n" +
545                            "Options:\n"
546                            "  --version   show program's version number and exit\n"
547                            "  -h, --help  show this help message and exit\n")
548        finally:
549            sys.argv[:] = save_argv
550
551    def test_custom_progname(self):
552        parser = OptionParser(prog="thingy",
553                              version="%prog 0.1",
554                              usage="%prog arg arg")
555        parser.remove_option("-h")
556        parser.remove_option("--version")
557        expected_usage = "Usage: thingy arg arg\n"
558        self.assertUsage(parser, expected_usage)
559        self.assertVersion(parser, "thingy 0.1")
560        self.assertHelp(parser, expected_usage + "\n")
561
562
563class TestExpandDefaults(BaseTest):
564    def setUp(self):
565        self.parser = OptionParser(prog="test")
566        self.help_prefix = """\
567Usage: test [options]
568
569Options:
570  -h, --help            show this help message and exit
571"""
572        self.file_help = "read from FILE [default: %default]"
573        self.expected_help_file = self.help_prefix + \
574            "  -f FILE, --file=FILE  read from FILE [default: foo.txt]\n"
575        self.expected_help_none = self.help_prefix + \
576            "  -f FILE, --file=FILE  read from FILE [default: none]\n"
577
578    def test_option_default(self):
579        self.parser.add_option("-f", "--file",
580                               default="foo.txt",
581                               help=self.file_help)
582        self.assertHelp(self.parser, self.expected_help_file)
583
584    def test_parser_default_1(self):
585        self.parser.add_option("-f", "--file",
586                               help=self.file_help)
587        self.parser.set_default('file', "foo.txt")
588        self.assertHelp(self.parser, self.expected_help_file)
589
590    def test_parser_default_2(self):
591        self.parser.add_option("-f", "--file",
592                               help=self.file_help)
593        self.parser.set_defaults(file="foo.txt")
594        self.assertHelp(self.parser, self.expected_help_file)
595
596    def test_no_default(self):
597        self.parser.add_option("-f", "--file",
598                               help=self.file_help)
599        self.assertHelp(self.parser, self.expected_help_none)
600
601    def test_default_none_1(self):
602        self.parser.add_option("-f", "--file",
603                               default=None,
604                               help=self.file_help)
605        self.assertHelp(self.parser, self.expected_help_none)
606
607    def test_default_none_2(self):
608        self.parser.add_option("-f", "--file",
609                               help=self.file_help)
610        self.parser.set_defaults(file=None)
611        self.assertHelp(self.parser, self.expected_help_none)
612
613    def test_float_default(self):
614        self.parser.add_option(
615            "-p", "--prob",
616            help="blow up with probability PROB [default: %default]")
617        self.parser.set_defaults(prob=0.43)
618        expected_help = self.help_prefix + \
619            "  -p PROB, --prob=PROB  blow up with probability PROB [default: 0.43]\n"
620        self.assertHelp(self.parser, expected_help)
621
622    def test_alt_expand(self):
623        self.parser.add_option("-f", "--file",
624                               default="foo.txt",
625                               help="read from FILE [default: *DEFAULT*]")
626        self.parser.formatter.default_tag = "*DEFAULT*"
627        self.assertHelp(self.parser, self.expected_help_file)
628
629    def test_no_expand(self):
630        self.parser.add_option("-f", "--file",
631                               default="foo.txt",
632                               help="read from %default file")
633        self.parser.formatter.default_tag = None
634        expected_help = self.help_prefix + \
635            "  -f FILE, --file=FILE  read from %default file\n"
636        self.assertHelp(self.parser, expected_help)
637
638
639# -- Test parser.parse_args() ------------------------------------------
640
641class TestStandard(BaseTest):
642    def setUp(self):
643        options = [make_option("-a", type="string"),
644                   make_option("-b", "--boo", type="int", dest='boo'),
645                   make_option("--foo", action="append")]
646
647        self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE,
648                                               option_list=options)
649
650    def test_required_value(self):
651        self.assertParseFail(["-a"], "-a option requires 1 argument")
652
653    def test_invalid_integer(self):
654        self.assertParseFail(["-b", "5x"],
655                             "option -b: invalid integer value: '5x'")
656
657    def test_no_such_option(self):
658        self.assertParseFail(["--boo13"], "no such option: --boo13")
659
660    def test_long_invalid_integer(self):
661        self.assertParseFail(["--boo=x5"],
662                             "option --boo: invalid integer value: 'x5'")
663
664    def test_empty(self):
665        self.assertParseOK([], {'a': None, 'boo': None, 'foo': None}, [])
666
667    def test_shortopt_empty_longopt_append(self):
668        self.assertParseOK(["-a", "", "--foo=blah", "--foo="],
669                           {'a': "", 'boo': None, 'foo': ["blah", ""]},
670                           [])
671
672    def test_long_option_append(self):
673        self.assertParseOK(["--foo", "bar", "--foo", "", "--foo=x"],
674                           {'a': None,
675                            'boo': None,
676                            'foo': ["bar", "", "x"]},
677                           [])
678
679    def test_option_argument_joined(self):
680        self.assertParseOK(["-abc"],
681                           {'a': "bc", 'boo': None, 'foo': None},
682                           [])
683
684    def test_option_argument_split(self):
685        self.assertParseOK(["-a", "34"],
686                           {'a': "34", 'boo': None, 'foo': None},
687                           [])
688
689    def test_option_argument_joined_integer(self):
690        self.assertParseOK(["-b34"],
691                           {'a': None, 'boo': 34, 'foo': None},
692                           [])
693
694    def test_option_argument_split_negative_integer(self):
695        self.assertParseOK(["-b", "-5"],
696                           {'a': None, 'boo': -5, 'foo': None},
697                           [])
698
699    def test_long_option_argument_joined(self):
700        self.assertParseOK(["--boo=13"],
701                           {'a': None, 'boo': 13, 'foo': None},
702                           [])
703
704    def test_long_option_argument_split(self):
705        self.assertParseOK(["--boo", "111"],
706                           {'a': None, 'boo': 111, 'foo': None},
707                           [])
708
709    def test_long_option_short_option(self):
710        self.assertParseOK(["--foo=bar", "-axyz"],
711                           {'a': 'xyz', 'boo': None, 'foo': ["bar"]},
712                           [])
713
714    def test_abbrev_long_option(self):
715        self.assertParseOK(["--f=bar", "-axyz"],
716                           {'a': 'xyz', 'boo': None, 'foo': ["bar"]},
717                           [])
718
719    def test_defaults(self):
720        (options, args) = self.parser.parse_args([])
721        defaults = self.parser.get_default_values()
722        self.assertEqual(vars(defaults), vars(options))
723
724    def test_ambiguous_option(self):
725        self.parser.add_option("--foz", action="store",
726                               type="string", dest="foo")
727        self.assertParseFail(["--f=bar"],
728                             "ambiguous option: --f (--foo, --foz?)")
729
730
731    def test_short_and_long_option_split(self):
732        self.assertParseOK(["-a", "xyz", "--foo", "bar"],
733                           {'a': 'xyz', 'boo': None, 'foo': ["bar"]},
734                           [])
735
736    def test_short_option_split_long_option_append(self):
737        self.assertParseOK(["--foo=bar", "-b", "123", "--foo", "baz"],
738                           {'a': None, 'boo': 123, 'foo': ["bar", "baz"]},
739                           [])
740
741    def test_short_option_split_one_positional_arg(self):
742        self.assertParseOK(["-a", "foo", "bar"],
743                           {'a': "foo", 'boo': None, 'foo': None},
744                           ["bar"])
745
746    def test_short_option_consumes_separator(self):
747        self.assertParseOK(["-a", "--", "foo", "bar"],
748                           {'a': "--", 'boo': None, 'foo': None},
749                           ["foo", "bar"])
750        self.assertParseOK(["-a", "--", "--foo", "bar"],
751                           {'a': "--", 'boo': None, 'foo': ["bar"]},
752                           [])
753
754    def test_short_option_joined_and_separator(self):
755        self.assertParseOK(["-ab", "--", "--foo", "bar"],
756                           {'a': "b", 'boo': None, 'foo': None},
757                           ["--foo", "bar"]),
758
759    def test_hyphen_becomes_positional_arg(self):
760        self.assertParseOK(["-ab", "-", "--foo", "bar"],
761                           {'a': "b", 'boo': None, 'foo': ["bar"]},
762                           ["-"])
763
764    def test_no_append_versus_append(self):
765        self.assertParseOK(["-b3", "-b", "5", "--foo=bar", "--foo", "baz"],
766                           {'a': None, 'boo': 5, 'foo': ["bar", "baz"]},
767                           [])
768
769    def test_option_consumes_optionlike_string(self):
770        self.assertParseOK(["-a", "-b3"],
771                           {'a': "-b3", 'boo': None, 'foo': None},
772                           [])
773
774    def test_combined_single_invalid_option(self):
775        self.parser.add_option("-t", action="store_true")
776        self.assertParseFail(["-test"],
777                             "no such option: -e")
778
779class 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
814class 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
837class 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
895class 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
920class 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
949class 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
967class 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
983class 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
1017class 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(os_helper.TESTFN):
1026            os.rmdir(os_helper.TESTFN)
1027        elif os.path.isfile(os_helper.TESTFN):
1028            os.unlink(os_helper.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        os_helper.create_empty_file(os_helper.TESTFN)
1044        self.assertParseOK(["--file", os_helper.TESTFN, "-afoo"],
1045                           {'file': os_helper.TESTFN, 'a': 'foo'},
1046                           [])
1047
1048    def test_filetype_noexist(self):
1049        self.assertParseFail(["--file", os_helper.TESTFN, "-afoo"],
1050                             "%s: file does not exist" %
1051                             os_helper.TESTFN)
1052
1053    def test_filetype_notfile(self):
1054        os.mkdir(os_helper.TESTFN)
1055        self.assertParseFail(["--file", os_helper.TESTFN, "-afoo"],
1056                             "%s: not a regular file" %
1057                             os_helper.TESTFN)
1058
1059
1060class 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
1091class 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
1145class 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
1170class 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
1199class 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
1224class 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
1236class 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
1289class 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
1299class 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
1325class 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"], """\
1347Options:
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
1368class 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"], """\
1386Options:
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 = """\
1399Usage: bar.py [options]
1400
1401Options:
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 = """\
1410Usage: bar.py [options]
1411
1412Options:
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 = """\
1421Usage
1422=====
1423  bar.py [options]
1424
1425Options
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 = """\
1435Usage: bar.py [options]
1436
1437Options:
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_expected_very_help_short_lines = """\
1448Usage: bar.py [options]
1449
1450Options:
1451  -a APPLE
1452    throw
1453    APPLEs at
1454    basket
1455  -b NUM, --boo=NUM
1456    shout
1457    "boo!" NUM
1458    times (in
1459    order to
1460    frighten
1461    away all
1462    the evil
1463    spirits
1464    that cause
1465    trouble and
1466    mayhem)
1467  --foo=FOO
1468    store FOO
1469    in the foo
1470    list for
1471    later
1472    fooing
1473  -h, --help
1474    show this
1475    help
1476    message and
1477    exit
1478"""
1479
1480class TestHelp(BaseTest):
1481    def setUp(self):
1482        self.parser = self.make_parser(80)
1483
1484    def make_parser(self, columns):
1485        options = [
1486            make_option("-a", type="string", dest='a',
1487                        metavar="APPLE", help="throw APPLEs at basket"),
1488            make_option("-b", "--boo", type="int", dest='boo',
1489                        metavar="NUM",
1490                        help=
1491                        "shout \"boo!\" NUM times (in order to frighten away "
1492                        "all the evil spirits that cause trouble and mayhem)"),
1493            make_option("--foo", action="append", type="string", dest='foo',
1494                        help="store FOO in the foo list for later fooing"),
1495            ]
1496
1497        # We need to set COLUMNS for the OptionParser constructor, but
1498        # we must restore its original value -- otherwise, this test
1499        # screws things up for other tests when it's part of the Python
1500        # test suite.
1501        with os_helper.EnvironmentVarGuard() as env:
1502            env['COLUMNS'] = str(columns)
1503            return InterceptingOptionParser(option_list=options)
1504
1505    def assertHelpEquals(self, expected_output):
1506        save_argv = sys.argv[:]
1507        try:
1508            # Make optparse believe bar.py is being executed.
1509            sys.argv[0] = os.path.join("foo", "bar.py")
1510            self.assertOutput(["-h"], expected_output)
1511        finally:
1512            sys.argv[:] = save_argv
1513
1514    def test_help(self):
1515        self.assertHelpEquals(_expected_help_basic)
1516
1517    def test_help_old_usage(self):
1518        self.parser.set_usage("Usage: %prog [options]")
1519        self.assertHelpEquals(_expected_help_basic)
1520
1521    def test_help_long_opts_first(self):
1522        self.parser.formatter.short_first = 0
1523        self.assertHelpEquals(_expected_help_long_opts_first)
1524
1525    def test_help_title_formatter(self):
1526        with os_helper.EnvironmentVarGuard() as env:
1527            env["COLUMNS"] = "80"
1528            self.parser.formatter = TitledHelpFormatter()
1529            self.assertHelpEquals(_expected_help_title_formatter)
1530
1531    def test_wrap_columns(self):
1532        # Ensure that wrapping respects $COLUMNS environment variable.
1533        # Need to reconstruct the parser, since that's the only time
1534        # we look at $COLUMNS.
1535        self.parser = self.make_parser(60)
1536        self.assertHelpEquals(_expected_help_short_lines)
1537        self.parser = self.make_parser(0)
1538        self.assertHelpEquals(_expected_very_help_short_lines)
1539
1540    def test_help_unicode(self):
1541        self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE)
1542        self.parser.add_option("-a", action="store_true", help="ol\u00E9!")
1543        expect = """\
1544Options:
1545  -h, --help  show this help message and exit
1546  -a          ol\u00E9!
1547"""
1548        self.assertHelpEquals(expect)
1549
1550    def test_help_unicode_description(self):
1551        self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE,
1552                                               description="ol\u00E9!")
1553        expect = """\
1554ol\u00E9!
1555
1556Options:
1557  -h, --help  show this help message and exit
1558"""
1559        self.assertHelpEquals(expect)
1560
1561    def test_help_description_groups(self):
1562        self.parser.set_description(
1563            "This is the program description for %prog.  %prog has "
1564            "an option group as well as single options.")
1565
1566        group = OptionGroup(
1567            self.parser, "Dangerous Options",
1568            "Caution: use of these options is at your own risk.  "
1569            "It is believed that some of them bite.")
1570        group.add_option("-g", action="store_true", help="Group option.")
1571        self.parser.add_option_group(group)
1572
1573        expect = """\
1574Usage: bar.py [options]
1575
1576This is the program description for bar.py.  bar.py has an option group as
1577well as single options.
1578
1579Options:
1580  -a APPLE           throw APPLEs at basket
1581  -b NUM, --boo=NUM  shout "boo!" NUM times (in order to frighten away all the
1582                     evil spirits that cause trouble and mayhem)
1583  --foo=FOO          store FOO in the foo list for later fooing
1584  -h, --help         show this help message and exit
1585
1586  Dangerous Options:
1587    Caution: use of these options is at your own risk.  It is believed
1588    that some of them bite.
1589
1590    -g               Group option.
1591"""
1592
1593        self.assertHelpEquals(expect)
1594
1595        self.parser.epilog = "Please report bugs to /dev/null."
1596        self.assertHelpEquals(expect + "\nPlease report bugs to /dev/null.\n")
1597
1598
1599class TestMatchAbbrev(BaseTest):
1600    def test_match_abbrev(self):
1601        self.assertEqual(_match_abbrev("--f",
1602                                       {"--foz": None,
1603                                        "--foo": None,
1604                                        "--fie": None,
1605                                        "--f": None}),
1606                         "--f")
1607
1608    def test_match_abbrev_error(self):
1609        s = "--f"
1610        wordmap = {"--foz": None, "--foo": None, "--fie": None}
1611        self.assertRaises(
1612            _match_abbrev, (s, wordmap), None,
1613            BadOptionError, "ambiguous option: --f (--fie, --foo, --foz?)")
1614
1615
1616class TestParseNumber(BaseTest):
1617    def setUp(self):
1618        self.parser = InterceptingOptionParser()
1619        self.parser.add_option("-n", type=int)
1620        self.parser.add_option("-l", type=int)
1621
1622    def test_parse_num_fail(self):
1623        self.assertRaises(
1624            _parse_num, ("", int), {},
1625            ValueError,
1626            re.compile(r"invalid literal for int().*: '?'?"))
1627        self.assertRaises(
1628            _parse_num, ("0xOoops", int), {},
1629            ValueError,
1630            re.compile(r"invalid literal for int().*: s?'?0xOoops'?"))
1631
1632    def test_parse_num_ok(self):
1633        self.assertEqual(_parse_num("0", int), 0)
1634        self.assertEqual(_parse_num("0x10", int), 16)
1635        self.assertEqual(_parse_num("0XA", int), 10)
1636        self.assertEqual(_parse_num("010", int), 8)
1637        self.assertEqual(_parse_num("0b11", int), 3)
1638        self.assertEqual(_parse_num("0b", int), 0)
1639
1640    def test_numeric_options(self):
1641        self.assertParseOK(["-n", "42", "-l", "0x20"],
1642                           { "n": 42, "l": 0x20 }, [])
1643        self.assertParseOK(["-n", "0b0101", "-l010"],
1644                           { "n": 5, "l": 8 }, [])
1645        self.assertParseFail(["-n008"],
1646                             "option -n: invalid integer value: '008'")
1647        self.assertParseFail(["-l0b0123"],
1648                             "option -l: invalid integer value: '0b0123'")
1649        self.assertParseFail(["-l", "0x12x"],
1650                             "option -l: invalid integer value: '0x12x'")
1651
1652
1653class MiscTestCase(unittest.TestCase):
1654    def test__all__(self):
1655        not_exported = {'check_builtin', 'AmbiguousOptionError', 'NO_DEFAULT'}
1656        support.check__all__(self, optparse, not_exported=not_exported)
1657
1658
1659if __name__ == '__main__':
1660    unittest.main()
1661