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
17
18
19import optparse
20from optparse import make_option, Option, \
21     TitledHelpFormatter, OptionParser, OptionGroup, \
22     SUPPRESS_USAGE, OptionError, OptionConflictError, \
23     BadOptionError, OptionValueError, Values
24from optparse import _match_abbrev
25from optparse import _parse_num
26
27class InterceptedError(Exception):
28    def __init__(self,
29                 error_message=None,
30                 exit_status=None,
31                 exit_message=None):
32        self.error_message = error_message
33        self.exit_status = exit_status
34        self.exit_message = exit_message
35
36    def __str__(self):
37        return self.error_message or self.exit_message or "intercepted error"
38
39class InterceptingOptionParser(OptionParser):
40    def exit(self, status=0, msg=None):
41        raise InterceptedError(exit_status=status, exit_message=msg)
42
43    def error(self, msg):
44        raise InterceptedError(error_message=msg)
45
46
47class BaseTest(unittest.TestCase):
48    def assertParseOK(self, args, expected_opts, expected_positional_args):
49        """Assert the options are what we expected when parsing arguments.
50
51        Otherwise, fail with a nicely formatted message.
52
53        Keyword arguments:
54        args -- A list of arguments to parse with OptionParser.
55        expected_opts -- The options expected.
56        expected_positional_args -- The positional arguments expected.
57
58        Returns the options and positional args for further testing.
59        """
60
61        (options, positional_args) = self.parser.parse_args(args)
62        optdict = vars(options)
63
64        self.assertEqual(optdict, expected_opts,
65                         """
66Options are %(optdict)s.
67Should be %(expected_opts)s.
68Args were %(args)s.""" % locals())
69
70        self.assertEqual(positional_args, expected_positional_args,
71                         """
72Positional arguments are %(positional_args)s.
73Should be %(expected_positional_args)s.
74Args were %(args)s.""" % locals ())
75
76        return (options, positional_args)
77
78    def assertRaises(self,
79                     func,
80                     args,
81                     kwargs,
82                     expected_exception,
83                     expected_message):
84        """
85        Assert that the expected exception is raised when calling a
86        function, and that the right error message is included with
87        that exception.
88
89        Arguments:
90          func -- the function to call
91          args -- positional arguments to `func`
92          kwargs -- keyword arguments to `func`
93          expected_exception -- exception that should be raised
94          expected_message -- expected exception message (or pattern
95            if a compiled regex object)
96
97        Returns the exception raised for further testing.
98        """
99        if args is None:
100            args = ()
101        if kwargs is None:
102            kwargs = {}
103
104        try:
105            func(*args, **kwargs)
106        except expected_exception as err:
107            actual_message = str(err)
108            if isinstance(expected_message, re.Pattern):
109                self.assertTrue(expected_message.search(actual_message),
110                             """\
111expected exception message pattern:
112/%s/
113actual exception message:
114'''%s'''
115""" % (expected_message.pattern, actual_message))
116            else:
117                self.assertEqual(actual_message,
118                                 expected_message,
119                                 """\
120expected exception message:
121'''%s'''
122actual exception message:
123'''%s'''
124""" % (expected_message, actual_message))
125
126            return err
127        else:
128            self.fail("""expected exception %(expected_exception)s not raised
129called %(func)r
130with args %(args)r
131and kwargs %(kwargs)r
132""" % locals ())
133
134
135    # -- Assertions used in more than one class --------------------
136
137    def assertParseFail(self, cmdline_args, expected_output):
138        """
139        Assert the parser fails with the expected message.  Caller
140        must ensure that self.parser is an InterceptingOptionParser.
141        """
142        try:
143            self.parser.parse_args(cmdline_args)
144        except InterceptedError as err:
145            self.assertEqual(err.error_message, expected_output)
146        else:
147            self.assertFalse("expected parse failure")
148
149    def assertOutput(self,
150                     cmdline_args,
151                     expected_output,
152                     expected_status=0,
153                     expected_error=None):
154        """Assert the parser prints the expected output on stdout."""
155        save_stdout = sys.stdout
156        try:
157            try:
158                sys.stdout = StringIO()
159                self.parser.parse_args(cmdline_args)
160            finally:
161                output = sys.stdout.getvalue()
162                sys.stdout = save_stdout
163
164        except InterceptedError as err:
165            self.assertTrue(
166                isinstance(output, str),
167                "expected output to be an ordinary string, not %r"
168                % type(output))
169
170            if output != expected_output:
171                self.fail("expected: \n'''\n" + expected_output +
172                          "'''\nbut got \n'''\n" + output + "'''")
173            self.assertEqual(err.exit_status, expected_status)
174            self.assertEqual(err.exit_message, expected_error)
175        else:
176            self.assertFalse("expected parser.exit()")
177
178    def assertTypeError(self, func, expected_message, *args):
179        """Assert that TypeError is raised when executing func."""
180        self.assertRaises(func, args, None, TypeError, expected_message)
181
182    def assertHelp(self, parser, expected_help):
183        actual_help = parser.format_help()
184        if actual_help != expected_help:
185            raise self.failureException(
186                'help text failure; expected:\n"' +
187                expected_help + '"; got:\n"' +
188                actual_help + '"\n')
189
190# -- Test make_option() aka Option -------------------------------------
191
192# It's not necessary to test correct options here.  All the tests in the
193# parser.parse_args() section deal with those, because they're needed
194# there.
195
196class TestOptionChecks(BaseTest):
197    def setUp(self):
198        self.parser = OptionParser(usage=SUPPRESS_USAGE)
199
200    def assertOptionError(self, expected_message, args=[], kwargs={}):
201        self.assertRaises(make_option, args, kwargs,
202                          OptionError, expected_message)
203
204    def test_opt_string_empty(self):
205        self.assertTypeError(make_option,
206                             "at least one option string must be supplied")
207
208    def test_opt_string_too_short(self):
209        self.assertOptionError(
210            "invalid option string 'b': must be at least two characters long",
211            ["b"])
212
213    def test_opt_string_short_invalid(self):
214        self.assertOptionError(
215            "invalid short option string '--': must be "
216            "of the form -x, (x any non-dash char)",
217            ["--"])
218
219    def test_opt_string_long_invalid(self):
220        self.assertOptionError(
221            "invalid long option string '---': "
222            "must start with --, followed by non-dash",
223            ["---"])
224
225    def test_attr_invalid(self):
226        self.assertOptionError(
227            "option -b: invalid keyword arguments: bar, foo",
228            ["-b"], {'foo': None, 'bar': None})
229
230    def test_action_invalid(self):
231        self.assertOptionError(
232            "option -b: invalid action: 'foo'",
233            ["-b"], {'action': 'foo'})
234
235    def test_type_invalid(self):
236        self.assertOptionError(
237            "option -b: invalid option type: 'foo'",
238            ["-b"], {'type': 'foo'})
239        self.assertOptionError(
240            "option -b: invalid option type: 'tuple'",
241            ["-b"], {'type': tuple})
242
243    def test_no_type_for_action(self):
244        self.assertOptionError(
245            "option -b: must not supply a type for action 'count'",
246            ["-b"], {'action': 'count', 'type': 'int'})
247
248    def test_no_choices_list(self):
249        self.assertOptionError(
250            "option -b/--bad: must supply a list of "
251            "choices for type 'choice'",
252            ["-b", "--bad"], {'type': "choice"})
253
254    def test_bad_choices_list(self):
255        typename = type('').__name__
256        self.assertOptionError(
257            "option -b/--bad: choices must be a list of "
258            "strings ('%s' supplied)" % typename,
259            ["-b", "--bad"],
260            {'type': "choice", 'choices':"bad choices"})
261
262    def test_no_choices_for_type(self):
263        self.assertOptionError(
264            "option -b: must not supply choices for type 'int'",
265            ["-b"], {'type': 'int', 'choices':"bad"})
266
267    def test_no_const_for_action(self):
268        self.assertOptionError(
269            "option -b: 'const' must not be supplied for action 'store'",
270            ["-b"], {'action': 'store', 'const': 1})
271
272    def test_no_nargs_for_action(self):
273        self.assertOptionError(
274            "option -b: 'nargs' must not be supplied for action 'count'",
275            ["-b"], {'action': 'count', 'nargs': 2})
276
277    def test_callback_not_callable(self):
278        self.assertOptionError(
279            "option -b: callback not callable: 'foo'",
280            ["-b"], {'action': 'callback',
281                     'callback': 'foo'})
282
283    def dummy(self):
284        pass
285
286    def test_callback_args_no_tuple(self):
287        self.assertOptionError(
288            "option -b: callback_args, if supplied, "
289            "must be a tuple: not 'foo'",
290            ["-b"], {'action': 'callback',
291                     'callback': self.dummy,
292                     'callback_args': 'foo'})
293
294    def test_callback_kwargs_no_dict(self):
295        self.assertOptionError(
296            "option -b: callback_kwargs, if supplied, "
297            "must be a dict: not 'foo'",
298            ["-b"], {'action': 'callback',
299                     'callback': self.dummy,
300                     'callback_kwargs': 'foo'})
301
302    def test_no_callback_for_action(self):
303        self.assertOptionError(
304            "option -b: callback supplied ('foo') for non-callback option",
305            ["-b"], {'action': 'store',
306                     'callback': 'foo'})
307
308    def test_no_callback_args_for_action(self):
309        self.assertOptionError(
310            "option -b: callback_args supplied for non-callback option",
311            ["-b"], {'action': 'store',
312                     'callback_args': 'foo'})
313
314    def test_no_callback_kwargs_for_action(self):
315        self.assertOptionError(
316            "option -b: callback_kwargs supplied for non-callback option",
317            ["-b"], {'action': 'store',
318                     'callback_kwargs': 'foo'})
319
320    def test_no_single_dash(self):
321        self.assertOptionError(
322            "invalid long option string '-debug': "
323            "must start with --, followed by non-dash",
324            ["-debug"])
325
326        self.assertOptionError(
327            "option -d: invalid long option string '-debug': must start with"
328            " --, followed by non-dash",
329            ["-d", "-debug"])
330
331        self.assertOptionError(
332            "invalid long option string '-debug': "
333            "must start with --, followed by non-dash",
334            ["-debug", "--debug"])
335
336class TestOptionParser(BaseTest):
337    def setUp(self):
338        self.parser = OptionParser()
339        self.parser.add_option("-v", "--verbose", "-n", "--noisy",
340                          action="store_true", dest="verbose")
341        self.parser.add_option("-q", "--quiet", "--silent",
342                          action="store_false", dest="verbose")
343
344    def test_add_option_no_Option(self):
345        self.assertTypeError(self.parser.add_option,
346                             "not an Option instance: None", None)
347
348    def test_add_option_invalid_arguments(self):
349        self.assertTypeError(self.parser.add_option,
350                             "invalid arguments", None, None)
351
352    def test_get_option(self):
353        opt1 = self.parser.get_option("-v")
354        self.assertIsInstance(opt1, Option)
355        self.assertEqual(opt1._short_opts, ["-v", "-n"])
356        self.assertEqual(opt1._long_opts, ["--verbose", "--noisy"])
357        self.assertEqual(opt1.action, "store_true")
358        self.assertEqual(opt1.dest, "verbose")
359
360    def test_get_option_equals(self):
361        opt1 = self.parser.get_option("-v")
362        opt2 = self.parser.get_option("--verbose")
363        opt3 = self.parser.get_option("-n")
364        opt4 = self.parser.get_option("--noisy")
365        self.assertTrue(opt1 is opt2 is opt3 is opt4)
366
367    def test_has_option(self):
368        self.assertTrue(self.parser.has_option("-v"))
369        self.assertTrue(self.parser.has_option("--verbose"))
370
371    def assertTrueremoved(self):
372        self.assertTrue(self.parser.get_option("-v") is None)
373        self.assertTrue(self.parser.get_option("--verbose") is None)
374        self.assertTrue(self.parser.get_option("-n") is None)
375        self.assertTrue(self.parser.get_option("--noisy") is None)
376
377        self.assertFalse(self.parser.has_option("-v"))
378        self.assertFalse(self.parser.has_option("--verbose"))
379        self.assertFalse(self.parser.has_option("-n"))
380        self.assertFalse(self.parser.has_option("--noisy"))
381
382        self.assertTrue(self.parser.has_option("-q"))
383        self.assertTrue(self.parser.has_option("--silent"))
384
385    def test_remove_short_opt(self):
386        self.parser.remove_option("-n")
387        self.assertTrueremoved()
388
389    def test_remove_long_opt(self):
390        self.parser.remove_option("--verbose")
391        self.assertTrueremoved()
392
393    def test_remove_nonexistent(self):
394        self.assertRaises(self.parser.remove_option, ('foo',), None,
395                          ValueError, "no such option 'foo'")
396
397    @support.impl_detail('Relies on sys.getrefcount', cpython=True)
398    def test_refleak(self):
399        # If an OptionParser is carrying around a reference to a large
400        # object, various cycles can prevent it from being GC'd in
401        # a timely fashion.  destroy() breaks the cycles to ensure stuff
402        # can be cleaned up.
403        big_thing = [42]
404        refcount = sys.getrefcount(big_thing)
405        parser = OptionParser()
406        parser.add_option("-a", "--aaarggh")
407        parser.big_thing = big_thing
408
409        parser.destroy()
410        #self.assertEqual(refcount, sys.getrefcount(big_thing))
411        del parser
412        self.assertEqual(refcount, sys.getrefcount(big_thing))
413
414
415class TestOptionValues(BaseTest):
416    def setUp(self):
417        pass
418
419    def test_basics(self):
420        values = Values()
421        self.assertEqual(vars(values), {})
422        self.assertEqual(values, {})
423        self.assertNotEqual(values, {"foo": "bar"})
424        self.assertNotEqual(values, "")
425
426        dict = {"foo": "bar", "baz": 42}
427        values = Values(defaults=dict)
428        self.assertEqual(vars(values), dict)
429        self.assertEqual(values, dict)
430        self.assertNotEqual(values, {"foo": "bar"})
431        self.assertNotEqual(values, {})
432        self.assertNotEqual(values, "")
433        self.assertNotEqual(values, [])
434
435
436class TestTypeAliases(BaseTest):
437    def setUp(self):
438        self.parser = OptionParser()
439
440    def test_str_aliases_string(self):
441        self.parser.add_option("-s", type="str")
442        self.assertEqual(self.parser.get_option("-s").type, "string")
443
444    def test_type_object(self):
445        self.parser.add_option("-s", type=str)
446        self.assertEqual(self.parser.get_option("-s").type, "string")
447        self.parser.add_option("-x", type=int)
448        self.assertEqual(self.parser.get_option("-x").type, "int")
449
450
451# Custom type for testing processing of default values.
452_time_units = { 's' : 1, 'm' : 60, 'h' : 60*60, 'd' : 60*60*24 }
453
454def _check_duration(option, opt, value):
455    try:
456        if value[-1].isdigit():
457            return int(value)
458        else:
459            return int(value[:-1]) * _time_units[value[-1]]
460    except (ValueError, IndexError):
461        raise OptionValueError(
462            'option %s: invalid duration: %r' % (opt, value))
463
464class DurationOption(Option):
465    TYPES = Option.TYPES + ('duration',)
466    TYPE_CHECKER = copy.copy(Option.TYPE_CHECKER)
467    TYPE_CHECKER['duration'] = _check_duration
468
469class TestDefaultValues(BaseTest):
470    def setUp(self):
471        self.parser = OptionParser()
472        self.parser.add_option("-v", "--verbose", default=True)
473        self.parser.add_option("-q", "--quiet", dest='verbose')
474        self.parser.add_option("-n", type="int", default=37)
475        self.parser.add_option("-m", type="int")
476        self.parser.add_option("-s", default="foo")
477        self.parser.add_option("-t")
478        self.parser.add_option("-u", default=None)
479        self.expected = { 'verbose': True,
480                          'n': 37,
481                          'm': None,
482                          's': "foo",
483                          't': None,
484                          'u': None }
485
486    def test_basic_defaults(self):
487        self.assertEqual(self.parser.get_default_values(), self.expected)
488
489    def test_mixed_defaults_post(self):
490        self.parser.set_defaults(n=42, m=-100)
491        self.expected.update({'n': 42, 'm': -100})
492        self.assertEqual(self.parser.get_default_values(), self.expected)
493
494    def test_mixed_defaults_pre(self):
495        self.parser.set_defaults(x="barf", y="blah")
496        self.parser.add_option("-x", default="frob")
497        self.parser.add_option("-y")
498
499        self.expected.update({'x': "frob", 'y': "blah"})
500        self.assertEqual(self.parser.get_default_values(), self.expected)
501
502        self.parser.remove_option("-y")
503        self.parser.add_option("-y", default=None)
504        self.expected.update({'y': None})
505        self.assertEqual(self.parser.get_default_values(), self.expected)
506
507    def test_process_default(self):
508        self.parser.option_class = DurationOption
509        self.parser.add_option("-d", type="duration", default=300)
510        self.parser.add_option("-e", type="duration", default="6m")
511        self.parser.set_defaults(n="42")
512        self.expected.update({'d': 300, 'e': 360, 'n': 42})
513        self.assertEqual(self.parser.get_default_values(), self.expected)
514
515        self.parser.set_process_default_values(False)
516        self.expected.update({'d': 300, 'e': "6m", 'n': "42"})
517        self.assertEqual(self.parser.get_default_values(), self.expected)
518
519
520class TestProgName(BaseTest):
521    """
522    Test that %prog expands to the right thing in usage, version,
523    and help strings.
524    """
525
526    def assertUsage(self, parser, expected_usage):
527        self.assertEqual(parser.get_usage(), expected_usage)
528
529    def assertVersion(self, parser, expected_version):
530        self.assertEqual(parser.get_version(), expected_version)
531
532
533    def test_default_progname(self):
534        # Make sure that program name taken from sys.argv[0] by default.
535        save_argv = sys.argv[:]
536        try:
537            sys.argv[0] = os.path.join("foo", "bar", "baz.py")
538            parser = OptionParser("%prog ...", version="%prog 1.2")
539            expected_usage = "Usage: baz.py ...\n"
540            self.assertUsage(parser, expected_usage)
541            self.assertVersion(parser, "baz.py 1.2")
542            self.assertHelp(parser,
543                            expected_usage + "\n" +
544                            "Options:\n"
545                            "  --version   show program's version number and exit\n"
546                            "  -h, --help  show this help message and exit\n")
547        finally:
548            sys.argv[:] = save_argv
549
550    def test_custom_progname(self):
551        parser = OptionParser(prog="thingy",
552                              version="%prog 0.1",
553                              usage="%prog arg arg")
554        parser.remove_option("-h")
555        parser.remove_option("--version")
556        expected_usage = "Usage: thingy arg arg\n"
557        self.assertUsage(parser, expected_usage)
558        self.assertVersion(parser, "thingy 0.1")
559        self.assertHelp(parser, expected_usage + "\n")
560
561
562class TestExpandDefaults(BaseTest):
563    def setUp(self):
564        self.parser = OptionParser(prog="test")
565        self.help_prefix = """\
566Usage: test [options]
567
568Options:
569  -h, --help            show this help message and exit
570"""
571        self.file_help = "read from FILE [default: %default]"
572        self.expected_help_file = self.help_prefix + \
573            "  -f FILE, --file=FILE  read from FILE [default: foo.txt]\n"
574        self.expected_help_none = self.help_prefix + \
575            "  -f FILE, --file=FILE  read from FILE [default: none]\n"
576
577    def test_option_default(self):
578        self.parser.add_option("-f", "--file",
579                               default="foo.txt",
580                               help=self.file_help)
581        self.assertHelp(self.parser, self.expected_help_file)
582
583    def test_parser_default_1(self):
584        self.parser.add_option("-f", "--file",
585                               help=self.file_help)
586        self.parser.set_default('file', "foo.txt")
587        self.assertHelp(self.parser, self.expected_help_file)
588
589    def test_parser_default_2(self):
590        self.parser.add_option("-f", "--file",
591                               help=self.file_help)
592        self.parser.set_defaults(file="foo.txt")
593        self.assertHelp(self.parser, self.expected_help_file)
594
595    def test_no_default(self):
596        self.parser.add_option("-f", "--file",
597                               help=self.file_help)
598        self.assertHelp(self.parser, self.expected_help_none)
599
600    def test_default_none_1(self):
601        self.parser.add_option("-f", "--file",
602                               default=None,
603                               help=self.file_help)
604        self.assertHelp(self.parser, self.expected_help_none)
605
606    def test_default_none_2(self):
607        self.parser.add_option("-f", "--file",
608                               help=self.file_help)
609        self.parser.set_defaults(file=None)
610        self.assertHelp(self.parser, self.expected_help_none)
611
612    def test_float_default(self):
613        self.parser.add_option(
614            "-p", "--prob",
615            help="blow up with probability PROB [default: %default]")
616        self.parser.set_defaults(prob=0.43)
617        expected_help = self.help_prefix + \
618            "  -p PROB, --prob=PROB  blow up with probability PROB [default: 0.43]\n"
619        self.assertHelp(self.parser, expected_help)
620
621    def test_alt_expand(self):
622        self.parser.add_option("-f", "--file",
623                               default="foo.txt",
624                               help="read from FILE [default: *DEFAULT*]")
625        self.parser.formatter.default_tag = "*DEFAULT*"
626        self.assertHelp(self.parser, self.expected_help_file)
627
628    def test_no_expand(self):
629        self.parser.add_option("-f", "--file",
630                               default="foo.txt",
631                               help="read from %default file")
632        self.parser.formatter.default_tag = None
633        expected_help = self.help_prefix + \
634            "  -f FILE, --file=FILE  read from %default file\n"
635        self.assertHelp(self.parser, expected_help)
636
637
638# -- Test parser.parse_args() ------------------------------------------
639
640class TestStandard(BaseTest):
641    def setUp(self):
642        options = [make_option("-a", type="string"),
643                   make_option("-b", "--boo", type="int", dest='boo'),
644                   make_option("--foo", action="append")]
645
646        self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE,
647                                               option_list=options)
648
649    def test_required_value(self):
650        self.assertParseFail(["-a"], "-a option requires 1 argument")
651
652    def test_invalid_integer(self):
653        self.assertParseFail(["-b", "5x"],
654                             "option -b: invalid integer value: '5x'")
655
656    def test_no_such_option(self):
657        self.assertParseFail(["--boo13"], "no such option: --boo13")
658
659    def test_long_invalid_integer(self):
660        self.assertParseFail(["--boo=x5"],
661                             "option --boo: invalid integer value: 'x5'")
662
663    def test_empty(self):
664        self.assertParseOK([], {'a': None, 'boo': None, 'foo': None}, [])
665
666    def test_shortopt_empty_longopt_append(self):
667        self.assertParseOK(["-a", "", "--foo=blah", "--foo="],
668                           {'a': "", 'boo': None, 'foo': ["blah", ""]},
669                           [])
670
671    def test_long_option_append(self):
672        self.assertParseOK(["--foo", "bar", "--foo", "", "--foo=x"],
673                           {'a': None,
674                            'boo': None,
675                            'foo': ["bar", "", "x"]},
676                           [])
677
678    def test_option_argument_joined(self):
679        self.assertParseOK(["-abc"],
680                           {'a': "bc", 'boo': None, 'foo': None},
681                           [])
682
683    def test_option_argument_split(self):
684        self.assertParseOK(["-a", "34"],
685                           {'a': "34", 'boo': None, 'foo': None},
686                           [])
687
688    def test_option_argument_joined_integer(self):
689        self.assertParseOK(["-b34"],
690                           {'a': None, 'boo': 34, 'foo': None},
691                           [])
692
693    def test_option_argument_split_negative_integer(self):
694        self.assertParseOK(["-b", "-5"],
695                           {'a': None, 'boo': -5, 'foo': None},
696                           [])
697
698    def test_long_option_argument_joined(self):
699        self.assertParseOK(["--boo=13"],
700                           {'a': None, 'boo': 13, 'foo': None},
701                           [])
702
703    def test_long_option_argument_split(self):
704        self.assertParseOK(["--boo", "111"],
705                           {'a': None, 'boo': 111, 'foo': None},
706                           [])
707
708    def test_long_option_short_option(self):
709        self.assertParseOK(["--foo=bar", "-axyz"],
710                           {'a': 'xyz', 'boo': None, 'foo': ["bar"]},
711                           [])
712
713    def test_abbrev_long_option(self):
714        self.assertParseOK(["--f=bar", "-axyz"],
715                           {'a': 'xyz', 'boo': None, 'foo': ["bar"]},
716                           [])
717
718    def test_defaults(self):
719        (options, args) = self.parser.parse_args([])
720        defaults = self.parser.get_default_values()
721        self.assertEqual(vars(defaults), vars(options))
722
723    def test_ambiguous_option(self):
724        self.parser.add_option("--foz", action="store",
725                               type="string", dest="foo")
726        self.assertParseFail(["--f=bar"],
727                             "ambiguous option: --f (--foo, --foz?)")
728
729
730    def test_short_and_long_option_split(self):
731        self.assertParseOK(["-a", "xyz", "--foo", "bar"],
732                           {'a': 'xyz', 'boo': None, 'foo': ["bar"]},
733                           [])
734
735    def test_short_option_split_long_option_append(self):
736        self.assertParseOK(["--foo=bar", "-b", "123", "--foo", "baz"],
737                           {'a': None, 'boo': 123, 'foo': ["bar", "baz"]},
738                           [])
739
740    def test_short_option_split_one_positional_arg(self):
741        self.assertParseOK(["-a", "foo", "bar"],
742                           {'a': "foo", 'boo': None, 'foo': None},
743                           ["bar"])
744
745    def test_short_option_consumes_separator(self):
746        self.assertParseOK(["-a", "--", "foo", "bar"],
747                           {'a': "--", 'boo': None, 'foo': None},
748                           ["foo", "bar"])
749        self.assertParseOK(["-a", "--", "--foo", "bar"],
750                           {'a': "--", 'boo': None, 'foo': ["bar"]},
751                           [])
752
753    def test_short_option_joined_and_separator(self):
754        self.assertParseOK(["-ab", "--", "--foo", "bar"],
755                           {'a': "b", 'boo': None, 'foo': None},
756                           ["--foo", "bar"]),
757
758    def test_hyphen_becomes_positional_arg(self):
759        self.assertParseOK(["-ab", "-", "--foo", "bar"],
760                           {'a': "b", 'boo': None, 'foo': ["bar"]},
761                           ["-"])
762
763    def test_no_append_versus_append(self):
764        self.assertParseOK(["-b3", "-b", "5", "--foo=bar", "--foo", "baz"],
765                           {'a': None, 'boo': 5, 'foo': ["bar", "baz"]},
766                           [])
767
768    def test_option_consumes_optionlike_string(self):
769        self.assertParseOK(["-a", "-b3"],
770                           {'a': "-b3", 'boo': None, 'foo': None},
771                           [])
772
773    def test_combined_single_invalid_option(self):
774        self.parser.add_option("-t", action="store_true")
775        self.assertParseFail(["-test"],
776                             "no such option: -e")
777
778class TestBool(BaseTest):
779    def setUp(self):
780        options = [make_option("-v",
781                               "--verbose",
782                               action="store_true",
783                               dest="verbose",
784                               default=''),
785                   make_option("-q",
786                               "--quiet",
787                               action="store_false",
788                               dest="verbose")]
789        self.parser = OptionParser(option_list = options)
790
791    def test_bool_default(self):
792        self.assertParseOK([],
793                           {'verbose': ''},
794                           [])
795
796    def test_bool_false(self):
797        (options, args) = self.assertParseOK(["-q"],
798                                             {'verbose': 0},
799                                             [])
800        self.assertTrue(options.verbose is False)
801
802    def test_bool_true(self):
803        (options, args) = self.assertParseOK(["-v"],
804                                             {'verbose': 1},
805                                             [])
806        self.assertTrue(options.verbose is True)
807
808    def test_bool_flicker_on_and_off(self):
809        self.assertParseOK(["-qvq", "-q", "-v"],
810                           {'verbose': 1},
811                           [])
812
813class TestChoice(BaseTest):
814    def setUp(self):
815        self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE)
816        self.parser.add_option("-c", action="store", type="choice",
817                               dest="choice", choices=["one", "two", "three"])
818
819    def test_valid_choice(self):
820        self.assertParseOK(["-c", "one", "xyz"],
821                           {'choice': 'one'},
822                           ["xyz"])
823
824    def test_invalid_choice(self):
825        self.assertParseFail(["-c", "four", "abc"],
826                             "option -c: invalid choice: 'four' "
827                             "(choose from 'one', 'two', 'three')")
828
829    def test_add_choice_option(self):
830        self.parser.add_option("-d", "--default",
831                               choices=["four", "five", "six"])
832        opt = self.parser.get_option("-d")
833        self.assertEqual(opt.type, "choice")
834        self.assertEqual(opt.action, "store")
835
836class TestCount(BaseTest):
837    def setUp(self):
838        self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE)
839        self.v_opt = make_option("-v", action="count", dest="verbose")
840        self.parser.add_option(self.v_opt)
841        self.parser.add_option("--verbose", type="int", dest="verbose")
842        self.parser.add_option("-q", "--quiet",
843                               action="store_const", dest="verbose", const=0)
844
845    def test_empty(self):
846        self.assertParseOK([], {'verbose': None}, [])
847
848    def test_count_one(self):
849        self.assertParseOK(["-v"], {'verbose': 1}, [])
850
851    def test_count_three(self):
852        self.assertParseOK(["-vvv"], {'verbose': 3}, [])
853
854    def test_count_three_apart(self):
855        self.assertParseOK(["-v", "-v", "-v"], {'verbose': 3}, [])
856
857    def test_count_override_amount(self):
858        self.assertParseOK(["-vvv", "--verbose=2"], {'verbose': 2}, [])
859
860    def test_count_override_quiet(self):
861        self.assertParseOK(["-vvv", "--verbose=2", "-q"], {'verbose': 0}, [])
862
863    def test_count_overriding(self):
864        self.assertParseOK(["-vvv", "--verbose=2", "-q", "-v"],
865                           {'verbose': 1}, [])
866
867    def test_count_interspersed_args(self):
868        self.assertParseOK(["--quiet", "3", "-v"],
869                           {'verbose': 1},
870                           ["3"])
871
872    def test_count_no_interspersed_args(self):
873        self.parser.disable_interspersed_args()
874        self.assertParseOK(["--quiet", "3", "-v"],
875                           {'verbose': 0},
876                           ["3", "-v"])
877
878    def test_count_no_such_option(self):
879        self.assertParseFail(["-q3", "-v"], "no such option: -3")
880
881    def test_count_option_no_value(self):
882        self.assertParseFail(["--quiet=3", "-v"],
883                             "--quiet option does not take a value")
884
885    def test_count_with_default(self):
886        self.parser.set_default('verbose', 0)
887        self.assertParseOK([], {'verbose':0}, [])
888
889    def test_count_overriding_default(self):
890        self.parser.set_default('verbose', 0)
891        self.assertParseOK(["-vvv", "--verbose=2", "-q", "-v"],
892                           {'verbose': 1}, [])
893
894class TestMultipleArgs(BaseTest):
895    def setUp(self):
896        self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE)
897        self.parser.add_option("-p", "--point",
898                               action="store", nargs=3, type="float", dest="point")
899
900    def test_nargs_with_positional_args(self):
901        self.assertParseOK(["foo", "-p", "1", "2.5", "-4.3", "xyz"],
902                           {'point': (1.0, 2.5, -4.3)},
903                           ["foo", "xyz"])
904
905    def test_nargs_long_opt(self):
906        self.assertParseOK(["--point", "-1", "2.5", "-0", "xyz"],
907                           {'point': (-1.0, 2.5, -0.0)},
908                           ["xyz"])
909
910    def test_nargs_invalid_float_value(self):
911        self.assertParseFail(["-p", "1.0", "2x", "3.5"],
912                             "option -p: "
913                             "invalid floating-point value: '2x'")
914
915    def test_nargs_required_values(self):
916        self.assertParseFail(["--point", "1.0", "3.5"],
917                             "--point option requires 3 arguments")
918
919class TestMultipleArgsAppend(BaseTest):
920    def setUp(self):
921        self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE)
922        self.parser.add_option("-p", "--point", action="store", nargs=3,
923                               type="float", dest="point")
924        self.parser.add_option("-f", "--foo", action="append", nargs=2,
925                               type="int", dest="foo")
926        self.parser.add_option("-z", "--zero", action="append_const",
927                               dest="foo", const=(0, 0))
928
929    def test_nargs_append(self):
930        self.assertParseOK(["-f", "4", "-3", "blah", "--foo", "1", "666"],
931                           {'point': None, 'foo': [(4, -3), (1, 666)]},
932                           ["blah"])
933
934    def test_nargs_append_required_values(self):
935        self.assertParseFail(["-f4,3"],
936                             "-f option requires 2 arguments")
937
938    def test_nargs_append_simple(self):
939        self.assertParseOK(["--foo=3", "4"],
940                           {'point': None, 'foo':[(3, 4)]},
941                           [])
942
943    def test_nargs_append_const(self):
944        self.assertParseOK(["--zero", "--foo", "3", "4", "-z"],
945                           {'point': None, 'foo':[(0, 0), (3, 4), (0, 0)]},
946                           [])
947
948class TestVersion(BaseTest):
949    def test_version(self):
950        self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE,
951                                               version="%prog 0.1")
952        save_argv = sys.argv[:]
953        try:
954            sys.argv[0] = os.path.join(os.curdir, "foo", "bar")
955            self.assertOutput(["--version"], "bar 0.1\n")
956        finally:
957            sys.argv[:] = save_argv
958
959    def test_no_version(self):
960        self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE)
961        self.assertParseFail(["--version"],
962                             "no such option: --version")
963
964# -- Test conflicting default values and parser.parse_args() -----------
965
966class TestConflictingDefaults(BaseTest):
967    """Conflicting default values: the last one should win."""
968    def setUp(self):
969        self.parser = OptionParser(option_list=[
970            make_option("-v", action="store_true", dest="verbose", default=1)])
971
972    def test_conflict_default(self):
973        self.parser.add_option("-q", action="store_false", dest="verbose",
974                               default=0)
975        self.assertParseOK([], {'verbose': 0}, [])
976
977    def test_conflict_default_none(self):
978        self.parser.add_option("-q", action="store_false", dest="verbose",
979                               default=None)
980        self.assertParseOK([], {'verbose': None}, [])
981
982class TestOptionGroup(BaseTest):
983    def setUp(self):
984        self.parser = OptionParser(usage=SUPPRESS_USAGE)
985
986    def test_option_group_create_instance(self):
987        group = OptionGroup(self.parser, "Spam")
988        self.parser.add_option_group(group)
989        group.add_option("--spam", action="store_true",
990                         help="spam spam spam spam")
991        self.assertParseOK(["--spam"], {'spam': 1}, [])
992
993    def test_add_group_no_group(self):
994        self.assertTypeError(self.parser.add_option_group,
995                             "not an OptionGroup instance: None", None)
996
997    def test_add_group_invalid_arguments(self):
998        self.assertTypeError(self.parser.add_option_group,
999                             "invalid arguments", None, None)
1000
1001    def test_add_group_wrong_parser(self):
1002        group = OptionGroup(self.parser, "Spam")
1003        group.parser = OptionParser()
1004        self.assertRaises(self.parser.add_option_group, (group,), None,
1005                          ValueError, "invalid OptionGroup (wrong parser)")
1006
1007    def test_group_manipulate(self):
1008        group = self.parser.add_option_group("Group 2",
1009                                             description="Some more options")
1010        group.set_title("Bacon")
1011        group.add_option("--bacon", type="int")
1012        self.assertTrue(self.parser.get_option_group("--bacon"), group)
1013
1014# -- Test extending and parser.parse_args() ----------------------------
1015
1016class TestExtendAddTypes(BaseTest):
1017    def setUp(self):
1018        self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE,
1019                                               option_class=self.MyOption)
1020        self.parser.add_option("-a", None, type="string", dest="a")
1021        self.parser.add_option("-f", "--file", type="file", dest="file")
1022
1023    def tearDown(self):
1024        if os.path.isdir(support.TESTFN):
1025            os.rmdir(support.TESTFN)
1026        elif os.path.isfile(support.TESTFN):
1027            os.unlink(support.TESTFN)
1028
1029    class MyOption (Option):
1030        def check_file(option, opt, value):
1031            if not os.path.exists(value):
1032                raise OptionValueError("%s: file does not exist" % value)
1033            elif not os.path.isfile(value):
1034                raise OptionValueError("%s: not a regular file" % value)
1035            return value
1036
1037        TYPES = Option.TYPES + ("file",)
1038        TYPE_CHECKER = copy.copy(Option.TYPE_CHECKER)
1039        TYPE_CHECKER["file"] = check_file
1040
1041    def test_filetype_ok(self):
1042        support.create_empty_file(support.TESTFN)
1043        self.assertParseOK(["--file", support.TESTFN, "-afoo"],
1044                           {'file': support.TESTFN, 'a': 'foo'},
1045                           [])
1046
1047    def test_filetype_noexist(self):
1048        self.assertParseFail(["--file", support.TESTFN, "-afoo"],
1049                             "%s: file does not exist" %
1050                             support.TESTFN)
1051
1052    def test_filetype_notfile(self):
1053        os.mkdir(support.TESTFN)
1054        self.assertParseFail(["--file", support.TESTFN, "-afoo"],
1055                             "%s: not a regular file" %
1056                             support.TESTFN)
1057
1058
1059class TestExtendAddActions(BaseTest):
1060    def setUp(self):
1061        options = [self.MyOption("-a", "--apple", action="extend",
1062                                 type="string", dest="apple")]
1063        self.parser = OptionParser(option_list=options)
1064
1065    class MyOption (Option):
1066        ACTIONS = Option.ACTIONS + ("extend",)
1067        STORE_ACTIONS = Option.STORE_ACTIONS + ("extend",)
1068        TYPED_ACTIONS = Option.TYPED_ACTIONS + ("extend",)
1069
1070        def take_action(self, action, dest, opt, value, values, parser):
1071            if action == "extend":
1072                lvalue = value.split(",")
1073                values.ensure_value(dest, []).extend(lvalue)
1074            else:
1075                Option.take_action(self, action, dest, opt, parser, value,
1076                                   values)
1077
1078    def test_extend_add_action(self):
1079        self.assertParseOK(["-afoo,bar", "--apple=blah"],
1080                           {'apple': ["foo", "bar", "blah"]},
1081                           [])
1082
1083    def test_extend_add_action_normal(self):
1084        self.assertParseOK(["-a", "foo", "-abar", "--apple=x,y"],
1085                           {'apple': ["foo", "bar", "x", "y"]},
1086                           [])
1087
1088# -- Test callbacks and parser.parse_args() ----------------------------
1089
1090class TestCallback(BaseTest):
1091    def setUp(self):
1092        options = [make_option("-x",
1093                               None,
1094                               action="callback",
1095                               callback=self.process_opt),
1096                   make_option("-f",
1097                               "--file",
1098                               action="callback",
1099                               callback=self.process_opt,
1100                               type="string",
1101                               dest="filename")]
1102        self.parser = OptionParser(option_list=options)
1103
1104    def process_opt(self, option, opt, value, parser_):
1105        if opt == "-x":
1106            self.assertEqual(option._short_opts, ["-x"])
1107            self.assertEqual(option._long_opts, [])
1108            self.assertTrue(parser_ is self.parser)
1109            self.assertTrue(value is None)
1110            self.assertEqual(vars(parser_.values), {'filename': None})
1111
1112            parser_.values.x = 42
1113        elif opt == "--file":
1114            self.assertEqual(option._short_opts, ["-f"])
1115            self.assertEqual(option._long_opts, ["--file"])
1116            self.assertTrue(parser_ is self.parser)
1117            self.assertEqual(value, "foo")
1118            self.assertEqual(vars(parser_.values), {'filename': None, 'x': 42})
1119
1120            setattr(parser_.values, option.dest, value)
1121        else:
1122            self.fail("Unknown option %r in process_opt." % opt)
1123
1124    def test_callback(self):
1125        self.assertParseOK(["-x", "--file=foo"],
1126                           {'filename': "foo", 'x': 42},
1127                           [])
1128
1129    def test_callback_help(self):
1130        # This test was prompted by SF bug #960515 -- the point is
1131        # not to inspect the help text, just to make sure that
1132        # format_help() doesn't crash.
1133        parser = OptionParser(usage=SUPPRESS_USAGE)
1134        parser.remove_option("-h")
1135        parser.add_option("-t", "--test", action="callback",
1136                          callback=lambda: None, type="string",
1137                          help="foo")
1138
1139        expected_help = ("Options:\n"
1140                         "  -t TEST, --test=TEST  foo\n")
1141        self.assertHelp(parser, expected_help)
1142
1143
1144class TestCallbackExtraArgs(BaseTest):
1145    def setUp(self):
1146        options = [make_option("-p", "--point", action="callback",
1147                               callback=self.process_tuple,
1148                               callback_args=(3, int), type="string",
1149                               dest="points", default=[])]
1150        self.parser = OptionParser(option_list=options)
1151
1152    def process_tuple(self, option, opt, value, parser_, len, type):
1153        self.assertEqual(len, 3)
1154        self.assertTrue(type is int)
1155
1156        if opt == "-p":
1157            self.assertEqual(value, "1,2,3")
1158        elif opt == "--point":
1159            self.assertEqual(value, "4,5,6")
1160
1161        value = tuple(map(type, value.split(",")))
1162        getattr(parser_.values, option.dest).append(value)
1163
1164    def test_callback_extra_args(self):
1165        self.assertParseOK(["-p1,2,3", "--point", "4,5,6"],
1166                           {'points': [(1,2,3), (4,5,6)]},
1167                           [])
1168
1169class TestCallbackMeddleArgs(BaseTest):
1170    def setUp(self):
1171        options = [make_option(str(x), action="callback",
1172                               callback=self.process_n, dest='things')
1173                   for x in range(-1, -6, -1)]
1174        self.parser = OptionParser(option_list=options)
1175
1176    # Callback that meddles in rargs, largs
1177    def process_n(self, option, opt, value, parser_):
1178        # option is -3, -5, etc.
1179        nargs = int(opt[1:])
1180        rargs = parser_.rargs
1181        if len(rargs) < nargs:
1182            self.fail("Expected %d arguments for %s option." % (nargs, opt))
1183        dest = parser_.values.ensure_value(option.dest, [])
1184        dest.append(tuple(rargs[0:nargs]))
1185        parser_.largs.append(nargs)
1186        del rargs[0:nargs]
1187
1188    def test_callback_meddle_args(self):
1189        self.assertParseOK(["-1", "foo", "-3", "bar", "baz", "qux"],
1190                           {'things': [("foo",), ("bar", "baz", "qux")]},
1191                           [1, 3])
1192
1193    def test_callback_meddle_args_separator(self):
1194        self.assertParseOK(["-2", "foo", "--"],
1195                           {'things': [('foo', '--')]},
1196                           [2])
1197
1198class TestCallbackManyArgs(BaseTest):
1199    def setUp(self):
1200        options = [make_option("-a", "--apple", action="callback", nargs=2,
1201                               callback=self.process_many, type="string"),
1202                   make_option("-b", "--bob", action="callback", nargs=3,
1203                               callback=self.process_many, type="int")]
1204        self.parser = OptionParser(option_list=options)
1205
1206    def process_many(self, option, opt, value, parser_):
1207        if opt == "-a":
1208            self.assertEqual(value, ("foo", "bar"))
1209        elif opt == "--apple":
1210            self.assertEqual(value, ("ding", "dong"))
1211        elif opt == "-b":
1212            self.assertEqual(value, (1, 2, 3))
1213        elif opt == "--bob":
1214            self.assertEqual(value, (-666, 42, 0))
1215
1216    def test_many_args(self):
1217        self.assertParseOK(["-a", "foo", "bar", "--apple", "ding", "dong",
1218                            "-b", "1", "2", "3", "--bob", "-666", "42",
1219                            "0"],
1220                           {"apple": None, "bob": None},
1221                           [])
1222
1223class TestCallbackCheckAbbrev(BaseTest):
1224    def setUp(self):
1225        self.parser = OptionParser()
1226        self.parser.add_option("--foo-bar", action="callback",
1227                               callback=self.check_abbrev)
1228
1229    def check_abbrev(self, option, opt, value, parser):
1230        self.assertEqual(opt, "--foo-bar")
1231
1232    def test_abbrev_callback_expansion(self):
1233        self.assertParseOK(["--foo"], {}, [])
1234
1235class TestCallbackVarArgs(BaseTest):
1236    def setUp(self):
1237        options = [make_option("-a", type="int", nargs=2, dest="a"),
1238                   make_option("-b", action="store_true", dest="b"),
1239                   make_option("-c", "--callback", action="callback",
1240                               callback=self.variable_args, dest="c")]
1241        self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE,
1242                                               option_list=options)
1243
1244    def variable_args(self, option, opt, value, parser):
1245        self.assertTrue(value is None)
1246        value = []
1247        rargs = parser.rargs
1248        while rargs:
1249            arg = rargs[0]
1250            if ((arg[:2] == "--" and len(arg) > 2) or
1251                (arg[:1] == "-" and len(arg) > 1 and arg[1] != "-")):
1252                break
1253            else:
1254                value.append(arg)
1255                del rargs[0]
1256        setattr(parser.values, option.dest, value)
1257
1258    def test_variable_args(self):
1259        self.assertParseOK(["-a3", "-5", "--callback", "foo", "bar"],
1260                           {'a': (3, -5), 'b': None, 'c': ["foo", "bar"]},
1261                           [])
1262
1263    def test_consume_separator_stop_at_option(self):
1264        self.assertParseOK(["-c", "37", "--", "xxx", "-b", "hello"],
1265                           {'a': None,
1266                            'b': True,
1267                            'c': ["37", "--", "xxx"]},
1268                           ["hello"])
1269
1270    def test_positional_arg_and_variable_args(self):
1271        self.assertParseOK(["hello", "-c", "foo", "-", "bar"],
1272                           {'a': None,
1273                            'b': None,
1274                            'c':["foo", "-", "bar"]},
1275                           ["hello"])
1276
1277    def test_stop_at_option(self):
1278        self.assertParseOK(["-c", "foo", "-b"],
1279                           {'a': None, 'b': True, 'c': ["foo"]},
1280                           [])
1281
1282    def test_stop_at_invalid_option(self):
1283        self.assertParseFail(["-c", "3", "-5", "-a"], "no such option: -5")
1284
1285
1286# -- Test conflict handling and parser.parse_args() --------------------
1287
1288class ConflictBase(BaseTest):
1289    def setUp(self):
1290        options = [make_option("-v", "--verbose", action="count",
1291                               dest="verbose", help="increment verbosity")]
1292        self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE,
1293                                               option_list=options)
1294
1295    def show_version(self, option, opt, value, parser):
1296        parser.values.show_version = 1
1297
1298class TestConflict(ConflictBase):
1299    """Use the default conflict resolution for Optik 1.2: error."""
1300    def assertTrueconflict_error(self, func):
1301        err = self.assertRaises(
1302            func, ("-v", "--version"), {'action' : "callback",
1303                                        'callback' : self.show_version,
1304                                        'help' : "show version"},
1305            OptionConflictError,
1306            "option -v/--version: conflicting option string(s): -v")
1307
1308        self.assertEqual(err.msg, "conflicting option string(s): -v")
1309        self.assertEqual(err.option_id, "-v/--version")
1310
1311    def test_conflict_error(self):
1312        self.assertTrueconflict_error(self.parser.add_option)
1313
1314    def test_conflict_error_group(self):
1315        group = OptionGroup(self.parser, "Group 1")
1316        self.assertTrueconflict_error(group.add_option)
1317
1318    def test_no_such_conflict_handler(self):
1319        self.assertRaises(
1320            self.parser.set_conflict_handler, ('foo',), None,
1321            ValueError, "invalid conflict_resolution value 'foo'")
1322
1323
1324class TestConflictResolve(ConflictBase):
1325    def setUp(self):
1326        ConflictBase.setUp(self)
1327        self.parser.set_conflict_handler("resolve")
1328        self.parser.add_option("-v", "--version", action="callback",
1329                               callback=self.show_version, help="show version")
1330
1331    def test_conflict_resolve(self):
1332        v_opt = self.parser.get_option("-v")
1333        verbose_opt = self.parser.get_option("--verbose")
1334        version_opt = self.parser.get_option("--version")
1335
1336        self.assertTrue(v_opt is version_opt)
1337        self.assertTrue(v_opt is not verbose_opt)
1338        self.assertEqual(v_opt._long_opts, ["--version"])
1339        self.assertEqual(version_opt._short_opts, ["-v"])
1340        self.assertEqual(version_opt._long_opts, ["--version"])
1341        self.assertEqual(verbose_opt._short_opts, [])
1342        self.assertEqual(verbose_opt._long_opts, ["--verbose"])
1343
1344    def test_conflict_resolve_help(self):
1345        self.assertOutput(["-h"], """\
1346Options:
1347  --verbose      increment verbosity
1348  -h, --help     show this help message and exit
1349  -v, --version  show version
1350""")
1351
1352    def test_conflict_resolve_short_opt(self):
1353        self.assertParseOK(["-v"],
1354                           {'verbose': None, 'show_version': 1},
1355                           [])
1356
1357    def test_conflict_resolve_long_opt(self):
1358        self.assertParseOK(["--verbose"],
1359                           {'verbose': 1},
1360                           [])
1361
1362    def test_conflict_resolve_long_opts(self):
1363        self.assertParseOK(["--verbose", "--version"],
1364                           {'verbose': 1, 'show_version': 1},
1365                           [])
1366
1367class TestConflictOverride(BaseTest):
1368    def setUp(self):
1369        self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE)
1370        self.parser.set_conflict_handler("resolve")
1371        self.parser.add_option("-n", "--dry-run",
1372                               action="store_true", dest="dry_run",
1373                               help="don't do anything")
1374        self.parser.add_option("--dry-run", "-n",
1375                               action="store_const", const=42, dest="dry_run",
1376                               help="dry run mode")
1377
1378    def test_conflict_override_opts(self):
1379        opt = self.parser.get_option("--dry-run")
1380        self.assertEqual(opt._short_opts, ["-n"])
1381        self.assertEqual(opt._long_opts, ["--dry-run"])
1382
1383    def test_conflict_override_help(self):
1384        self.assertOutput(["-h"], """\
1385Options:
1386  -h, --help     show this help message and exit
1387  -n, --dry-run  dry run mode
1388""")
1389
1390    def test_conflict_override_args(self):
1391        self.assertParseOK(["-n"],
1392                           {'dry_run': 42},
1393                           [])
1394
1395# -- Other testing. ----------------------------------------------------
1396
1397_expected_help_basic = """\
1398Usage: bar.py [options]
1399
1400Options:
1401  -a APPLE           throw APPLEs at basket
1402  -b NUM, --boo=NUM  shout "boo!" NUM times (in order to frighten away all the
1403                     evil spirits that cause trouble and mayhem)
1404  --foo=FOO          store FOO in the foo list for later fooing
1405  -h, --help         show this help message and exit
1406"""
1407
1408_expected_help_long_opts_first = """\
1409Usage: bar.py [options]
1410
1411Options:
1412  -a APPLE           throw APPLEs at basket
1413  --boo=NUM, -b NUM  shout "boo!" NUM times (in order to frighten away all the
1414                     evil spirits that cause trouble and mayhem)
1415  --foo=FOO          store FOO in the foo list for later fooing
1416  --help, -h         show this help message and exit
1417"""
1418
1419_expected_help_title_formatter = """\
1420Usage
1421=====
1422  bar.py [options]
1423
1424Options
1425=======
1426-a APPLE           throw APPLEs at basket
1427--boo=NUM, -b NUM  shout "boo!" NUM times (in order to frighten away all the
1428                   evil spirits that cause trouble and mayhem)
1429--foo=FOO          store FOO in the foo list for later fooing
1430--help, -h         show this help message and exit
1431"""
1432
1433_expected_help_short_lines = """\
1434Usage: bar.py [options]
1435
1436Options:
1437  -a APPLE           throw APPLEs at basket
1438  -b NUM, --boo=NUM  shout "boo!" NUM times (in order to
1439                     frighten away all the evil spirits
1440                     that cause trouble and mayhem)
1441  --foo=FOO          store FOO in the foo list for later
1442                     fooing
1443  -h, --help         show this help message and exit
1444"""
1445
1446_expected_very_help_short_lines = """\
1447Usage: bar.py [options]
1448
1449Options:
1450  -a APPLE
1451    throw
1452    APPLEs at
1453    basket
1454  -b NUM, --boo=NUM
1455    shout
1456    "boo!" NUM
1457    times (in
1458    order to
1459    frighten
1460    away all
1461    the evil
1462    spirits
1463    that cause
1464    trouble and
1465    mayhem)
1466  --foo=FOO
1467    store FOO
1468    in the foo
1469    list for
1470    later
1471    fooing
1472  -h, --help
1473    show this
1474    help
1475    message and
1476    exit
1477"""
1478
1479class TestHelp(BaseTest):
1480    def setUp(self):
1481        self.parser = self.make_parser(80)
1482
1483    def make_parser(self, columns):
1484        options = [
1485            make_option("-a", type="string", dest='a',
1486                        metavar="APPLE", help="throw APPLEs at basket"),
1487            make_option("-b", "--boo", type="int", dest='boo',
1488                        metavar="NUM",
1489                        help=
1490                        "shout \"boo!\" NUM times (in order to frighten away "
1491                        "all the evil spirits that cause trouble and mayhem)"),
1492            make_option("--foo", action="append", type="string", dest='foo',
1493                        help="store FOO in the foo list for later fooing"),
1494            ]
1495
1496        # We need to set COLUMNS for the OptionParser constructor, but
1497        # we must restore its original value -- otherwise, this test
1498        # screws things up for other tests when it's part of the Python
1499        # test suite.
1500        with support.EnvironmentVarGuard() as env:
1501            env['COLUMNS'] = str(columns)
1502            return InterceptingOptionParser(option_list=options)
1503
1504    def assertHelpEquals(self, expected_output):
1505        save_argv = sys.argv[:]
1506        try:
1507            # Make optparse believe bar.py is being executed.
1508            sys.argv[0] = os.path.join("foo", "bar.py")
1509            self.assertOutput(["-h"], expected_output)
1510        finally:
1511            sys.argv[:] = save_argv
1512
1513    def test_help(self):
1514        self.assertHelpEquals(_expected_help_basic)
1515
1516    def test_help_old_usage(self):
1517        self.parser.set_usage("Usage: %prog [options]")
1518        self.assertHelpEquals(_expected_help_basic)
1519
1520    def test_help_long_opts_first(self):
1521        self.parser.formatter.short_first = 0
1522        self.assertHelpEquals(_expected_help_long_opts_first)
1523
1524    def test_help_title_formatter(self):
1525        with support.EnvironmentVarGuard() as env:
1526            env["COLUMNS"] = "80"
1527            self.parser.formatter = TitledHelpFormatter()
1528            self.assertHelpEquals(_expected_help_title_formatter)
1529
1530    def test_wrap_columns(self):
1531        # Ensure that wrapping respects $COLUMNS environment variable.
1532        # Need to reconstruct the parser, since that's the only time
1533        # we look at $COLUMNS.
1534        self.parser = self.make_parser(60)
1535        self.assertHelpEquals(_expected_help_short_lines)
1536        self.parser = self.make_parser(0)
1537        self.assertHelpEquals(_expected_very_help_short_lines)
1538
1539    def test_help_unicode(self):
1540        self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE)
1541        self.parser.add_option("-a", action="store_true", help="ol\u00E9!")
1542        expect = """\
1543Options:
1544  -h, --help  show this help message and exit
1545  -a          ol\u00E9!
1546"""
1547        self.assertHelpEquals(expect)
1548
1549    def test_help_unicode_description(self):
1550        self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE,
1551                                               description="ol\u00E9!")
1552        expect = """\
1553ol\u00E9!
1554
1555Options:
1556  -h, --help  show this help message and exit
1557"""
1558        self.assertHelpEquals(expect)
1559
1560    def test_help_description_groups(self):
1561        self.parser.set_description(
1562            "This is the program description for %prog.  %prog has "
1563            "an option group as well as single options.")
1564
1565        group = OptionGroup(
1566            self.parser, "Dangerous Options",
1567            "Caution: use of these options is at your own risk.  "
1568            "It is believed that some of them bite.")
1569        group.add_option("-g", action="store_true", help="Group option.")
1570        self.parser.add_option_group(group)
1571
1572        expect = """\
1573Usage: bar.py [options]
1574
1575This is the program description for bar.py.  bar.py has an option group as
1576well as single options.
1577
1578Options:
1579  -a APPLE           throw APPLEs at basket
1580  -b NUM, --boo=NUM  shout "boo!" NUM times (in order to frighten away all the
1581                     evil spirits that cause trouble and mayhem)
1582  --foo=FOO          store FOO in the foo list for later fooing
1583  -h, --help         show this help message and exit
1584
1585  Dangerous Options:
1586    Caution: use of these options is at your own risk.  It is believed
1587    that some of them bite.
1588
1589    -g               Group option.
1590"""
1591
1592        self.assertHelpEquals(expect)
1593
1594        self.parser.epilog = "Please report bugs to /dev/null."
1595        self.assertHelpEquals(expect + "\nPlease report bugs to /dev/null.\n")
1596
1597
1598class TestMatchAbbrev(BaseTest):
1599    def test_match_abbrev(self):
1600        self.assertEqual(_match_abbrev("--f",
1601                                       {"--foz": None,
1602                                        "--foo": None,
1603                                        "--fie": None,
1604                                        "--f": None}),
1605                         "--f")
1606
1607    def test_match_abbrev_error(self):
1608        s = "--f"
1609        wordmap = {"--foz": None, "--foo": None, "--fie": None}
1610        self.assertRaises(
1611            _match_abbrev, (s, wordmap), None,
1612            BadOptionError, "ambiguous option: --f (--fie, --foo, --foz?)")
1613
1614
1615class TestParseNumber(BaseTest):
1616    def setUp(self):
1617        self.parser = InterceptingOptionParser()
1618        self.parser.add_option("-n", type=int)
1619        self.parser.add_option("-l", type=int)
1620
1621    def test_parse_num_fail(self):
1622        self.assertRaises(
1623            _parse_num, ("", int), {},
1624            ValueError,
1625            re.compile(r"invalid literal for int().*: '?'?"))
1626        self.assertRaises(
1627            _parse_num, ("0xOoops", int), {},
1628            ValueError,
1629            re.compile(r"invalid literal for int().*: s?'?0xOoops'?"))
1630
1631    def test_parse_num_ok(self):
1632        self.assertEqual(_parse_num("0", int), 0)
1633        self.assertEqual(_parse_num("0x10", int), 16)
1634        self.assertEqual(_parse_num("0XA", int), 10)
1635        self.assertEqual(_parse_num("010", int), 8)
1636        self.assertEqual(_parse_num("0b11", int), 3)
1637        self.assertEqual(_parse_num("0b", int), 0)
1638
1639    def test_numeric_options(self):
1640        self.assertParseOK(["-n", "42", "-l", "0x20"],
1641                           { "n": 42, "l": 0x20 }, [])
1642        self.assertParseOK(["-n", "0b0101", "-l010"],
1643                           { "n": 5, "l": 8 }, [])
1644        self.assertParseFail(["-n008"],
1645                             "option -n: invalid integer value: '008'")
1646        self.assertParseFail(["-l0b0123"],
1647                             "option -l: invalid integer value: '0b0123'")
1648        self.assertParseFail(["-l", "0x12x"],
1649                             "option -l: invalid integer value: '0x12x'")
1650
1651
1652class MiscTestCase(unittest.TestCase):
1653    def test__all__(self):
1654        blacklist = {'check_builtin', 'AmbiguousOptionError', 'NO_DEFAULT'}
1655        support.check__all__(self, optparse, blacklist=blacklist)
1656
1657
1658def test_main():
1659    support.run_unittest(__name__)
1660
1661if __name__ == '__main__':
1662    test_main()
1663