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