1# Author: Steven J. Bethard <steven.bethard@gmail.com>.
2
3import codecs
4import inspect
5import os
6import shutil
7import stat
8import sys
9import textwrap
10import tempfile
11import unittest
12import argparse
13
14from io import StringIO
15
16from test import support
17from unittest import mock
18class StdIOBuffer(StringIO):
19    pass
20
21class TestCase(unittest.TestCase):
22
23    def setUp(self):
24        # The tests assume that line wrapping occurs at 80 columns, but this
25        # behaviour can be overridden by setting the COLUMNS environment
26        # variable.  To ensure that this assumption is true, unset COLUMNS.
27        env = support.EnvironmentVarGuard()
28        env.unset("COLUMNS")
29        self.addCleanup(env.__exit__)
30
31
32class TempDirMixin(object):
33
34    def setUp(self):
35        self.temp_dir = tempfile.mkdtemp()
36        self.old_dir = os.getcwd()
37        os.chdir(self.temp_dir)
38
39    def tearDown(self):
40        os.chdir(self.old_dir)
41        for root, dirs, files in os.walk(self.temp_dir, topdown=False):
42            for name in files:
43                os.chmod(os.path.join(self.temp_dir, name), stat.S_IWRITE)
44        shutil.rmtree(self.temp_dir, True)
45
46    def create_readonly_file(self, filename):
47        file_path = os.path.join(self.temp_dir, filename)
48        with open(file_path, 'w') as file:
49            file.write(filename)
50        os.chmod(file_path, stat.S_IREAD)
51
52class Sig(object):
53
54    def __init__(self, *args, **kwargs):
55        self.args = args
56        self.kwargs = kwargs
57
58
59class NS(object):
60
61    def __init__(self, **kwargs):
62        self.__dict__.update(kwargs)
63
64    def __repr__(self):
65        sorted_items = sorted(self.__dict__.items())
66        kwarg_str = ', '.join(['%s=%r' % tup for tup in sorted_items])
67        return '%s(%s)' % (type(self).__name__, kwarg_str)
68
69    def __eq__(self, other):
70        return vars(self) == vars(other)
71
72
73class ArgumentParserError(Exception):
74
75    def __init__(self, message, stdout=None, stderr=None, error_code=None):
76        Exception.__init__(self, message, stdout, stderr)
77        self.message = message
78        self.stdout = stdout
79        self.stderr = stderr
80        self.error_code = error_code
81
82
83def stderr_to_parser_error(parse_args, *args, **kwargs):
84    # if this is being called recursively and stderr or stdout is already being
85    # redirected, simply call the function and let the enclosing function
86    # catch the exception
87    if isinstance(sys.stderr, StdIOBuffer) or isinstance(sys.stdout, StdIOBuffer):
88        return parse_args(*args, **kwargs)
89
90    # if this is not being called recursively, redirect stderr and
91    # use it as the ArgumentParserError message
92    old_stdout = sys.stdout
93    old_stderr = sys.stderr
94    sys.stdout = StdIOBuffer()
95    sys.stderr = StdIOBuffer()
96    try:
97        try:
98            result = parse_args(*args, **kwargs)
99            for key in list(vars(result)):
100                if getattr(result, key) is sys.stdout:
101                    setattr(result, key, old_stdout)
102                if getattr(result, key) is sys.stderr:
103                    setattr(result, key, old_stderr)
104            return result
105        except SystemExit:
106            code = sys.exc_info()[1].code
107            stdout = sys.stdout.getvalue()
108            stderr = sys.stderr.getvalue()
109            raise ArgumentParserError("SystemExit", stdout, stderr, code)
110    finally:
111        sys.stdout = old_stdout
112        sys.stderr = old_stderr
113
114
115class ErrorRaisingArgumentParser(argparse.ArgumentParser):
116
117    def parse_args(self, *args, **kwargs):
118        parse_args = super(ErrorRaisingArgumentParser, self).parse_args
119        return stderr_to_parser_error(parse_args, *args, **kwargs)
120
121    def exit(self, *args, **kwargs):
122        exit = super(ErrorRaisingArgumentParser, self).exit
123        return stderr_to_parser_error(exit, *args, **kwargs)
124
125    def error(self, *args, **kwargs):
126        error = super(ErrorRaisingArgumentParser, self).error
127        return stderr_to_parser_error(error, *args, **kwargs)
128
129
130class ParserTesterMetaclass(type):
131    """Adds parser tests using the class attributes.
132
133    Classes of this type should specify the following attributes:
134
135    argument_signatures -- a list of Sig objects which specify
136        the signatures of Argument objects to be created
137    failures -- a list of args lists that should cause the parser
138        to fail
139    successes -- a list of (initial_args, options, remaining_args) tuples
140        where initial_args specifies the string args to be parsed,
141        options is a dict that should match the vars() of the options
142        parsed out of initial_args, and remaining_args should be any
143        remaining unparsed arguments
144    """
145
146    def __init__(cls, name, bases, bodydict):
147        if name == 'ParserTestCase':
148            return
149
150        # default parser signature is empty
151        if not hasattr(cls, 'parser_signature'):
152            cls.parser_signature = Sig()
153        if not hasattr(cls, 'parser_class'):
154            cls.parser_class = ErrorRaisingArgumentParser
155
156        # ---------------------------------------
157        # functions for adding optional arguments
158        # ---------------------------------------
159        def no_groups(parser, argument_signatures):
160            """Add all arguments directly to the parser"""
161            for sig in argument_signatures:
162                parser.add_argument(*sig.args, **sig.kwargs)
163
164        def one_group(parser, argument_signatures):
165            """Add all arguments under a single group in the parser"""
166            group = parser.add_argument_group('foo')
167            for sig in argument_signatures:
168                group.add_argument(*sig.args, **sig.kwargs)
169
170        def many_groups(parser, argument_signatures):
171            """Add each argument in its own group to the parser"""
172            for i, sig in enumerate(argument_signatures):
173                group = parser.add_argument_group('foo:%i' % i)
174                group.add_argument(*sig.args, **sig.kwargs)
175
176        # --------------------------
177        # functions for parsing args
178        # --------------------------
179        def listargs(parser, args):
180            """Parse the args by passing in a list"""
181            return parser.parse_args(args)
182
183        def sysargs(parser, args):
184            """Parse the args by defaulting to sys.argv"""
185            old_sys_argv = sys.argv
186            sys.argv = [old_sys_argv[0]] + args
187            try:
188                return parser.parse_args()
189            finally:
190                sys.argv = old_sys_argv
191
192        # class that holds the combination of one optional argument
193        # addition method and one arg parsing method
194        class AddTests(object):
195
196            def __init__(self, tester_cls, add_arguments, parse_args):
197                self._add_arguments = add_arguments
198                self._parse_args = parse_args
199
200                add_arguments_name = self._add_arguments.__name__
201                parse_args_name = self._parse_args.__name__
202                for test_func in [self.test_failures, self.test_successes]:
203                    func_name = test_func.__name__
204                    names = func_name, add_arguments_name, parse_args_name
205                    test_name = '_'.join(names)
206
207                    def wrapper(self, test_func=test_func):
208                        test_func(self)
209                    try:
210                        wrapper.__name__ = test_name
211                    except TypeError:
212                        pass
213                    setattr(tester_cls, test_name, wrapper)
214
215            def _get_parser(self, tester):
216                args = tester.parser_signature.args
217                kwargs = tester.parser_signature.kwargs
218                parser = tester.parser_class(*args, **kwargs)
219                self._add_arguments(parser, tester.argument_signatures)
220                return parser
221
222            def test_failures(self, tester):
223                parser = self._get_parser(tester)
224                for args_str in tester.failures:
225                    args = args_str.split()
226                    with tester.assertRaises(ArgumentParserError, msg=args):
227                        parser.parse_args(args)
228
229            def test_successes(self, tester):
230                parser = self._get_parser(tester)
231                for args, expected_ns in tester.successes:
232                    if isinstance(args, str):
233                        args = args.split()
234                    result_ns = self._parse_args(parser, args)
235                    tester.assertEqual(expected_ns, result_ns)
236
237        # add tests for each combination of an optionals adding method
238        # and an arg parsing method
239        for add_arguments in [no_groups, one_group, many_groups]:
240            for parse_args in [listargs, sysargs]:
241                AddTests(cls, add_arguments, parse_args)
242
243bases = TestCase,
244ParserTestCase = ParserTesterMetaclass('ParserTestCase', bases, {})
245
246# ===============
247# Optionals tests
248# ===============
249
250class TestOptionalsSingleDash(ParserTestCase):
251    """Test an Optional with a single-dash option string"""
252
253    argument_signatures = [Sig('-x')]
254    failures = ['-x', 'a', '--foo', '-x --foo', '-x -y']
255    successes = [
256        ('', NS(x=None)),
257        ('-x a', NS(x='a')),
258        ('-xa', NS(x='a')),
259        ('-x -1', NS(x='-1')),
260        ('-x-1', NS(x='-1')),
261    ]
262
263
264class TestOptionalsSingleDashCombined(ParserTestCase):
265    """Test an Optional with a single-dash option string"""
266
267    argument_signatures = [
268        Sig('-x', action='store_true'),
269        Sig('-yyy', action='store_const', const=42),
270        Sig('-z'),
271    ]
272    failures = ['a', '--foo', '-xa', '-x --foo', '-x -z', '-z -x',
273                '-yx', '-yz a', '-yyyx', '-yyyza', '-xyza']
274    successes = [
275        ('', NS(x=False, yyy=None, z=None)),
276        ('-x', NS(x=True, yyy=None, z=None)),
277        ('-za', NS(x=False, yyy=None, z='a')),
278        ('-z a', NS(x=False, yyy=None, z='a')),
279        ('-xza', NS(x=True, yyy=None, z='a')),
280        ('-xz a', NS(x=True, yyy=None, z='a')),
281        ('-x -za', NS(x=True, yyy=None, z='a')),
282        ('-x -z a', NS(x=True, yyy=None, z='a')),
283        ('-y', NS(x=False, yyy=42, z=None)),
284        ('-yyy', NS(x=False, yyy=42, z=None)),
285        ('-x -yyy -za', NS(x=True, yyy=42, z='a')),
286        ('-x -yyy -z a', NS(x=True, yyy=42, z='a')),
287    ]
288
289
290class TestOptionalsSingleDashLong(ParserTestCase):
291    """Test an Optional with a multi-character single-dash option string"""
292
293    argument_signatures = [Sig('-foo')]
294    failures = ['-foo', 'a', '--foo', '-foo --foo', '-foo -y', '-fooa']
295    successes = [
296        ('', NS(foo=None)),
297        ('-foo a', NS(foo='a')),
298        ('-foo -1', NS(foo='-1')),
299        ('-fo a', NS(foo='a')),
300        ('-f a', NS(foo='a')),
301    ]
302
303
304class TestOptionalsSingleDashSubsetAmbiguous(ParserTestCase):
305    """Test Optionals where option strings are subsets of each other"""
306
307    argument_signatures = [Sig('-f'), Sig('-foobar'), Sig('-foorab')]
308    failures = ['-f', '-foo', '-fo', '-foo b', '-foob', '-fooba', '-foora']
309    successes = [
310        ('', NS(f=None, foobar=None, foorab=None)),
311        ('-f a', NS(f='a', foobar=None, foorab=None)),
312        ('-fa', NS(f='a', foobar=None, foorab=None)),
313        ('-foa', NS(f='oa', foobar=None, foorab=None)),
314        ('-fooa', NS(f='ooa', foobar=None, foorab=None)),
315        ('-foobar a', NS(f=None, foobar='a', foorab=None)),
316        ('-foorab a', NS(f=None, foobar=None, foorab='a')),
317    ]
318
319
320class TestOptionalsSingleDashAmbiguous(ParserTestCase):
321    """Test Optionals that partially match but are not subsets"""
322
323    argument_signatures = [Sig('-foobar'), Sig('-foorab')]
324    failures = ['-f', '-f a', '-fa', '-foa', '-foo', '-fo', '-foo b']
325    successes = [
326        ('', NS(foobar=None, foorab=None)),
327        ('-foob a', NS(foobar='a', foorab=None)),
328        ('-foor a', NS(foobar=None, foorab='a')),
329        ('-fooba a', NS(foobar='a', foorab=None)),
330        ('-foora a', NS(foobar=None, foorab='a')),
331        ('-foobar a', NS(foobar='a', foorab=None)),
332        ('-foorab a', NS(foobar=None, foorab='a')),
333    ]
334
335
336class TestOptionalsNumeric(ParserTestCase):
337    """Test an Optional with a short opt string"""
338
339    argument_signatures = [Sig('-1', dest='one')]
340    failures = ['-1', 'a', '-1 --foo', '-1 -y', '-1 -1', '-1 -2']
341    successes = [
342        ('', NS(one=None)),
343        ('-1 a', NS(one='a')),
344        ('-1a', NS(one='a')),
345        ('-1-2', NS(one='-2')),
346    ]
347
348
349class TestOptionalsDoubleDash(ParserTestCase):
350    """Test an Optional with a double-dash option string"""
351
352    argument_signatures = [Sig('--foo')]
353    failures = ['--foo', '-f', '-f a', 'a', '--foo -x', '--foo --bar']
354    successes = [
355        ('', NS(foo=None)),
356        ('--foo a', NS(foo='a')),
357        ('--foo=a', NS(foo='a')),
358        ('--foo -2.5', NS(foo='-2.5')),
359        ('--foo=-2.5', NS(foo='-2.5')),
360    ]
361
362
363class TestOptionalsDoubleDashPartialMatch(ParserTestCase):
364    """Tests partial matching with a double-dash option string"""
365
366    argument_signatures = [
367        Sig('--badger', action='store_true'),
368        Sig('--bat'),
369    ]
370    failures = ['--bar', '--b', '--ba', '--b=2', '--ba=4', '--badge 5']
371    successes = [
372        ('', NS(badger=False, bat=None)),
373        ('--bat X', NS(badger=False, bat='X')),
374        ('--bad', NS(badger=True, bat=None)),
375        ('--badg', NS(badger=True, bat=None)),
376        ('--badge', NS(badger=True, bat=None)),
377        ('--badger', NS(badger=True, bat=None)),
378    ]
379
380
381class TestOptionalsDoubleDashPrefixMatch(ParserTestCase):
382    """Tests when one double-dash option string is a prefix of another"""
383
384    argument_signatures = [
385        Sig('--badger', action='store_true'),
386        Sig('--ba'),
387    ]
388    failures = ['--bar', '--b', '--ba', '--b=2', '--badge 5']
389    successes = [
390        ('', NS(badger=False, ba=None)),
391        ('--ba X', NS(badger=False, ba='X')),
392        ('--ba=X', NS(badger=False, ba='X')),
393        ('--bad', NS(badger=True, ba=None)),
394        ('--badg', NS(badger=True, ba=None)),
395        ('--badge', NS(badger=True, ba=None)),
396        ('--badger', NS(badger=True, ba=None)),
397    ]
398
399
400class TestOptionalsSingleDoubleDash(ParserTestCase):
401    """Test an Optional with single- and double-dash option strings"""
402
403    argument_signatures = [
404        Sig('-f', action='store_true'),
405        Sig('--bar'),
406        Sig('-baz', action='store_const', const=42),
407    ]
408    failures = ['--bar', '-fbar', '-fbaz', '-bazf', '-b B', 'B']
409    successes = [
410        ('', NS(f=False, bar=None, baz=None)),
411        ('-f', NS(f=True, bar=None, baz=None)),
412        ('--ba B', NS(f=False, bar='B', baz=None)),
413        ('-f --bar B', NS(f=True, bar='B', baz=None)),
414        ('-f -b', NS(f=True, bar=None, baz=42)),
415        ('-ba -f', NS(f=True, bar=None, baz=42)),
416    ]
417
418
419class TestOptionalsAlternatePrefixChars(ParserTestCase):
420    """Test an Optional with option strings with custom prefixes"""
421
422    parser_signature = Sig(prefix_chars='+:/', add_help=False)
423    argument_signatures = [
424        Sig('+f', action='store_true'),
425        Sig('::bar'),
426        Sig('/baz', action='store_const', const=42),
427    ]
428    failures = ['--bar', '-fbar', '-b B', 'B', '-f', '--bar B', '-baz', '-h', '--help', '+h', '::help', '/help']
429    successes = [
430        ('', NS(f=False, bar=None, baz=None)),
431        ('+f', NS(f=True, bar=None, baz=None)),
432        ('::ba B', NS(f=False, bar='B', baz=None)),
433        ('+f ::bar B', NS(f=True, bar='B', baz=None)),
434        ('+f /b', NS(f=True, bar=None, baz=42)),
435        ('/ba +f', NS(f=True, bar=None, baz=42)),
436    ]
437
438
439class TestOptionalsAlternatePrefixCharsAddedHelp(ParserTestCase):
440    """When ``-`` not in prefix_chars, default operators created for help
441       should use the prefix_chars in use rather than - or --
442       http://bugs.python.org/issue9444"""
443
444    parser_signature = Sig(prefix_chars='+:/', add_help=True)
445    argument_signatures = [
446        Sig('+f', action='store_true'),
447        Sig('::bar'),
448        Sig('/baz', action='store_const', const=42),
449    ]
450    failures = ['--bar', '-fbar', '-b B', 'B', '-f', '--bar B', '-baz']
451    successes = [
452        ('', NS(f=False, bar=None, baz=None)),
453        ('+f', NS(f=True, bar=None, baz=None)),
454        ('::ba B', NS(f=False, bar='B', baz=None)),
455        ('+f ::bar B', NS(f=True, bar='B', baz=None)),
456        ('+f /b', NS(f=True, bar=None, baz=42)),
457        ('/ba +f', NS(f=True, bar=None, baz=42))
458    ]
459
460
461class TestOptionalsAlternatePrefixCharsMultipleShortArgs(ParserTestCase):
462    """Verify that Optionals must be called with their defined prefixes"""
463
464    parser_signature = Sig(prefix_chars='+-', add_help=False)
465    argument_signatures = [
466        Sig('-x', action='store_true'),
467        Sig('+y', action='store_true'),
468        Sig('+z', action='store_true'),
469    ]
470    failures = ['-w',
471                '-xyz',
472                '+x',
473                '-y',
474                '+xyz',
475    ]
476    successes = [
477        ('', NS(x=False, y=False, z=False)),
478        ('-x', NS(x=True, y=False, z=False)),
479        ('+y -x', NS(x=True, y=True, z=False)),
480        ('+yz -x', NS(x=True, y=True, z=True)),
481    ]
482
483
484class TestOptionalsShortLong(ParserTestCase):
485    """Test a combination of single- and double-dash option strings"""
486
487    argument_signatures = [
488        Sig('-v', '--verbose', '-n', '--noisy', action='store_true'),
489    ]
490    failures = ['--x --verbose', '-N', 'a', '-v x']
491    successes = [
492        ('', NS(verbose=False)),
493        ('-v', NS(verbose=True)),
494        ('--verbose', NS(verbose=True)),
495        ('-n', NS(verbose=True)),
496        ('--noisy', NS(verbose=True)),
497    ]
498
499
500class TestOptionalsDest(ParserTestCase):
501    """Tests various means of setting destination"""
502
503    argument_signatures = [Sig('--foo-bar'), Sig('--baz', dest='zabbaz')]
504    failures = ['a']
505    successes = [
506        ('--foo-bar f', NS(foo_bar='f', zabbaz=None)),
507        ('--baz g', NS(foo_bar=None, zabbaz='g')),
508        ('--foo-bar h --baz i', NS(foo_bar='h', zabbaz='i')),
509        ('--baz j --foo-bar k', NS(foo_bar='k', zabbaz='j')),
510    ]
511
512
513class TestOptionalsDefault(ParserTestCase):
514    """Tests specifying a default for an Optional"""
515
516    argument_signatures = [Sig('-x'), Sig('-y', default=42)]
517    failures = ['a']
518    successes = [
519        ('', NS(x=None, y=42)),
520        ('-xx', NS(x='x', y=42)),
521        ('-yy', NS(x=None, y='y')),
522    ]
523
524
525class TestOptionalsNargsDefault(ParserTestCase):
526    """Tests not specifying the number of args for an Optional"""
527
528    argument_signatures = [Sig('-x')]
529    failures = ['a', '-x']
530    successes = [
531        ('', NS(x=None)),
532        ('-x a', NS(x='a')),
533    ]
534
535
536class TestOptionalsNargs1(ParserTestCase):
537    """Tests specifying 1 arg for an Optional"""
538
539    argument_signatures = [Sig('-x', nargs=1)]
540    failures = ['a', '-x']
541    successes = [
542        ('', NS(x=None)),
543        ('-x a', NS(x=['a'])),
544    ]
545
546
547class TestOptionalsNargs3(ParserTestCase):
548    """Tests specifying 3 args for an Optional"""
549
550    argument_signatures = [Sig('-x', nargs=3)]
551    failures = ['a', '-x', '-x a', '-x a b', 'a -x', 'a -x b']
552    successes = [
553        ('', NS(x=None)),
554        ('-x a b c', NS(x=['a', 'b', 'c'])),
555    ]
556
557
558class TestOptionalsNargsOptional(ParserTestCase):
559    """Tests specifying an Optional arg for an Optional"""
560
561    argument_signatures = [
562        Sig('-w', nargs='?'),
563        Sig('-x', nargs='?', const=42),
564        Sig('-y', nargs='?', default='spam'),
565        Sig('-z', nargs='?', type=int, const='42', default='84'),
566    ]
567    failures = ['2']
568    successes = [
569        ('', NS(w=None, x=None, y='spam', z=84)),
570        ('-w', NS(w=None, x=None, y='spam', z=84)),
571        ('-w 2', NS(w='2', x=None, y='spam', z=84)),
572        ('-x', NS(w=None, x=42, y='spam', z=84)),
573        ('-x 2', NS(w=None, x='2', y='spam', z=84)),
574        ('-y', NS(w=None, x=None, y=None, z=84)),
575        ('-y 2', NS(w=None, x=None, y='2', z=84)),
576        ('-z', NS(w=None, x=None, y='spam', z=42)),
577        ('-z 2', NS(w=None, x=None, y='spam', z=2)),
578    ]
579
580
581class TestOptionalsNargsZeroOrMore(ParserTestCase):
582    """Tests specifying args for an Optional that accepts zero or more"""
583
584    argument_signatures = [
585        Sig('-x', nargs='*'),
586        Sig('-y', nargs='*', default='spam'),
587    ]
588    failures = ['a']
589    successes = [
590        ('', NS(x=None, y='spam')),
591        ('-x', NS(x=[], y='spam')),
592        ('-x a', NS(x=['a'], y='spam')),
593        ('-x a b', NS(x=['a', 'b'], y='spam')),
594        ('-y', NS(x=None, y=[])),
595        ('-y a', NS(x=None, y=['a'])),
596        ('-y a b', NS(x=None, y=['a', 'b'])),
597    ]
598
599
600class TestOptionalsNargsOneOrMore(ParserTestCase):
601    """Tests specifying args for an Optional that accepts one or more"""
602
603    argument_signatures = [
604        Sig('-x', nargs='+'),
605        Sig('-y', nargs='+', default='spam'),
606    ]
607    failures = ['a', '-x', '-y', 'a -x', 'a -y b']
608    successes = [
609        ('', NS(x=None, y='spam')),
610        ('-x a', NS(x=['a'], y='spam')),
611        ('-x a b', NS(x=['a', 'b'], y='spam')),
612        ('-y a', NS(x=None, y=['a'])),
613        ('-y a b', NS(x=None, y=['a', 'b'])),
614    ]
615
616
617class TestOptionalsChoices(ParserTestCase):
618    """Tests specifying the choices for an Optional"""
619
620    argument_signatures = [
621        Sig('-f', choices='abc'),
622        Sig('-g', type=int, choices=range(5))]
623    failures = ['a', '-f d', '-fad', '-ga', '-g 6']
624    successes = [
625        ('', NS(f=None, g=None)),
626        ('-f a', NS(f='a', g=None)),
627        ('-f c', NS(f='c', g=None)),
628        ('-g 0', NS(f=None, g=0)),
629        ('-g 03', NS(f=None, g=3)),
630        ('-fb -g4', NS(f='b', g=4)),
631    ]
632
633
634class TestOptionalsRequired(ParserTestCase):
635    """Tests an optional action that is required"""
636
637    argument_signatures = [
638        Sig('-x', type=int, required=True),
639    ]
640    failures = ['a', '']
641    successes = [
642        ('-x 1', NS(x=1)),
643        ('-x42', NS(x=42)),
644    ]
645
646
647class TestOptionalsActionStore(ParserTestCase):
648    """Tests the store action for an Optional"""
649
650    argument_signatures = [Sig('-x', action='store')]
651    failures = ['a', 'a -x']
652    successes = [
653        ('', NS(x=None)),
654        ('-xfoo', NS(x='foo')),
655    ]
656
657
658class TestOptionalsActionStoreConst(ParserTestCase):
659    """Tests the store_const action for an Optional"""
660
661    argument_signatures = [Sig('-y', action='store_const', const=object)]
662    failures = ['a']
663    successes = [
664        ('', NS(y=None)),
665        ('-y', NS(y=object)),
666    ]
667
668
669class TestOptionalsActionStoreFalse(ParserTestCase):
670    """Tests the store_false action for an Optional"""
671
672    argument_signatures = [Sig('-z', action='store_false')]
673    failures = ['a', '-za', '-z a']
674    successes = [
675        ('', NS(z=True)),
676        ('-z', NS(z=False)),
677    ]
678
679
680class TestOptionalsActionStoreTrue(ParserTestCase):
681    """Tests the store_true action for an Optional"""
682
683    argument_signatures = [Sig('--apple', action='store_true')]
684    failures = ['a', '--apple=b', '--apple b']
685    successes = [
686        ('', NS(apple=False)),
687        ('--apple', NS(apple=True)),
688    ]
689
690
691class TestOptionalsActionAppend(ParserTestCase):
692    """Tests the append action for an Optional"""
693
694    argument_signatures = [Sig('--baz', action='append')]
695    failures = ['a', '--baz', 'a --baz', '--baz a b']
696    successes = [
697        ('', NS(baz=None)),
698        ('--baz a', NS(baz=['a'])),
699        ('--baz a --baz b', NS(baz=['a', 'b'])),
700    ]
701
702
703class TestOptionalsActionAppendWithDefault(ParserTestCase):
704    """Tests the append action for an Optional"""
705
706    argument_signatures = [Sig('--baz', action='append', default=['X'])]
707    failures = ['a', '--baz', 'a --baz', '--baz a b']
708    successes = [
709        ('', NS(baz=['X'])),
710        ('--baz a', NS(baz=['X', 'a'])),
711        ('--baz a --baz b', NS(baz=['X', 'a', 'b'])),
712    ]
713
714
715class TestOptionalsActionAppendConst(ParserTestCase):
716    """Tests the append_const action for an Optional"""
717
718    argument_signatures = [
719        Sig('-b', action='append_const', const=Exception),
720        Sig('-c', action='append', dest='b'),
721    ]
722    failures = ['a', '-c', 'a -c', '-bx', '-b x']
723    successes = [
724        ('', NS(b=None)),
725        ('-b', NS(b=[Exception])),
726        ('-b -cx -b -cyz', NS(b=[Exception, 'x', Exception, 'yz'])),
727    ]
728
729
730class TestOptionalsActionAppendConstWithDefault(ParserTestCase):
731    """Tests the append_const action for an Optional"""
732
733    argument_signatures = [
734        Sig('-b', action='append_const', const=Exception, default=['X']),
735        Sig('-c', action='append', dest='b'),
736    ]
737    failures = ['a', '-c', 'a -c', '-bx', '-b x']
738    successes = [
739        ('', NS(b=['X'])),
740        ('-b', NS(b=['X', Exception])),
741        ('-b -cx -b -cyz', NS(b=['X', Exception, 'x', Exception, 'yz'])),
742    ]
743
744
745class TestOptionalsActionCount(ParserTestCase):
746    """Tests the count action for an Optional"""
747
748    argument_signatures = [Sig('-x', action='count')]
749    failures = ['a', '-x a', '-x b', '-x a -x b']
750    successes = [
751        ('', NS(x=None)),
752        ('-x', NS(x=1)),
753    ]
754
755
756class TestOptionalsAllowLongAbbreviation(ParserTestCase):
757    """Allow long options to be abbreviated unambiguously"""
758
759    argument_signatures = [
760        Sig('--foo'),
761        Sig('--foobaz'),
762        Sig('--fooble', action='store_true'),
763    ]
764    failures = ['--foob 5', '--foob']
765    successes = [
766        ('', NS(foo=None, foobaz=None, fooble=False)),
767        ('--foo 7', NS(foo='7', foobaz=None, fooble=False)),
768        ('--fooba a', NS(foo=None, foobaz='a', fooble=False)),
769        ('--foobl --foo g', NS(foo='g', foobaz=None, fooble=True)),
770    ]
771
772
773class TestOptionalsDisallowLongAbbreviation(ParserTestCase):
774    """Do not allow abbreviations of long options at all"""
775
776    parser_signature = Sig(allow_abbrev=False)
777    argument_signatures = [
778        Sig('--foo'),
779        Sig('--foodle', action='store_true'),
780        Sig('--foonly'),
781    ]
782    failures = ['-foon 3', '--foon 3', '--food', '--food --foo 2']
783    successes = [
784        ('', NS(foo=None, foodle=False, foonly=None)),
785        ('--foo 3', NS(foo='3', foodle=False, foonly=None)),
786        ('--foonly 7 --foodle --foo 2', NS(foo='2', foodle=True, foonly='7')),
787    ]
788
789# ================
790# Positional tests
791# ================
792
793class TestPositionalsNargsNone(ParserTestCase):
794    """Test a Positional that doesn't specify nargs"""
795
796    argument_signatures = [Sig('foo')]
797    failures = ['', '-x', 'a b']
798    successes = [
799        ('a', NS(foo='a')),
800    ]
801
802
803class TestPositionalsNargs1(ParserTestCase):
804    """Test a Positional that specifies an nargs of 1"""
805
806    argument_signatures = [Sig('foo', nargs=1)]
807    failures = ['', '-x', 'a b']
808    successes = [
809        ('a', NS(foo=['a'])),
810    ]
811
812
813class TestPositionalsNargs2(ParserTestCase):
814    """Test a Positional that specifies an nargs of 2"""
815
816    argument_signatures = [Sig('foo', nargs=2)]
817    failures = ['', 'a', '-x', 'a b c']
818    successes = [
819        ('a b', NS(foo=['a', 'b'])),
820    ]
821
822
823class TestPositionalsNargsZeroOrMore(ParserTestCase):
824    """Test a Positional that specifies unlimited nargs"""
825
826    argument_signatures = [Sig('foo', nargs='*')]
827    failures = ['-x']
828    successes = [
829        ('', NS(foo=[])),
830        ('a', NS(foo=['a'])),
831        ('a b', NS(foo=['a', 'b'])),
832    ]
833
834
835class TestPositionalsNargsZeroOrMoreDefault(ParserTestCase):
836    """Test a Positional that specifies unlimited nargs and a default"""
837
838    argument_signatures = [Sig('foo', nargs='*', default='bar')]
839    failures = ['-x']
840    successes = [
841        ('', NS(foo='bar')),
842        ('a', NS(foo=['a'])),
843        ('a b', NS(foo=['a', 'b'])),
844    ]
845
846
847class TestPositionalsNargsOneOrMore(ParserTestCase):
848    """Test a Positional that specifies one or more nargs"""
849
850    argument_signatures = [Sig('foo', nargs='+')]
851    failures = ['', '-x']
852    successes = [
853        ('a', NS(foo=['a'])),
854        ('a b', NS(foo=['a', 'b'])),
855    ]
856
857
858class TestPositionalsNargsOptional(ParserTestCase):
859    """Tests an Optional Positional"""
860
861    argument_signatures = [Sig('foo', nargs='?')]
862    failures = ['-x', 'a b']
863    successes = [
864        ('', NS(foo=None)),
865        ('a', NS(foo='a')),
866    ]
867
868
869class TestPositionalsNargsOptionalDefault(ParserTestCase):
870    """Tests an Optional Positional with a default value"""
871
872    argument_signatures = [Sig('foo', nargs='?', default=42)]
873    failures = ['-x', 'a b']
874    successes = [
875        ('', NS(foo=42)),
876        ('a', NS(foo='a')),
877    ]
878
879
880class TestPositionalsNargsOptionalConvertedDefault(ParserTestCase):
881    """Tests an Optional Positional with a default value
882    that needs to be converted to the appropriate type.
883    """
884
885    argument_signatures = [
886        Sig('foo', nargs='?', type=int, default='42'),
887    ]
888    failures = ['-x', 'a b', '1 2']
889    successes = [
890        ('', NS(foo=42)),
891        ('1', NS(foo=1)),
892    ]
893
894
895class TestPositionalsNargsNoneNone(ParserTestCase):
896    """Test two Positionals that don't specify nargs"""
897
898    argument_signatures = [Sig('foo'), Sig('bar')]
899    failures = ['', '-x', 'a', 'a b c']
900    successes = [
901        ('a b', NS(foo='a', bar='b')),
902    ]
903
904
905class TestPositionalsNargsNone1(ParserTestCase):
906    """Test a Positional with no nargs followed by one with 1"""
907
908    argument_signatures = [Sig('foo'), Sig('bar', nargs=1)]
909    failures = ['', '--foo', 'a', 'a b c']
910    successes = [
911        ('a b', NS(foo='a', bar=['b'])),
912    ]
913
914
915class TestPositionalsNargs2None(ParserTestCase):
916    """Test a Positional with 2 nargs followed by one with none"""
917
918    argument_signatures = [Sig('foo', nargs=2), Sig('bar')]
919    failures = ['', '--foo', 'a', 'a b', 'a b c d']
920    successes = [
921        ('a b c', NS(foo=['a', 'b'], bar='c')),
922    ]
923
924
925class TestPositionalsNargsNoneZeroOrMore(ParserTestCase):
926    """Test a Positional with no nargs followed by one with unlimited"""
927
928    argument_signatures = [Sig('foo'), Sig('bar', nargs='*')]
929    failures = ['', '--foo']
930    successes = [
931        ('a', NS(foo='a', bar=[])),
932        ('a b', NS(foo='a', bar=['b'])),
933        ('a b c', NS(foo='a', bar=['b', 'c'])),
934    ]
935
936
937class TestPositionalsNargsNoneOneOrMore(ParserTestCase):
938    """Test a Positional with no nargs followed by one with one or more"""
939
940    argument_signatures = [Sig('foo'), Sig('bar', nargs='+')]
941    failures = ['', '--foo', 'a']
942    successes = [
943        ('a b', NS(foo='a', bar=['b'])),
944        ('a b c', NS(foo='a', bar=['b', 'c'])),
945    ]
946
947
948class TestPositionalsNargsNoneOptional(ParserTestCase):
949    """Test a Positional with no nargs followed by one with an Optional"""
950
951    argument_signatures = [Sig('foo'), Sig('bar', nargs='?')]
952    failures = ['', '--foo', 'a b c']
953    successes = [
954        ('a', NS(foo='a', bar=None)),
955        ('a b', NS(foo='a', bar='b')),
956    ]
957
958
959class TestPositionalsNargsZeroOrMoreNone(ParserTestCase):
960    """Test a Positional with unlimited nargs followed by one with none"""
961
962    argument_signatures = [Sig('foo', nargs='*'), Sig('bar')]
963    failures = ['', '--foo']
964    successes = [
965        ('a', NS(foo=[], bar='a')),
966        ('a b', NS(foo=['a'], bar='b')),
967        ('a b c', NS(foo=['a', 'b'], bar='c')),
968    ]
969
970
971class TestPositionalsNargsOneOrMoreNone(ParserTestCase):
972    """Test a Positional with one or more nargs followed by one with none"""
973
974    argument_signatures = [Sig('foo', nargs='+'), Sig('bar')]
975    failures = ['', '--foo', 'a']
976    successes = [
977        ('a b', NS(foo=['a'], bar='b')),
978        ('a b c', NS(foo=['a', 'b'], bar='c')),
979    ]
980
981
982class TestPositionalsNargsOptionalNone(ParserTestCase):
983    """Test a Positional with an Optional nargs followed by one with none"""
984
985    argument_signatures = [Sig('foo', nargs='?', default=42), Sig('bar')]
986    failures = ['', '--foo', 'a b c']
987    successes = [
988        ('a', NS(foo=42, bar='a')),
989        ('a b', NS(foo='a', bar='b')),
990    ]
991
992
993class TestPositionalsNargs2ZeroOrMore(ParserTestCase):
994    """Test a Positional with 2 nargs followed by one with unlimited"""
995
996    argument_signatures = [Sig('foo', nargs=2), Sig('bar', nargs='*')]
997    failures = ['', '--foo', 'a']
998    successes = [
999        ('a b', NS(foo=['a', 'b'], bar=[])),
1000        ('a b c', NS(foo=['a', 'b'], bar=['c'])),
1001    ]
1002
1003
1004class TestPositionalsNargs2OneOrMore(ParserTestCase):
1005    """Test a Positional with 2 nargs followed by one with one or more"""
1006
1007    argument_signatures = [Sig('foo', nargs=2), Sig('bar', nargs='+')]
1008    failures = ['', '--foo', 'a', 'a b']
1009    successes = [
1010        ('a b c', NS(foo=['a', 'b'], bar=['c'])),
1011    ]
1012
1013
1014class TestPositionalsNargs2Optional(ParserTestCase):
1015    """Test a Positional with 2 nargs followed by one optional"""
1016
1017    argument_signatures = [Sig('foo', nargs=2), Sig('bar', nargs='?')]
1018    failures = ['', '--foo', 'a', 'a b c d']
1019    successes = [
1020        ('a b', NS(foo=['a', 'b'], bar=None)),
1021        ('a b c', NS(foo=['a', 'b'], bar='c')),
1022    ]
1023
1024
1025class TestPositionalsNargsZeroOrMore1(ParserTestCase):
1026    """Test a Positional with unlimited nargs followed by one with 1"""
1027
1028    argument_signatures = [Sig('foo', nargs='*'), Sig('bar', nargs=1)]
1029    failures = ['', '--foo', ]
1030    successes = [
1031        ('a', NS(foo=[], bar=['a'])),
1032        ('a b', NS(foo=['a'], bar=['b'])),
1033        ('a b c', NS(foo=['a', 'b'], bar=['c'])),
1034    ]
1035
1036
1037class TestPositionalsNargsOneOrMore1(ParserTestCase):
1038    """Test a Positional with one or more nargs followed by one with 1"""
1039
1040    argument_signatures = [Sig('foo', nargs='+'), Sig('bar', nargs=1)]
1041    failures = ['', '--foo', 'a']
1042    successes = [
1043        ('a b', NS(foo=['a'], bar=['b'])),
1044        ('a b c', NS(foo=['a', 'b'], bar=['c'])),
1045    ]
1046
1047
1048class TestPositionalsNargsOptional1(ParserTestCase):
1049    """Test a Positional with an Optional nargs followed by one with 1"""
1050
1051    argument_signatures = [Sig('foo', nargs='?'), Sig('bar', nargs=1)]
1052    failures = ['', '--foo', 'a b c']
1053    successes = [
1054        ('a', NS(foo=None, bar=['a'])),
1055        ('a b', NS(foo='a', bar=['b'])),
1056    ]
1057
1058
1059class TestPositionalsNargsNoneZeroOrMore1(ParserTestCase):
1060    """Test three Positionals: no nargs, unlimited nargs and 1 nargs"""
1061
1062    argument_signatures = [
1063        Sig('foo'),
1064        Sig('bar', nargs='*'),
1065        Sig('baz', nargs=1),
1066    ]
1067    failures = ['', '--foo', 'a']
1068    successes = [
1069        ('a b', NS(foo='a', bar=[], baz=['b'])),
1070        ('a b c', NS(foo='a', bar=['b'], baz=['c'])),
1071    ]
1072
1073
1074class TestPositionalsNargsNoneOneOrMore1(ParserTestCase):
1075    """Test three Positionals: no nargs, one or more nargs and 1 nargs"""
1076
1077    argument_signatures = [
1078        Sig('foo'),
1079        Sig('bar', nargs='+'),
1080        Sig('baz', nargs=1),
1081    ]
1082    failures = ['', '--foo', 'a', 'b']
1083    successes = [
1084        ('a b c', NS(foo='a', bar=['b'], baz=['c'])),
1085        ('a b c d', NS(foo='a', bar=['b', 'c'], baz=['d'])),
1086    ]
1087
1088
1089class TestPositionalsNargsNoneOptional1(ParserTestCase):
1090    """Test three Positionals: no nargs, optional narg and 1 nargs"""
1091
1092    argument_signatures = [
1093        Sig('foo'),
1094        Sig('bar', nargs='?', default=0.625),
1095        Sig('baz', nargs=1),
1096    ]
1097    failures = ['', '--foo', 'a']
1098    successes = [
1099        ('a b', NS(foo='a', bar=0.625, baz=['b'])),
1100        ('a b c', NS(foo='a', bar='b', baz=['c'])),
1101    ]
1102
1103
1104class TestPositionalsNargsOptionalOptional(ParserTestCase):
1105    """Test two optional nargs"""
1106
1107    argument_signatures = [
1108        Sig('foo', nargs='?'),
1109        Sig('bar', nargs='?', default=42),
1110    ]
1111    failures = ['--foo', 'a b c']
1112    successes = [
1113        ('', NS(foo=None, bar=42)),
1114        ('a', NS(foo='a', bar=42)),
1115        ('a b', NS(foo='a', bar='b')),
1116    ]
1117
1118
1119class TestPositionalsNargsOptionalZeroOrMore(ParserTestCase):
1120    """Test an Optional narg followed by unlimited nargs"""
1121
1122    argument_signatures = [Sig('foo', nargs='?'), Sig('bar', nargs='*')]
1123    failures = ['--foo']
1124    successes = [
1125        ('', NS(foo=None, bar=[])),
1126        ('a', NS(foo='a', bar=[])),
1127        ('a b', NS(foo='a', bar=['b'])),
1128        ('a b c', NS(foo='a', bar=['b', 'c'])),
1129    ]
1130
1131
1132class TestPositionalsNargsOptionalOneOrMore(ParserTestCase):
1133    """Test an Optional narg followed by one or more nargs"""
1134
1135    argument_signatures = [Sig('foo', nargs='?'), Sig('bar', nargs='+')]
1136    failures = ['', '--foo']
1137    successes = [
1138        ('a', NS(foo=None, bar=['a'])),
1139        ('a b', NS(foo='a', bar=['b'])),
1140        ('a b c', NS(foo='a', bar=['b', 'c'])),
1141    ]
1142
1143
1144class TestPositionalsChoicesString(ParserTestCase):
1145    """Test a set of single-character choices"""
1146
1147    argument_signatures = [Sig('spam', choices=set('abcdefg'))]
1148    failures = ['', '--foo', 'h', '42', 'ef']
1149    successes = [
1150        ('a', NS(spam='a')),
1151        ('g', NS(spam='g')),
1152    ]
1153
1154
1155class TestPositionalsChoicesInt(ParserTestCase):
1156    """Test a set of integer choices"""
1157
1158    argument_signatures = [Sig('spam', type=int, choices=range(20))]
1159    failures = ['', '--foo', 'h', '42', 'ef']
1160    successes = [
1161        ('4', NS(spam=4)),
1162        ('15', NS(spam=15)),
1163    ]
1164
1165
1166class TestPositionalsActionAppend(ParserTestCase):
1167    """Test the 'append' action"""
1168
1169    argument_signatures = [
1170        Sig('spam', action='append'),
1171        Sig('spam', action='append', nargs=2),
1172    ]
1173    failures = ['', '--foo', 'a', 'a b', 'a b c d']
1174    successes = [
1175        ('a b c', NS(spam=['a', ['b', 'c']])),
1176    ]
1177
1178# ========================================
1179# Combined optionals and positionals tests
1180# ========================================
1181
1182class TestOptionalsNumericAndPositionals(ParserTestCase):
1183    """Tests negative number args when numeric options are present"""
1184
1185    argument_signatures = [
1186        Sig('x', nargs='?'),
1187        Sig('-4', dest='y', action='store_true'),
1188    ]
1189    failures = ['-2', '-315']
1190    successes = [
1191        ('', NS(x=None, y=False)),
1192        ('a', NS(x='a', y=False)),
1193        ('-4', NS(x=None, y=True)),
1194        ('-4 a', NS(x='a', y=True)),
1195    ]
1196
1197
1198class TestOptionalsAlmostNumericAndPositionals(ParserTestCase):
1199    """Tests negative number args when almost numeric options are present"""
1200
1201    argument_signatures = [
1202        Sig('x', nargs='?'),
1203        Sig('-k4', dest='y', action='store_true'),
1204    ]
1205    failures = ['-k3']
1206    successes = [
1207        ('', NS(x=None, y=False)),
1208        ('-2', NS(x='-2', y=False)),
1209        ('a', NS(x='a', y=False)),
1210        ('-k4', NS(x=None, y=True)),
1211        ('-k4 a', NS(x='a', y=True)),
1212    ]
1213
1214
1215class TestEmptyAndSpaceContainingArguments(ParserTestCase):
1216
1217    argument_signatures = [
1218        Sig('x', nargs='?'),
1219        Sig('-y', '--yyy', dest='y'),
1220    ]
1221    failures = ['-y']
1222    successes = [
1223        ([''], NS(x='', y=None)),
1224        (['a badger'], NS(x='a badger', y=None)),
1225        (['-a badger'], NS(x='-a badger', y=None)),
1226        (['-y', ''], NS(x=None, y='')),
1227        (['-y', 'a badger'], NS(x=None, y='a badger')),
1228        (['-y', '-a badger'], NS(x=None, y='-a badger')),
1229        (['--yyy=a badger'], NS(x=None, y='a badger')),
1230        (['--yyy=-a badger'], NS(x=None, y='-a badger')),
1231    ]
1232
1233
1234class TestPrefixCharacterOnlyArguments(ParserTestCase):
1235
1236    parser_signature = Sig(prefix_chars='-+')
1237    argument_signatures = [
1238        Sig('-', dest='x', nargs='?', const='badger'),
1239        Sig('+', dest='y', type=int, default=42),
1240        Sig('-+-', dest='z', action='store_true'),
1241    ]
1242    failures = ['-y', '+ -']
1243    successes = [
1244        ('', NS(x=None, y=42, z=False)),
1245        ('-', NS(x='badger', y=42, z=False)),
1246        ('- X', NS(x='X', y=42, z=False)),
1247        ('+ -3', NS(x=None, y=-3, z=False)),
1248        ('-+-', NS(x=None, y=42, z=True)),
1249        ('- ===', NS(x='===', y=42, z=False)),
1250    ]
1251
1252
1253class TestNargsZeroOrMore(ParserTestCase):
1254    """Tests specifying args for an Optional that accepts zero or more"""
1255
1256    argument_signatures = [Sig('-x', nargs='*'), Sig('y', nargs='*')]
1257    failures = []
1258    successes = [
1259        ('', NS(x=None, y=[])),
1260        ('-x', NS(x=[], y=[])),
1261        ('-x a', NS(x=['a'], y=[])),
1262        ('-x a -- b', NS(x=['a'], y=['b'])),
1263        ('a', NS(x=None, y=['a'])),
1264        ('a -x', NS(x=[], y=['a'])),
1265        ('a -x b', NS(x=['b'], y=['a'])),
1266    ]
1267
1268
1269class TestNargsRemainder(ParserTestCase):
1270    """Tests specifying a positional with nargs=REMAINDER"""
1271
1272    argument_signatures = [Sig('x'), Sig('y', nargs='...'), Sig('-z')]
1273    failures = ['', '-z', '-z Z']
1274    successes = [
1275        ('X', NS(x='X', y=[], z=None)),
1276        ('-z Z X', NS(x='X', y=[], z='Z')),
1277        ('X A B -z Z', NS(x='X', y=['A', 'B', '-z', 'Z'], z=None)),
1278        ('X Y --foo', NS(x='X', y=['Y', '--foo'], z=None)),
1279    ]
1280
1281
1282class TestOptionLike(ParserTestCase):
1283    """Tests options that may or may not be arguments"""
1284
1285    argument_signatures = [
1286        Sig('-x', type=float),
1287        Sig('-3', type=float, dest='y'),
1288        Sig('z', nargs='*'),
1289    ]
1290    failures = ['-x', '-y2.5', '-xa', '-x -a',
1291                '-x -3', '-x -3.5', '-3 -3.5',
1292                '-x -2.5', '-x -2.5 a', '-3 -.5',
1293                'a x -1', '-x -1 a', '-3 -1 a']
1294    successes = [
1295        ('', NS(x=None, y=None, z=[])),
1296        ('-x 2.5', NS(x=2.5, y=None, z=[])),
1297        ('-x 2.5 a', NS(x=2.5, y=None, z=['a'])),
1298        ('-3.5', NS(x=None, y=0.5, z=[])),
1299        ('-3-.5', NS(x=None, y=-0.5, z=[])),
1300        ('-3 .5', NS(x=None, y=0.5, z=[])),
1301        ('a -3.5', NS(x=None, y=0.5, z=['a'])),
1302        ('a', NS(x=None, y=None, z=['a'])),
1303        ('a -x 1', NS(x=1.0, y=None, z=['a'])),
1304        ('-x 1 a', NS(x=1.0, y=None, z=['a'])),
1305        ('-3 1 a', NS(x=None, y=1.0, z=['a'])),
1306    ]
1307
1308
1309class TestDefaultSuppress(ParserTestCase):
1310    """Test actions with suppressed defaults"""
1311
1312    argument_signatures = [
1313        Sig('foo', nargs='?', default=argparse.SUPPRESS),
1314        Sig('bar', nargs='*', default=argparse.SUPPRESS),
1315        Sig('--baz', action='store_true', default=argparse.SUPPRESS),
1316    ]
1317    failures = ['-x']
1318    successes = [
1319        ('', NS()),
1320        ('a', NS(foo='a')),
1321        ('a b', NS(foo='a', bar=['b'])),
1322        ('--baz', NS(baz=True)),
1323        ('a --baz', NS(foo='a', baz=True)),
1324        ('--baz a b', NS(foo='a', bar=['b'], baz=True)),
1325    ]
1326
1327
1328class TestParserDefaultSuppress(ParserTestCase):
1329    """Test actions with a parser-level default of SUPPRESS"""
1330
1331    parser_signature = Sig(argument_default=argparse.SUPPRESS)
1332    argument_signatures = [
1333        Sig('foo', nargs='?'),
1334        Sig('bar', nargs='*'),
1335        Sig('--baz', action='store_true'),
1336    ]
1337    failures = ['-x']
1338    successes = [
1339        ('', NS()),
1340        ('a', NS(foo='a')),
1341        ('a b', NS(foo='a', bar=['b'])),
1342        ('--baz', NS(baz=True)),
1343        ('a --baz', NS(foo='a', baz=True)),
1344        ('--baz a b', NS(foo='a', bar=['b'], baz=True)),
1345    ]
1346
1347
1348class TestParserDefault42(ParserTestCase):
1349    """Test actions with a parser-level default of 42"""
1350
1351    parser_signature = Sig(argument_default=42)
1352    argument_signatures = [
1353        Sig('--version', action='version', version='1.0'),
1354        Sig('foo', nargs='?'),
1355        Sig('bar', nargs='*'),
1356        Sig('--baz', action='store_true'),
1357    ]
1358    failures = ['-x']
1359    successes = [
1360        ('', NS(foo=42, bar=42, baz=42, version=42)),
1361        ('a', NS(foo='a', bar=42, baz=42, version=42)),
1362        ('a b', NS(foo='a', bar=['b'], baz=42, version=42)),
1363        ('--baz', NS(foo=42, bar=42, baz=True, version=42)),
1364        ('a --baz', NS(foo='a', bar=42, baz=True, version=42)),
1365        ('--baz a b', NS(foo='a', bar=['b'], baz=True, version=42)),
1366    ]
1367
1368
1369class TestArgumentsFromFile(TempDirMixin, ParserTestCase):
1370    """Test reading arguments from a file"""
1371
1372    def setUp(self):
1373        super(TestArgumentsFromFile, self).setUp()
1374        file_texts = [
1375            ('hello', 'hello world!\n'),
1376            ('recursive', '-a\n'
1377                          'A\n'
1378                          '@hello'),
1379            ('invalid', '@no-such-path\n'),
1380        ]
1381        for path, text in file_texts:
1382            file = open(path, 'w')
1383            file.write(text)
1384            file.close()
1385
1386    parser_signature = Sig(fromfile_prefix_chars='@')
1387    argument_signatures = [
1388        Sig('-a'),
1389        Sig('x'),
1390        Sig('y', nargs='+'),
1391    ]
1392    failures = ['', '-b', 'X', '@invalid', '@missing']
1393    successes = [
1394        ('X Y', NS(a=None, x='X', y=['Y'])),
1395        ('X -a A Y Z', NS(a='A', x='X', y=['Y', 'Z'])),
1396        ('@hello X', NS(a=None, x='hello world!', y=['X'])),
1397        ('X @hello', NS(a=None, x='X', y=['hello world!'])),
1398        ('-a B @recursive Y Z', NS(a='A', x='hello world!', y=['Y', 'Z'])),
1399        ('X @recursive Z -a B', NS(a='B', x='X', y=['hello world!', 'Z'])),
1400        (["-a", "", "X", "Y"], NS(a='', x='X', y=['Y'])),
1401    ]
1402
1403
1404class TestArgumentsFromFileConverter(TempDirMixin, ParserTestCase):
1405    """Test reading arguments from a file"""
1406
1407    def setUp(self):
1408        super(TestArgumentsFromFileConverter, self).setUp()
1409        file_texts = [
1410            ('hello', 'hello world!\n'),
1411        ]
1412        for path, text in file_texts:
1413            file = open(path, 'w')
1414            file.write(text)
1415            file.close()
1416
1417    class FromFileConverterArgumentParser(ErrorRaisingArgumentParser):
1418
1419        def convert_arg_line_to_args(self, arg_line):
1420            for arg in arg_line.split():
1421                if not arg.strip():
1422                    continue
1423                yield arg
1424    parser_class = FromFileConverterArgumentParser
1425    parser_signature = Sig(fromfile_prefix_chars='@')
1426    argument_signatures = [
1427        Sig('y', nargs='+'),
1428    ]
1429    failures = []
1430    successes = [
1431        ('@hello X', NS(y=['hello', 'world!', 'X'])),
1432    ]
1433
1434
1435# =====================
1436# Type conversion tests
1437# =====================
1438
1439class TestFileTypeRepr(TestCase):
1440
1441    def test_r(self):
1442        type = argparse.FileType('r')
1443        self.assertEqual("FileType('r')", repr(type))
1444
1445    def test_wb_1(self):
1446        type = argparse.FileType('wb', 1)
1447        self.assertEqual("FileType('wb', 1)", repr(type))
1448
1449    def test_r_latin(self):
1450        type = argparse.FileType('r', encoding='latin_1')
1451        self.assertEqual("FileType('r', encoding='latin_1')", repr(type))
1452
1453    def test_w_big5_ignore(self):
1454        type = argparse.FileType('w', encoding='big5', errors='ignore')
1455        self.assertEqual("FileType('w', encoding='big5', errors='ignore')",
1456                         repr(type))
1457
1458    def test_r_1_replace(self):
1459        type = argparse.FileType('r', 1, errors='replace')
1460        self.assertEqual("FileType('r', 1, errors='replace')", repr(type))
1461
1462class StdStreamComparer:
1463    def __init__(self, attr):
1464        self.attr = attr
1465
1466    def __eq__(self, other):
1467        return other == getattr(sys, self.attr)
1468
1469eq_stdin = StdStreamComparer('stdin')
1470eq_stdout = StdStreamComparer('stdout')
1471eq_stderr = StdStreamComparer('stderr')
1472
1473class RFile(object):
1474    seen = {}
1475
1476    def __init__(self, name):
1477        self.name = name
1478
1479    def __eq__(self, other):
1480        if other in self.seen:
1481            text = self.seen[other]
1482        else:
1483            text = self.seen[other] = other.read()
1484            other.close()
1485        if not isinstance(text, str):
1486            text = text.decode('ascii')
1487        return self.name == other.name == text
1488
1489
1490class TestFileTypeR(TempDirMixin, ParserTestCase):
1491    """Test the FileType option/argument type for reading files"""
1492
1493    def setUp(self):
1494        super(TestFileTypeR, self).setUp()
1495        for file_name in ['foo', 'bar']:
1496            file = open(os.path.join(self.temp_dir, file_name), 'w')
1497            file.write(file_name)
1498            file.close()
1499        self.create_readonly_file('readonly')
1500
1501    argument_signatures = [
1502        Sig('-x', type=argparse.FileType()),
1503        Sig('spam', type=argparse.FileType('r')),
1504    ]
1505    failures = ['-x', '', 'non-existent-file.txt']
1506    successes = [
1507        ('foo', NS(x=None, spam=RFile('foo'))),
1508        ('-x foo bar', NS(x=RFile('foo'), spam=RFile('bar'))),
1509        ('bar -x foo', NS(x=RFile('foo'), spam=RFile('bar'))),
1510        ('-x - -', NS(x=eq_stdin, spam=eq_stdin)),
1511        ('readonly', NS(x=None, spam=RFile('readonly'))),
1512    ]
1513
1514class TestFileTypeDefaults(TempDirMixin, ParserTestCase):
1515    """Test that a file is not created unless the default is needed"""
1516    def setUp(self):
1517        super(TestFileTypeDefaults, self).setUp()
1518        file = open(os.path.join(self.temp_dir, 'good'), 'w')
1519        file.write('good')
1520        file.close()
1521
1522    argument_signatures = [
1523        Sig('-c', type=argparse.FileType('r'), default='no-file.txt'),
1524    ]
1525    # should provoke no such file error
1526    failures = ['']
1527    # should not provoke error because default file is created
1528    successes = [('-c good', NS(c=RFile('good')))]
1529
1530
1531class TestFileTypeRB(TempDirMixin, ParserTestCase):
1532    """Test the FileType option/argument type for reading files"""
1533
1534    def setUp(self):
1535        super(TestFileTypeRB, self).setUp()
1536        for file_name in ['foo', 'bar']:
1537            file = open(os.path.join(self.temp_dir, file_name), 'w')
1538            file.write(file_name)
1539            file.close()
1540
1541    argument_signatures = [
1542        Sig('-x', type=argparse.FileType('rb')),
1543        Sig('spam', type=argparse.FileType('rb')),
1544    ]
1545    failures = ['-x', '']
1546    successes = [
1547        ('foo', NS(x=None, spam=RFile('foo'))),
1548        ('-x foo bar', NS(x=RFile('foo'), spam=RFile('bar'))),
1549        ('bar -x foo', NS(x=RFile('foo'), spam=RFile('bar'))),
1550        ('-x - -', NS(x=eq_stdin, spam=eq_stdin)),
1551    ]
1552
1553
1554class WFile(object):
1555    seen = set()
1556
1557    def __init__(self, name):
1558        self.name = name
1559
1560    def __eq__(self, other):
1561        if other not in self.seen:
1562            text = 'Check that file is writable.'
1563            if 'b' in other.mode:
1564                text = text.encode('ascii')
1565            other.write(text)
1566            other.close()
1567            self.seen.add(other)
1568        return self.name == other.name
1569
1570
1571@unittest.skipIf(hasattr(os, 'geteuid') and os.geteuid() == 0,
1572                 "non-root user required")
1573class TestFileTypeW(TempDirMixin, ParserTestCase):
1574    """Test the FileType option/argument type for writing files"""
1575
1576    def setUp(self):
1577        super(TestFileTypeW, self).setUp()
1578        self.create_readonly_file('readonly')
1579
1580    argument_signatures = [
1581        Sig('-x', type=argparse.FileType('w')),
1582        Sig('spam', type=argparse.FileType('w')),
1583    ]
1584    failures = ['-x', '', 'readonly']
1585    successes = [
1586        ('foo', NS(x=None, spam=WFile('foo'))),
1587        ('-x foo bar', NS(x=WFile('foo'), spam=WFile('bar'))),
1588        ('bar -x foo', NS(x=WFile('foo'), spam=WFile('bar'))),
1589        ('-x - -', NS(x=eq_stdout, spam=eq_stdout)),
1590    ]
1591
1592
1593class TestFileTypeWB(TempDirMixin, ParserTestCase):
1594
1595    argument_signatures = [
1596        Sig('-x', type=argparse.FileType('wb')),
1597        Sig('spam', type=argparse.FileType('wb')),
1598    ]
1599    failures = ['-x', '']
1600    successes = [
1601        ('foo', NS(x=None, spam=WFile('foo'))),
1602        ('-x foo bar', NS(x=WFile('foo'), spam=WFile('bar'))),
1603        ('bar -x foo', NS(x=WFile('foo'), spam=WFile('bar'))),
1604        ('-x - -', NS(x=eq_stdout, spam=eq_stdout)),
1605    ]
1606
1607
1608class TestFileTypeOpenArgs(TestCase):
1609    """Test that open (the builtin) is correctly called"""
1610
1611    def test_open_args(self):
1612        FT = argparse.FileType
1613        cases = [
1614            (FT('rb'), ('rb', -1, None, None)),
1615            (FT('w', 1), ('w', 1, None, None)),
1616            (FT('w', errors='replace'), ('w', -1, None, 'replace')),
1617            (FT('wb', encoding='big5'), ('wb', -1, 'big5', None)),
1618            (FT('w', 0, 'l1', 'strict'), ('w', 0, 'l1', 'strict')),
1619        ]
1620        with mock.patch('builtins.open') as m:
1621            for type, args in cases:
1622                type('foo')
1623                m.assert_called_with('foo', *args)
1624
1625
1626class TestTypeCallable(ParserTestCase):
1627    """Test some callables as option/argument types"""
1628
1629    argument_signatures = [
1630        Sig('--eggs', type=complex),
1631        Sig('spam', type=float),
1632    ]
1633    failures = ['a', '42j', '--eggs a', '--eggs 2i']
1634    successes = [
1635        ('--eggs=42 42', NS(eggs=42, spam=42.0)),
1636        ('--eggs 2j -- -1.5', NS(eggs=2j, spam=-1.5)),
1637        ('1024.675', NS(eggs=None, spam=1024.675)),
1638    ]
1639
1640
1641class TestTypeUserDefined(ParserTestCase):
1642    """Test a user-defined option/argument type"""
1643
1644    class MyType(TestCase):
1645
1646        def __init__(self, value):
1647            self.value = value
1648
1649        def __eq__(self, other):
1650            return (type(self), self.value) == (type(other), other.value)
1651
1652    argument_signatures = [
1653        Sig('-x', type=MyType),
1654        Sig('spam', type=MyType),
1655    ]
1656    failures = []
1657    successes = [
1658        ('a -x b', NS(x=MyType('b'), spam=MyType('a'))),
1659        ('-xf g', NS(x=MyType('f'), spam=MyType('g'))),
1660    ]
1661
1662
1663class TestTypeClassicClass(ParserTestCase):
1664    """Test a classic class type"""
1665
1666    class C:
1667
1668        def __init__(self, value):
1669            self.value = value
1670
1671        def __eq__(self, other):
1672            return (type(self), self.value) == (type(other), other.value)
1673
1674    argument_signatures = [
1675        Sig('-x', type=C),
1676        Sig('spam', type=C),
1677    ]
1678    failures = []
1679    successes = [
1680        ('a -x b', NS(x=C('b'), spam=C('a'))),
1681        ('-xf g', NS(x=C('f'), spam=C('g'))),
1682    ]
1683
1684
1685class TestTypeRegistration(TestCase):
1686    """Test a user-defined type by registering it"""
1687
1688    def test(self):
1689
1690        def get_my_type(string):
1691            return 'my_type{%s}' % string
1692
1693        parser = argparse.ArgumentParser()
1694        parser.register('type', 'my_type', get_my_type)
1695        parser.add_argument('-x', type='my_type')
1696        parser.add_argument('y', type='my_type')
1697
1698        self.assertEqual(parser.parse_args('1'.split()),
1699                         NS(x=None, y='my_type{1}'))
1700        self.assertEqual(parser.parse_args('-x 1 42'.split()),
1701                         NS(x='my_type{1}', y='my_type{42}'))
1702
1703
1704# ============
1705# Action tests
1706# ============
1707
1708class TestActionUserDefined(ParserTestCase):
1709    """Test a user-defined option/argument action"""
1710
1711    class OptionalAction(argparse.Action):
1712
1713        def __call__(self, parser, namespace, value, option_string=None):
1714            try:
1715                # check destination and option string
1716                assert self.dest == 'spam', 'dest: %s' % self.dest
1717                assert option_string == '-s', 'flag: %s' % option_string
1718                # when option is before argument, badger=2, and when
1719                # option is after argument, badger=<whatever was set>
1720                expected_ns = NS(spam=0.25)
1721                if value in [0.125, 0.625]:
1722                    expected_ns.badger = 2
1723                elif value in [2.0]:
1724                    expected_ns.badger = 84
1725                else:
1726                    raise AssertionError('value: %s' % value)
1727                assert expected_ns == namespace, ('expected %s, got %s' %
1728                                                  (expected_ns, namespace))
1729            except AssertionError:
1730                e = sys.exc_info()[1]
1731                raise ArgumentParserError('opt_action failed: %s' % e)
1732            setattr(namespace, 'spam', value)
1733
1734    class PositionalAction(argparse.Action):
1735
1736        def __call__(self, parser, namespace, value, option_string=None):
1737            try:
1738                assert option_string is None, ('option_string: %s' %
1739                                               option_string)
1740                # check destination
1741                assert self.dest == 'badger', 'dest: %s' % self.dest
1742                # when argument is before option, spam=0.25, and when
1743                # option is after argument, spam=<whatever was set>
1744                expected_ns = NS(badger=2)
1745                if value in [42, 84]:
1746                    expected_ns.spam = 0.25
1747                elif value in [1]:
1748                    expected_ns.spam = 0.625
1749                elif value in [2]:
1750                    expected_ns.spam = 0.125
1751                else:
1752                    raise AssertionError('value: %s' % value)
1753                assert expected_ns == namespace, ('expected %s, got %s' %
1754                                                  (expected_ns, namespace))
1755            except AssertionError:
1756                e = sys.exc_info()[1]
1757                raise ArgumentParserError('arg_action failed: %s' % e)
1758            setattr(namespace, 'badger', value)
1759
1760    argument_signatures = [
1761        Sig('-s', dest='spam', action=OptionalAction,
1762            type=float, default=0.25),
1763        Sig('badger', action=PositionalAction,
1764            type=int, nargs='?', default=2),
1765    ]
1766    failures = []
1767    successes = [
1768        ('-s0.125', NS(spam=0.125, badger=2)),
1769        ('42', NS(spam=0.25, badger=42)),
1770        ('-s 0.625 1', NS(spam=0.625, badger=1)),
1771        ('84 -s2', NS(spam=2.0, badger=84)),
1772    ]
1773
1774
1775class TestActionRegistration(TestCase):
1776    """Test a user-defined action supplied by registering it"""
1777
1778    class MyAction(argparse.Action):
1779
1780        def __call__(self, parser, namespace, values, option_string=None):
1781            setattr(namespace, self.dest, 'foo[%s]' % values)
1782
1783    def test(self):
1784
1785        parser = argparse.ArgumentParser()
1786        parser.register('action', 'my_action', self.MyAction)
1787        parser.add_argument('badger', action='my_action')
1788
1789        self.assertEqual(parser.parse_args(['1']), NS(badger='foo[1]'))
1790        self.assertEqual(parser.parse_args(['42']), NS(badger='foo[42]'))
1791
1792
1793# ================
1794# Subparsers tests
1795# ================
1796
1797class TestAddSubparsers(TestCase):
1798    """Test the add_subparsers method"""
1799
1800    def assertArgumentParserError(self, *args, **kwargs):
1801        self.assertRaises(ArgumentParserError, *args, **kwargs)
1802
1803    def _get_parser(self, subparser_help=False, prefix_chars=None,
1804                    aliases=False):
1805        # create a parser with a subparsers argument
1806        if prefix_chars:
1807            parser = ErrorRaisingArgumentParser(
1808                prog='PROG', description='main description', prefix_chars=prefix_chars)
1809            parser.add_argument(
1810                prefix_chars[0] * 2 + 'foo', action='store_true', help='foo help')
1811        else:
1812            parser = ErrorRaisingArgumentParser(
1813                prog='PROG', description='main description')
1814            parser.add_argument(
1815                '--foo', action='store_true', help='foo help')
1816        parser.add_argument(
1817            'bar', type=float, help='bar help')
1818
1819        # check that only one subparsers argument can be added
1820        subparsers_kwargs = {'required': False}
1821        if aliases:
1822            subparsers_kwargs['metavar'] = 'COMMAND'
1823            subparsers_kwargs['title'] = 'commands'
1824        else:
1825            subparsers_kwargs['help'] = 'command help'
1826        subparsers = parser.add_subparsers(**subparsers_kwargs)
1827        self.assertArgumentParserError(parser.add_subparsers)
1828
1829        # add first sub-parser
1830        parser1_kwargs = dict(description='1 description')
1831        if subparser_help:
1832            parser1_kwargs['help'] = '1 help'
1833        if aliases:
1834            parser1_kwargs['aliases'] = ['1alias1', '1alias2']
1835        parser1 = subparsers.add_parser('1', **parser1_kwargs)
1836        parser1.add_argument('-w', type=int, help='w help')
1837        parser1.add_argument('x', choices='abc', help='x help')
1838
1839        # add second sub-parser
1840        parser2_kwargs = dict(description='2 description')
1841        if subparser_help:
1842            parser2_kwargs['help'] = '2 help'
1843        parser2 = subparsers.add_parser('2', **parser2_kwargs)
1844        parser2.add_argument('-y', choices='123', help='y help')
1845        parser2.add_argument('z', type=complex, nargs='*', help='z help')
1846
1847        # add third sub-parser
1848        parser3_kwargs = dict(description='3 description')
1849        if subparser_help:
1850            parser3_kwargs['help'] = '3 help'
1851        parser3 = subparsers.add_parser('3', **parser3_kwargs)
1852        parser3.add_argument('t', type=int, help='t help')
1853        parser3.add_argument('u', nargs='...', help='u help')
1854
1855        # return the main parser
1856        return parser
1857
1858    def setUp(self):
1859        super().setUp()
1860        self.parser = self._get_parser()
1861        self.command_help_parser = self._get_parser(subparser_help=True)
1862
1863    def test_parse_args_failures(self):
1864        # check some failure cases:
1865        for args_str in ['', 'a', 'a a', '0.5 a', '0.5 1',
1866                         '0.5 1 -y', '0.5 2 -w']:
1867            args = args_str.split()
1868            self.assertArgumentParserError(self.parser.parse_args, args)
1869
1870    def test_parse_args(self):
1871        # check some non-failure cases:
1872        self.assertEqual(
1873            self.parser.parse_args('0.5 1 b -w 7'.split()),
1874            NS(foo=False, bar=0.5, w=7, x='b'),
1875        )
1876        self.assertEqual(
1877            self.parser.parse_args('0.25 --foo 2 -y 2 3j -- -1j'.split()),
1878            NS(foo=True, bar=0.25, y='2', z=[3j, -1j]),
1879        )
1880        self.assertEqual(
1881            self.parser.parse_args('--foo 0.125 1 c'.split()),
1882            NS(foo=True, bar=0.125, w=None, x='c'),
1883        )
1884        self.assertEqual(
1885            self.parser.parse_args('-1.5 3 11 -- a --foo 7 -- b'.split()),
1886            NS(foo=False, bar=-1.5, t=11, u=['a', '--foo', '7', '--', 'b']),
1887        )
1888
1889    def test_parse_known_args(self):
1890        self.assertEqual(
1891            self.parser.parse_known_args('0.5 1 b -w 7'.split()),
1892            (NS(foo=False, bar=0.5, w=7, x='b'), []),
1893        )
1894        self.assertEqual(
1895            self.parser.parse_known_args('0.5 -p 1 b -w 7'.split()),
1896            (NS(foo=False, bar=0.5, w=7, x='b'), ['-p']),
1897        )
1898        self.assertEqual(
1899            self.parser.parse_known_args('0.5 1 b -w 7 -p'.split()),
1900            (NS(foo=False, bar=0.5, w=7, x='b'), ['-p']),
1901        )
1902        self.assertEqual(
1903            self.parser.parse_known_args('0.5 1 b -q -rs -w 7'.split()),
1904            (NS(foo=False, bar=0.5, w=7, x='b'), ['-q', '-rs']),
1905        )
1906        self.assertEqual(
1907            self.parser.parse_known_args('0.5 -W 1 b -X Y -w 7 Z'.split()),
1908            (NS(foo=False, bar=0.5, w=7, x='b'), ['-W', '-X', 'Y', 'Z']),
1909        )
1910
1911    def test_dest(self):
1912        parser = ErrorRaisingArgumentParser()
1913        parser.add_argument('--foo', action='store_true')
1914        subparsers = parser.add_subparsers(dest='bar')
1915        parser1 = subparsers.add_parser('1')
1916        parser1.add_argument('baz')
1917        self.assertEqual(NS(foo=False, bar='1', baz='2'),
1918                         parser.parse_args('1 2'.split()))
1919
1920    def _test_required_subparsers(self, parser):
1921        # Should parse the sub command
1922        ret = parser.parse_args(['run'])
1923        self.assertEqual(ret.command, 'run')
1924
1925        # Error when the command is missing
1926        self.assertArgumentParserError(parser.parse_args, ())
1927
1928    def test_required_subparsers_via_attribute(self):
1929        parser = ErrorRaisingArgumentParser()
1930        subparsers = parser.add_subparsers(dest='command')
1931        subparsers.required = True
1932        subparsers.add_parser('run')
1933        self._test_required_subparsers(parser)
1934
1935    def test_required_subparsers_via_kwarg(self):
1936        parser = ErrorRaisingArgumentParser()
1937        subparsers = parser.add_subparsers(dest='command', required=True)
1938        subparsers.add_parser('run')
1939        self._test_required_subparsers(parser)
1940
1941    def test_required_subparsers_default(self):
1942        parser = ErrorRaisingArgumentParser()
1943        subparsers = parser.add_subparsers(dest='command')
1944        subparsers.add_parser('run')
1945        # No error here
1946        ret = parser.parse_args(())
1947        self.assertIsNone(ret.command)
1948
1949    def test_optional_subparsers(self):
1950        parser = ErrorRaisingArgumentParser()
1951        subparsers = parser.add_subparsers(dest='command', required=False)
1952        subparsers.add_parser('run')
1953        # No error here
1954        ret = parser.parse_args(())
1955        self.assertIsNone(ret.command)
1956
1957    def test_help(self):
1958        self.assertEqual(self.parser.format_usage(),
1959                         'usage: PROG [-h] [--foo] bar {1,2,3} ...\n')
1960        self.assertEqual(self.parser.format_help(), textwrap.dedent('''\
1961            usage: PROG [-h] [--foo] bar {1,2,3} ...
1962
1963            main description
1964
1965            positional arguments:
1966              bar         bar help
1967              {1,2,3}     command help
1968
1969            optional arguments:
1970              -h, --help  show this help message and exit
1971              --foo       foo help
1972            '''))
1973
1974    def test_help_extra_prefix_chars(self):
1975        # Make sure - is still used for help if it is a non-first prefix char
1976        parser = self._get_parser(prefix_chars='+:-')
1977        self.assertEqual(parser.format_usage(),
1978                         'usage: PROG [-h] [++foo] bar {1,2,3} ...\n')
1979        self.assertEqual(parser.format_help(), textwrap.dedent('''\
1980            usage: PROG [-h] [++foo] bar {1,2,3} ...
1981
1982            main description
1983
1984            positional arguments:
1985              bar         bar help
1986              {1,2,3}     command help
1987
1988            optional arguments:
1989              -h, --help  show this help message and exit
1990              ++foo       foo help
1991            '''))
1992
1993    def test_help_non_breaking_spaces(self):
1994        parser = ErrorRaisingArgumentParser(
1995            prog='PROG', description='main description')
1996        parser.add_argument(
1997            "--non-breaking", action='store_false',
1998            help='help message containing non-breaking spaces shall not '
1999            'wrap\N{NO-BREAK SPACE}at non-breaking spaces')
2000        self.assertEqual(parser.format_help(), textwrap.dedent('''\
2001            usage: PROG [-h] [--non-breaking]
2002
2003            main description
2004
2005            optional arguments:
2006              -h, --help      show this help message and exit
2007              --non-breaking  help message containing non-breaking spaces shall not
2008                              wrap\N{NO-BREAK SPACE}at non-breaking spaces
2009        '''))
2010
2011    def test_help_alternate_prefix_chars(self):
2012        parser = self._get_parser(prefix_chars='+:/')
2013        self.assertEqual(parser.format_usage(),
2014                         'usage: PROG [+h] [++foo] bar {1,2,3} ...\n')
2015        self.assertEqual(parser.format_help(), textwrap.dedent('''\
2016            usage: PROG [+h] [++foo] bar {1,2,3} ...
2017
2018            main description
2019
2020            positional arguments:
2021              bar         bar help
2022              {1,2,3}     command help
2023
2024            optional arguments:
2025              +h, ++help  show this help message and exit
2026              ++foo       foo help
2027            '''))
2028
2029    def test_parser_command_help(self):
2030        self.assertEqual(self.command_help_parser.format_usage(),
2031                         'usage: PROG [-h] [--foo] bar {1,2,3} ...\n')
2032        self.assertEqual(self.command_help_parser.format_help(),
2033                         textwrap.dedent('''\
2034            usage: PROG [-h] [--foo] bar {1,2,3} ...
2035
2036            main description
2037
2038            positional arguments:
2039              bar         bar help
2040              {1,2,3}     command help
2041                1         1 help
2042                2         2 help
2043                3         3 help
2044
2045            optional arguments:
2046              -h, --help  show this help message and exit
2047              --foo       foo help
2048            '''))
2049
2050    def test_subparser_title_help(self):
2051        parser = ErrorRaisingArgumentParser(prog='PROG',
2052                                            description='main description')
2053        parser.add_argument('--foo', action='store_true', help='foo help')
2054        parser.add_argument('bar', help='bar help')
2055        subparsers = parser.add_subparsers(title='subcommands',
2056                                           description='command help',
2057                                           help='additional text')
2058        parser1 = subparsers.add_parser('1')
2059        parser2 = subparsers.add_parser('2')
2060        self.assertEqual(parser.format_usage(),
2061                         'usage: PROG [-h] [--foo] bar {1,2} ...\n')
2062        self.assertEqual(parser.format_help(), textwrap.dedent('''\
2063            usage: PROG [-h] [--foo] bar {1,2} ...
2064
2065            main description
2066
2067            positional arguments:
2068              bar         bar help
2069
2070            optional arguments:
2071              -h, --help  show this help message and exit
2072              --foo       foo help
2073
2074            subcommands:
2075              command help
2076
2077              {1,2}       additional text
2078            '''))
2079
2080    def _test_subparser_help(self, args_str, expected_help):
2081        with self.assertRaises(ArgumentParserError) as cm:
2082            self.parser.parse_args(args_str.split())
2083        self.assertEqual(expected_help, cm.exception.stdout)
2084
2085    def test_subparser1_help(self):
2086        self._test_subparser_help('5.0 1 -h', textwrap.dedent('''\
2087            usage: PROG bar 1 [-h] [-w W] {a,b,c}
2088
2089            1 description
2090
2091            positional arguments:
2092              {a,b,c}     x help
2093
2094            optional arguments:
2095              -h, --help  show this help message and exit
2096              -w W        w help
2097            '''))
2098
2099    def test_subparser2_help(self):
2100        self._test_subparser_help('5.0 2 -h', textwrap.dedent('''\
2101            usage: PROG bar 2 [-h] [-y {1,2,3}] [z [z ...]]
2102
2103            2 description
2104
2105            positional arguments:
2106              z           z help
2107
2108            optional arguments:
2109              -h, --help  show this help message and exit
2110              -y {1,2,3}  y help
2111            '''))
2112
2113    def test_alias_invocation(self):
2114        parser = self._get_parser(aliases=True)
2115        self.assertEqual(
2116            parser.parse_known_args('0.5 1alias1 b'.split()),
2117            (NS(foo=False, bar=0.5, w=None, x='b'), []),
2118        )
2119        self.assertEqual(
2120            parser.parse_known_args('0.5 1alias2 b'.split()),
2121            (NS(foo=False, bar=0.5, w=None, x='b'), []),
2122        )
2123
2124    def test_error_alias_invocation(self):
2125        parser = self._get_parser(aliases=True)
2126        self.assertArgumentParserError(parser.parse_args,
2127                                       '0.5 1alias3 b'.split())
2128
2129    def test_alias_help(self):
2130        parser = self._get_parser(aliases=True, subparser_help=True)
2131        self.maxDiff = None
2132        self.assertEqual(parser.format_help(), textwrap.dedent("""\
2133            usage: PROG [-h] [--foo] bar COMMAND ...
2134
2135            main description
2136
2137            positional arguments:
2138              bar                   bar help
2139
2140            optional arguments:
2141              -h, --help            show this help message and exit
2142              --foo                 foo help
2143
2144            commands:
2145              COMMAND
2146                1 (1alias1, 1alias2)
2147                                    1 help
2148                2                   2 help
2149                3                   3 help
2150            """))
2151
2152# ============
2153# Groups tests
2154# ============
2155
2156class TestPositionalsGroups(TestCase):
2157    """Tests that order of group positionals matches construction order"""
2158
2159    def test_nongroup_first(self):
2160        parser = ErrorRaisingArgumentParser()
2161        parser.add_argument('foo')
2162        group = parser.add_argument_group('g')
2163        group.add_argument('bar')
2164        parser.add_argument('baz')
2165        expected = NS(foo='1', bar='2', baz='3')
2166        result = parser.parse_args('1 2 3'.split())
2167        self.assertEqual(expected, result)
2168
2169    def test_group_first(self):
2170        parser = ErrorRaisingArgumentParser()
2171        group = parser.add_argument_group('xxx')
2172        group.add_argument('foo')
2173        parser.add_argument('bar')
2174        parser.add_argument('baz')
2175        expected = NS(foo='1', bar='2', baz='3')
2176        result = parser.parse_args('1 2 3'.split())
2177        self.assertEqual(expected, result)
2178
2179    def test_interleaved_groups(self):
2180        parser = ErrorRaisingArgumentParser()
2181        group = parser.add_argument_group('xxx')
2182        parser.add_argument('foo')
2183        group.add_argument('bar')
2184        parser.add_argument('baz')
2185        group = parser.add_argument_group('yyy')
2186        group.add_argument('frell')
2187        expected = NS(foo='1', bar='2', baz='3', frell='4')
2188        result = parser.parse_args('1 2 3 4'.split())
2189        self.assertEqual(expected, result)
2190
2191# ===================
2192# Parent parser tests
2193# ===================
2194
2195class TestParentParsers(TestCase):
2196    """Tests that parsers can be created with parent parsers"""
2197
2198    def assertArgumentParserError(self, *args, **kwargs):
2199        self.assertRaises(ArgumentParserError, *args, **kwargs)
2200
2201    def setUp(self):
2202        super().setUp()
2203        self.wxyz_parent = ErrorRaisingArgumentParser(add_help=False)
2204        self.wxyz_parent.add_argument('--w')
2205        x_group = self.wxyz_parent.add_argument_group('x')
2206        x_group.add_argument('-y')
2207        self.wxyz_parent.add_argument('z')
2208
2209        self.abcd_parent = ErrorRaisingArgumentParser(add_help=False)
2210        self.abcd_parent.add_argument('a')
2211        self.abcd_parent.add_argument('-b')
2212        c_group = self.abcd_parent.add_argument_group('c')
2213        c_group.add_argument('--d')
2214
2215        self.w_parent = ErrorRaisingArgumentParser(add_help=False)
2216        self.w_parent.add_argument('--w')
2217
2218        self.z_parent = ErrorRaisingArgumentParser(add_help=False)
2219        self.z_parent.add_argument('z')
2220
2221        # parents with mutually exclusive groups
2222        self.ab_mutex_parent = ErrorRaisingArgumentParser(add_help=False)
2223        group = self.ab_mutex_parent.add_mutually_exclusive_group()
2224        group.add_argument('-a', action='store_true')
2225        group.add_argument('-b', action='store_true')
2226
2227        self.main_program = os.path.basename(sys.argv[0])
2228
2229    def test_single_parent(self):
2230        parser = ErrorRaisingArgumentParser(parents=[self.wxyz_parent])
2231        self.assertEqual(parser.parse_args('-y 1 2 --w 3'.split()),
2232                         NS(w='3', y='1', z='2'))
2233
2234    def test_single_parent_mutex(self):
2235        self._test_mutex_ab(self.ab_mutex_parent.parse_args)
2236        parser = ErrorRaisingArgumentParser(parents=[self.ab_mutex_parent])
2237        self._test_mutex_ab(parser.parse_args)
2238
2239    def test_single_granparent_mutex(self):
2240        parents = [self.ab_mutex_parent]
2241        parser = ErrorRaisingArgumentParser(add_help=False, parents=parents)
2242        parser = ErrorRaisingArgumentParser(parents=[parser])
2243        self._test_mutex_ab(parser.parse_args)
2244
2245    def _test_mutex_ab(self, parse_args):
2246        self.assertEqual(parse_args([]), NS(a=False, b=False))
2247        self.assertEqual(parse_args(['-a']), NS(a=True, b=False))
2248        self.assertEqual(parse_args(['-b']), NS(a=False, b=True))
2249        self.assertArgumentParserError(parse_args, ['-a', '-b'])
2250        self.assertArgumentParserError(parse_args, ['-b', '-a'])
2251        self.assertArgumentParserError(parse_args, ['-c'])
2252        self.assertArgumentParserError(parse_args, ['-a', '-c'])
2253        self.assertArgumentParserError(parse_args, ['-b', '-c'])
2254
2255    def test_multiple_parents(self):
2256        parents = [self.abcd_parent, self.wxyz_parent]
2257        parser = ErrorRaisingArgumentParser(parents=parents)
2258        self.assertEqual(parser.parse_args('--d 1 --w 2 3 4'.split()),
2259                         NS(a='3', b=None, d='1', w='2', y=None, z='4'))
2260
2261    def test_multiple_parents_mutex(self):
2262        parents = [self.ab_mutex_parent, self.wxyz_parent]
2263        parser = ErrorRaisingArgumentParser(parents=parents)
2264        self.assertEqual(parser.parse_args('-a --w 2 3'.split()),
2265                         NS(a=True, b=False, w='2', y=None, z='3'))
2266        self.assertArgumentParserError(
2267            parser.parse_args, '-a --w 2 3 -b'.split())
2268        self.assertArgumentParserError(
2269            parser.parse_args, '-a -b --w 2 3'.split())
2270
2271    def test_conflicting_parents(self):
2272        self.assertRaises(
2273            argparse.ArgumentError,
2274            argparse.ArgumentParser,
2275            parents=[self.w_parent, self.wxyz_parent])
2276
2277    def test_conflicting_parents_mutex(self):
2278        self.assertRaises(
2279            argparse.ArgumentError,
2280            argparse.ArgumentParser,
2281            parents=[self.abcd_parent, self.ab_mutex_parent])
2282
2283    def test_same_argument_name_parents(self):
2284        parents = [self.wxyz_parent, self.z_parent]
2285        parser = ErrorRaisingArgumentParser(parents=parents)
2286        self.assertEqual(parser.parse_args('1 2'.split()),
2287                         NS(w=None, y=None, z='2'))
2288
2289    def test_subparser_parents(self):
2290        parser = ErrorRaisingArgumentParser()
2291        subparsers = parser.add_subparsers()
2292        abcde_parser = subparsers.add_parser('bar', parents=[self.abcd_parent])
2293        abcde_parser.add_argument('e')
2294        self.assertEqual(parser.parse_args('bar -b 1 --d 2 3 4'.split()),
2295                         NS(a='3', b='1', d='2', e='4'))
2296
2297    def test_subparser_parents_mutex(self):
2298        parser = ErrorRaisingArgumentParser()
2299        subparsers = parser.add_subparsers()
2300        parents = [self.ab_mutex_parent]
2301        abc_parser = subparsers.add_parser('foo', parents=parents)
2302        c_group = abc_parser.add_argument_group('c_group')
2303        c_group.add_argument('c')
2304        parents = [self.wxyz_parent, self.ab_mutex_parent]
2305        wxyzabe_parser = subparsers.add_parser('bar', parents=parents)
2306        wxyzabe_parser.add_argument('e')
2307        self.assertEqual(parser.parse_args('foo -a 4'.split()),
2308                         NS(a=True, b=False, c='4'))
2309        self.assertEqual(parser.parse_args('bar -b  --w 2 3 4'.split()),
2310                         NS(a=False, b=True, w='2', y=None, z='3', e='4'))
2311        self.assertArgumentParserError(
2312            parser.parse_args, 'foo -a -b 4'.split())
2313        self.assertArgumentParserError(
2314            parser.parse_args, 'bar -b -a 4'.split())
2315
2316    def test_parent_help(self):
2317        parents = [self.abcd_parent, self.wxyz_parent]
2318        parser = ErrorRaisingArgumentParser(parents=parents)
2319        parser_help = parser.format_help()
2320        progname = self.main_program
2321        self.assertEqual(parser_help, textwrap.dedent('''\
2322            usage: {}{}[-h] [-b B] [--d D] [--w W] [-y Y] a z
2323
2324            positional arguments:
2325              a
2326              z
2327
2328            optional arguments:
2329              -h, --help  show this help message and exit
2330              -b B
2331              --w W
2332
2333            c:
2334              --d D
2335
2336            x:
2337              -y Y
2338        '''.format(progname, ' ' if progname else '' )))
2339
2340    def test_groups_parents(self):
2341        parent = ErrorRaisingArgumentParser(add_help=False)
2342        g = parent.add_argument_group(title='g', description='gd')
2343        g.add_argument('-w')
2344        g.add_argument('-x')
2345        m = parent.add_mutually_exclusive_group()
2346        m.add_argument('-y')
2347        m.add_argument('-z')
2348        parser = ErrorRaisingArgumentParser(parents=[parent])
2349
2350        self.assertRaises(ArgumentParserError, parser.parse_args,
2351            ['-y', 'Y', '-z', 'Z'])
2352
2353        parser_help = parser.format_help()
2354        progname = self.main_program
2355        self.assertEqual(parser_help, textwrap.dedent('''\
2356            usage: {}{}[-h] [-w W] [-x X] [-y Y | -z Z]
2357
2358            optional arguments:
2359              -h, --help  show this help message and exit
2360              -y Y
2361              -z Z
2362
2363            g:
2364              gd
2365
2366              -w W
2367              -x X
2368        '''.format(progname, ' ' if progname else '' )))
2369
2370# ==============================
2371# Mutually exclusive group tests
2372# ==============================
2373
2374class TestMutuallyExclusiveGroupErrors(TestCase):
2375
2376    def test_invalid_add_argument_group(self):
2377        parser = ErrorRaisingArgumentParser()
2378        raises = self.assertRaises
2379        raises(TypeError, parser.add_mutually_exclusive_group, title='foo')
2380
2381    def test_invalid_add_argument(self):
2382        parser = ErrorRaisingArgumentParser()
2383        group = parser.add_mutually_exclusive_group()
2384        add_argument = group.add_argument
2385        raises = self.assertRaises
2386        raises(ValueError, add_argument, '--foo', required=True)
2387        raises(ValueError, add_argument, 'bar')
2388        raises(ValueError, add_argument, 'bar', nargs='+')
2389        raises(ValueError, add_argument, 'bar', nargs=1)
2390        raises(ValueError, add_argument, 'bar', nargs=argparse.PARSER)
2391
2392    def test_help(self):
2393        parser = ErrorRaisingArgumentParser(prog='PROG')
2394        group1 = parser.add_mutually_exclusive_group()
2395        group1.add_argument('--foo', action='store_true')
2396        group1.add_argument('--bar', action='store_false')
2397        group2 = parser.add_mutually_exclusive_group()
2398        group2.add_argument('--soup', action='store_true')
2399        group2.add_argument('--nuts', action='store_false')
2400        expected = '''\
2401            usage: PROG [-h] [--foo | --bar] [--soup | --nuts]
2402
2403            optional arguments:
2404              -h, --help  show this help message and exit
2405              --foo
2406              --bar
2407              --soup
2408              --nuts
2409              '''
2410        self.assertEqual(parser.format_help(), textwrap.dedent(expected))
2411
2412class MEMixin(object):
2413
2414    def test_failures_when_not_required(self):
2415        parse_args = self.get_parser(required=False).parse_args
2416        error = ArgumentParserError
2417        for args_string in self.failures:
2418            self.assertRaises(error, parse_args, args_string.split())
2419
2420    def test_failures_when_required(self):
2421        parse_args = self.get_parser(required=True).parse_args
2422        error = ArgumentParserError
2423        for args_string in self.failures + ['']:
2424            self.assertRaises(error, parse_args, args_string.split())
2425
2426    def test_successes_when_not_required(self):
2427        parse_args = self.get_parser(required=False).parse_args
2428        successes = self.successes + self.successes_when_not_required
2429        for args_string, expected_ns in successes:
2430            actual_ns = parse_args(args_string.split())
2431            self.assertEqual(actual_ns, expected_ns)
2432
2433    def test_successes_when_required(self):
2434        parse_args = self.get_parser(required=True).parse_args
2435        for args_string, expected_ns in self.successes:
2436            actual_ns = parse_args(args_string.split())
2437            self.assertEqual(actual_ns, expected_ns)
2438
2439    def test_usage_when_not_required(self):
2440        format_usage = self.get_parser(required=False).format_usage
2441        expected_usage = self.usage_when_not_required
2442        self.assertEqual(format_usage(), textwrap.dedent(expected_usage))
2443
2444    def test_usage_when_required(self):
2445        format_usage = self.get_parser(required=True).format_usage
2446        expected_usage = self.usage_when_required
2447        self.assertEqual(format_usage(), textwrap.dedent(expected_usage))
2448
2449    def test_help_when_not_required(self):
2450        format_help = self.get_parser(required=False).format_help
2451        help = self.usage_when_not_required + self.help
2452        self.assertEqual(format_help(), textwrap.dedent(help))
2453
2454    def test_help_when_required(self):
2455        format_help = self.get_parser(required=True).format_help
2456        help = self.usage_when_required + self.help
2457        self.assertEqual(format_help(), textwrap.dedent(help))
2458
2459
2460class TestMutuallyExclusiveSimple(MEMixin, TestCase):
2461
2462    def get_parser(self, required=None):
2463        parser = ErrorRaisingArgumentParser(prog='PROG')
2464        group = parser.add_mutually_exclusive_group(required=required)
2465        group.add_argument('--bar', help='bar help')
2466        group.add_argument('--baz', nargs='?', const='Z', help='baz help')
2467        return parser
2468
2469    failures = ['--bar X --baz Y', '--bar X --baz']
2470    successes = [
2471        ('--bar X', NS(bar='X', baz=None)),
2472        ('--bar X --bar Z', NS(bar='Z', baz=None)),
2473        ('--baz Y', NS(bar=None, baz='Y')),
2474        ('--baz', NS(bar=None, baz='Z')),
2475    ]
2476    successes_when_not_required = [
2477        ('', NS(bar=None, baz=None)),
2478    ]
2479
2480    usage_when_not_required = '''\
2481        usage: PROG [-h] [--bar BAR | --baz [BAZ]]
2482        '''
2483    usage_when_required = '''\
2484        usage: PROG [-h] (--bar BAR | --baz [BAZ])
2485        '''
2486    help = '''\
2487
2488        optional arguments:
2489          -h, --help   show this help message and exit
2490          --bar BAR    bar help
2491          --baz [BAZ]  baz help
2492        '''
2493
2494
2495class TestMutuallyExclusiveLong(MEMixin, TestCase):
2496
2497    def get_parser(self, required=None):
2498        parser = ErrorRaisingArgumentParser(prog='PROG')
2499        parser.add_argument('--abcde', help='abcde help')
2500        parser.add_argument('--fghij', help='fghij help')
2501        group = parser.add_mutually_exclusive_group(required=required)
2502        group.add_argument('--klmno', help='klmno help')
2503        group.add_argument('--pqrst', help='pqrst help')
2504        return parser
2505
2506    failures = ['--klmno X --pqrst Y']
2507    successes = [
2508        ('--klmno X', NS(abcde=None, fghij=None, klmno='X', pqrst=None)),
2509        ('--abcde Y --klmno X',
2510            NS(abcde='Y', fghij=None, klmno='X', pqrst=None)),
2511        ('--pqrst X', NS(abcde=None, fghij=None, klmno=None, pqrst='X')),
2512        ('--pqrst X --fghij Y',
2513            NS(abcde=None, fghij='Y', klmno=None, pqrst='X')),
2514    ]
2515    successes_when_not_required = [
2516        ('', NS(abcde=None, fghij=None, klmno=None, pqrst=None)),
2517    ]
2518
2519    usage_when_not_required = '''\
2520    usage: PROG [-h] [--abcde ABCDE] [--fghij FGHIJ]
2521                [--klmno KLMNO | --pqrst PQRST]
2522    '''
2523    usage_when_required = '''\
2524    usage: PROG [-h] [--abcde ABCDE] [--fghij FGHIJ]
2525                (--klmno KLMNO | --pqrst PQRST)
2526    '''
2527    help = '''\
2528
2529    optional arguments:
2530      -h, --help     show this help message and exit
2531      --abcde ABCDE  abcde help
2532      --fghij FGHIJ  fghij help
2533      --klmno KLMNO  klmno help
2534      --pqrst PQRST  pqrst help
2535    '''
2536
2537
2538class TestMutuallyExclusiveFirstSuppressed(MEMixin, TestCase):
2539
2540    def get_parser(self, required):
2541        parser = ErrorRaisingArgumentParser(prog='PROG')
2542        group = parser.add_mutually_exclusive_group(required=required)
2543        group.add_argument('-x', help=argparse.SUPPRESS)
2544        group.add_argument('-y', action='store_false', help='y help')
2545        return parser
2546
2547    failures = ['-x X -y']
2548    successes = [
2549        ('-x X', NS(x='X', y=True)),
2550        ('-x X -x Y', NS(x='Y', y=True)),
2551        ('-y', NS(x=None, y=False)),
2552    ]
2553    successes_when_not_required = [
2554        ('', NS(x=None, y=True)),
2555    ]
2556
2557    usage_when_not_required = '''\
2558        usage: PROG [-h] [-y]
2559        '''
2560    usage_when_required = '''\
2561        usage: PROG [-h] -y
2562        '''
2563    help = '''\
2564
2565        optional arguments:
2566          -h, --help  show this help message and exit
2567          -y          y help
2568        '''
2569
2570
2571class TestMutuallyExclusiveManySuppressed(MEMixin, TestCase):
2572
2573    def get_parser(self, required):
2574        parser = ErrorRaisingArgumentParser(prog='PROG')
2575        group = parser.add_mutually_exclusive_group(required=required)
2576        add = group.add_argument
2577        add('--spam', action='store_true', help=argparse.SUPPRESS)
2578        add('--badger', action='store_false', help=argparse.SUPPRESS)
2579        add('--bladder', help=argparse.SUPPRESS)
2580        return parser
2581
2582    failures = [
2583        '--spam --badger',
2584        '--badger --bladder B',
2585        '--bladder B --spam',
2586    ]
2587    successes = [
2588        ('--spam', NS(spam=True, badger=True, bladder=None)),
2589        ('--badger', NS(spam=False, badger=False, bladder=None)),
2590        ('--bladder B', NS(spam=False, badger=True, bladder='B')),
2591        ('--spam --spam', NS(spam=True, badger=True, bladder=None)),
2592    ]
2593    successes_when_not_required = [
2594        ('', NS(spam=False, badger=True, bladder=None)),
2595    ]
2596
2597    usage_when_required = usage_when_not_required = '''\
2598        usage: PROG [-h]
2599        '''
2600    help = '''\
2601
2602        optional arguments:
2603          -h, --help  show this help message and exit
2604        '''
2605
2606
2607class TestMutuallyExclusiveOptionalAndPositional(MEMixin, TestCase):
2608
2609    def get_parser(self, required):
2610        parser = ErrorRaisingArgumentParser(prog='PROG')
2611        group = parser.add_mutually_exclusive_group(required=required)
2612        group.add_argument('--foo', action='store_true', help='FOO')
2613        group.add_argument('--spam', help='SPAM')
2614        group.add_argument('badger', nargs='*', default='X', help='BADGER')
2615        return parser
2616
2617    failures = [
2618        '--foo --spam S',
2619        '--spam S X',
2620        'X --foo',
2621        'X Y Z --spam S',
2622        '--foo X Y',
2623    ]
2624    successes = [
2625        ('--foo', NS(foo=True, spam=None, badger='X')),
2626        ('--spam S', NS(foo=False, spam='S', badger='X')),
2627        ('X', NS(foo=False, spam=None, badger=['X'])),
2628        ('X Y Z', NS(foo=False, spam=None, badger=['X', 'Y', 'Z'])),
2629    ]
2630    successes_when_not_required = [
2631        ('', NS(foo=False, spam=None, badger='X')),
2632    ]
2633
2634    usage_when_not_required = '''\
2635        usage: PROG [-h] [--foo | --spam SPAM | badger [badger ...]]
2636        '''
2637    usage_when_required = '''\
2638        usage: PROG [-h] (--foo | --spam SPAM | badger [badger ...])
2639        '''
2640    help = '''\
2641
2642        positional arguments:
2643          badger       BADGER
2644
2645        optional arguments:
2646          -h, --help   show this help message and exit
2647          --foo        FOO
2648          --spam SPAM  SPAM
2649        '''
2650
2651
2652class TestMutuallyExclusiveOptionalsMixed(MEMixin, TestCase):
2653
2654    def get_parser(self, required):
2655        parser = ErrorRaisingArgumentParser(prog='PROG')
2656        parser.add_argument('-x', action='store_true', help='x help')
2657        group = parser.add_mutually_exclusive_group(required=required)
2658        group.add_argument('-a', action='store_true', help='a help')
2659        group.add_argument('-b', action='store_true', help='b help')
2660        parser.add_argument('-y', action='store_true', help='y help')
2661        group.add_argument('-c', action='store_true', help='c help')
2662        return parser
2663
2664    failures = ['-a -b', '-b -c', '-a -c', '-a -b -c']
2665    successes = [
2666        ('-a', NS(a=True, b=False, c=False, x=False, y=False)),
2667        ('-b', NS(a=False, b=True, c=False, x=False, y=False)),
2668        ('-c', NS(a=False, b=False, c=True, x=False, y=False)),
2669        ('-a -x', NS(a=True, b=False, c=False, x=True, y=False)),
2670        ('-y -b', NS(a=False, b=True, c=False, x=False, y=True)),
2671        ('-x -y -c', NS(a=False, b=False, c=True, x=True, y=True)),
2672    ]
2673    successes_when_not_required = [
2674        ('', NS(a=False, b=False, c=False, x=False, y=False)),
2675        ('-x', NS(a=False, b=False, c=False, x=True, y=False)),
2676        ('-y', NS(a=False, b=False, c=False, x=False, y=True)),
2677    ]
2678
2679    usage_when_required = usage_when_not_required = '''\
2680        usage: PROG [-h] [-x] [-a] [-b] [-y] [-c]
2681        '''
2682    help = '''\
2683
2684        optional arguments:
2685          -h, --help  show this help message and exit
2686          -x          x help
2687          -a          a help
2688          -b          b help
2689          -y          y help
2690          -c          c help
2691        '''
2692
2693
2694class TestMutuallyExclusiveInGroup(MEMixin, TestCase):
2695
2696    def get_parser(self, required=None):
2697        parser = ErrorRaisingArgumentParser(prog='PROG')
2698        titled_group = parser.add_argument_group(
2699            title='Titled group', description='Group description')
2700        mutex_group = \
2701            titled_group.add_mutually_exclusive_group(required=required)
2702        mutex_group.add_argument('--bar', help='bar help')
2703        mutex_group.add_argument('--baz', help='baz help')
2704        return parser
2705
2706    failures = ['--bar X --baz Y', '--baz X --bar Y']
2707    successes = [
2708        ('--bar X', NS(bar='X', baz=None)),
2709        ('--baz Y', NS(bar=None, baz='Y')),
2710    ]
2711    successes_when_not_required = [
2712        ('', NS(bar=None, baz=None)),
2713    ]
2714
2715    usage_when_not_required = '''\
2716        usage: PROG [-h] [--bar BAR | --baz BAZ]
2717        '''
2718    usage_when_required = '''\
2719        usage: PROG [-h] (--bar BAR | --baz BAZ)
2720        '''
2721    help = '''\
2722
2723        optional arguments:
2724          -h, --help  show this help message and exit
2725
2726        Titled group:
2727          Group description
2728
2729          --bar BAR   bar help
2730          --baz BAZ   baz help
2731        '''
2732
2733
2734class TestMutuallyExclusiveOptionalsAndPositionalsMixed(MEMixin, TestCase):
2735
2736    def get_parser(self, required):
2737        parser = ErrorRaisingArgumentParser(prog='PROG')
2738        parser.add_argument('x', help='x help')
2739        parser.add_argument('-y', action='store_true', help='y help')
2740        group = parser.add_mutually_exclusive_group(required=required)
2741        group.add_argument('a', nargs='?', help='a help')
2742        group.add_argument('-b', action='store_true', help='b help')
2743        group.add_argument('-c', action='store_true', help='c help')
2744        return parser
2745
2746    failures = ['X A -b', '-b -c', '-c X A']
2747    successes = [
2748        ('X A', NS(a='A', b=False, c=False, x='X', y=False)),
2749        ('X -b', NS(a=None, b=True, c=False, x='X', y=False)),
2750        ('X -c', NS(a=None, b=False, c=True, x='X', y=False)),
2751        ('X A -y', NS(a='A', b=False, c=False, x='X', y=True)),
2752        ('X -y -b', NS(a=None, b=True, c=False, x='X', y=True)),
2753    ]
2754    successes_when_not_required = [
2755        ('X', NS(a=None, b=False, c=False, x='X', y=False)),
2756        ('X -y', NS(a=None, b=False, c=False, x='X', y=True)),
2757    ]
2758
2759    usage_when_required = usage_when_not_required = '''\
2760        usage: PROG [-h] [-y] [-b] [-c] x [a]
2761        '''
2762    help = '''\
2763
2764        positional arguments:
2765          x           x help
2766          a           a help
2767
2768        optional arguments:
2769          -h, --help  show this help message and exit
2770          -y          y help
2771          -b          b help
2772          -c          c help
2773        '''
2774
2775class TestMutuallyExclusiveNested(MEMixin, TestCase):
2776
2777    def get_parser(self, required):
2778        parser = ErrorRaisingArgumentParser(prog='PROG')
2779        group = parser.add_mutually_exclusive_group(required=required)
2780        group.add_argument('-a')
2781        group.add_argument('-b')
2782        group2 = group.add_mutually_exclusive_group(required=required)
2783        group2.add_argument('-c')
2784        group2.add_argument('-d')
2785        group3 = group2.add_mutually_exclusive_group(required=required)
2786        group3.add_argument('-e')
2787        group3.add_argument('-f')
2788        return parser
2789
2790    usage_when_not_required = '''\
2791        usage: PROG [-h] [-a A | -b B | [-c C | -d D | [-e E | -f F]]]
2792        '''
2793    usage_when_required = '''\
2794        usage: PROG [-h] (-a A | -b B | (-c C | -d D | (-e E | -f F)))
2795        '''
2796
2797    help = '''\
2798
2799        optional arguments:
2800          -h, --help  show this help message and exit
2801          -a A
2802          -b B
2803          -c C
2804          -d D
2805          -e E
2806          -f F
2807        '''
2808
2809    # We are only interested in testing the behavior of format_usage().
2810    test_failures_when_not_required = None
2811    test_failures_when_required = None
2812    test_successes_when_not_required = None
2813    test_successes_when_required = None
2814
2815# =================================================
2816# Mutually exclusive group in parent parser tests
2817# =================================================
2818
2819class MEPBase(object):
2820
2821    def get_parser(self, required=None):
2822        parent = super(MEPBase, self).get_parser(required=required)
2823        parser = ErrorRaisingArgumentParser(
2824            prog=parent.prog, add_help=False, parents=[parent])
2825        return parser
2826
2827
2828class TestMutuallyExclusiveGroupErrorsParent(
2829    MEPBase, TestMutuallyExclusiveGroupErrors):
2830    pass
2831
2832
2833class TestMutuallyExclusiveSimpleParent(
2834    MEPBase, TestMutuallyExclusiveSimple):
2835    pass
2836
2837
2838class TestMutuallyExclusiveLongParent(
2839    MEPBase, TestMutuallyExclusiveLong):
2840    pass
2841
2842
2843class TestMutuallyExclusiveFirstSuppressedParent(
2844    MEPBase, TestMutuallyExclusiveFirstSuppressed):
2845    pass
2846
2847
2848class TestMutuallyExclusiveManySuppressedParent(
2849    MEPBase, TestMutuallyExclusiveManySuppressed):
2850    pass
2851
2852
2853class TestMutuallyExclusiveOptionalAndPositionalParent(
2854    MEPBase, TestMutuallyExclusiveOptionalAndPositional):
2855    pass
2856
2857
2858class TestMutuallyExclusiveOptionalsMixedParent(
2859    MEPBase, TestMutuallyExclusiveOptionalsMixed):
2860    pass
2861
2862
2863class TestMutuallyExclusiveOptionalsAndPositionalsMixedParent(
2864    MEPBase, TestMutuallyExclusiveOptionalsAndPositionalsMixed):
2865    pass
2866
2867# =================
2868# Set default tests
2869# =================
2870
2871class TestSetDefaults(TestCase):
2872
2873    def test_set_defaults_no_args(self):
2874        parser = ErrorRaisingArgumentParser()
2875        parser.set_defaults(x='foo')
2876        parser.set_defaults(y='bar', z=1)
2877        self.assertEqual(NS(x='foo', y='bar', z=1),
2878                         parser.parse_args([]))
2879        self.assertEqual(NS(x='foo', y='bar', z=1),
2880                         parser.parse_args([], NS()))
2881        self.assertEqual(NS(x='baz', y='bar', z=1),
2882                         parser.parse_args([], NS(x='baz')))
2883        self.assertEqual(NS(x='baz', y='bar', z=2),
2884                         parser.parse_args([], NS(x='baz', z=2)))
2885
2886    def test_set_defaults_with_args(self):
2887        parser = ErrorRaisingArgumentParser()
2888        parser.set_defaults(x='foo', y='bar')
2889        parser.add_argument('-x', default='xfoox')
2890        self.assertEqual(NS(x='xfoox', y='bar'),
2891                         parser.parse_args([]))
2892        self.assertEqual(NS(x='xfoox', y='bar'),
2893                         parser.parse_args([], NS()))
2894        self.assertEqual(NS(x='baz', y='bar'),
2895                         parser.parse_args([], NS(x='baz')))
2896        self.assertEqual(NS(x='1', y='bar'),
2897                         parser.parse_args('-x 1'.split()))
2898        self.assertEqual(NS(x='1', y='bar'),
2899                         parser.parse_args('-x 1'.split(), NS()))
2900        self.assertEqual(NS(x='1', y='bar'),
2901                         parser.parse_args('-x 1'.split(), NS(x='baz')))
2902
2903    def test_set_defaults_subparsers(self):
2904        parser = ErrorRaisingArgumentParser()
2905        parser.set_defaults(x='foo')
2906        subparsers = parser.add_subparsers()
2907        parser_a = subparsers.add_parser('a')
2908        parser_a.set_defaults(y='bar')
2909        self.assertEqual(NS(x='foo', y='bar'),
2910                         parser.parse_args('a'.split()))
2911
2912    def test_set_defaults_parents(self):
2913        parent = ErrorRaisingArgumentParser(add_help=False)
2914        parent.set_defaults(x='foo')
2915        parser = ErrorRaisingArgumentParser(parents=[parent])
2916        self.assertEqual(NS(x='foo'), parser.parse_args([]))
2917
2918    def test_set_defaults_on_parent_and_subparser(self):
2919        parser = argparse.ArgumentParser()
2920        xparser = parser.add_subparsers().add_parser('X')
2921        parser.set_defaults(foo=1)
2922        xparser.set_defaults(foo=2)
2923        self.assertEqual(NS(foo=2), parser.parse_args(['X']))
2924
2925    def test_set_defaults_same_as_add_argument(self):
2926        parser = ErrorRaisingArgumentParser()
2927        parser.set_defaults(w='W', x='X', y='Y', z='Z')
2928        parser.add_argument('-w')
2929        parser.add_argument('-x', default='XX')
2930        parser.add_argument('y', nargs='?')
2931        parser.add_argument('z', nargs='?', default='ZZ')
2932
2933        # defaults set previously
2934        self.assertEqual(NS(w='W', x='XX', y='Y', z='ZZ'),
2935                         parser.parse_args([]))
2936
2937        # reset defaults
2938        parser.set_defaults(w='WW', x='X', y='YY', z='Z')
2939        self.assertEqual(NS(w='WW', x='X', y='YY', z='Z'),
2940                         parser.parse_args([]))
2941
2942    def test_set_defaults_same_as_add_argument_group(self):
2943        parser = ErrorRaisingArgumentParser()
2944        parser.set_defaults(w='W', x='X', y='Y', z='Z')
2945        group = parser.add_argument_group('foo')
2946        group.add_argument('-w')
2947        group.add_argument('-x', default='XX')
2948        group.add_argument('y', nargs='?')
2949        group.add_argument('z', nargs='?', default='ZZ')
2950
2951
2952        # defaults set previously
2953        self.assertEqual(NS(w='W', x='XX', y='Y', z='ZZ'),
2954                         parser.parse_args([]))
2955
2956        # reset defaults
2957        parser.set_defaults(w='WW', x='X', y='YY', z='Z')
2958        self.assertEqual(NS(w='WW', x='X', y='YY', z='Z'),
2959                         parser.parse_args([]))
2960
2961# =================
2962# Get default tests
2963# =================
2964
2965class TestGetDefault(TestCase):
2966
2967    def test_get_default(self):
2968        parser = ErrorRaisingArgumentParser()
2969        self.assertIsNone(parser.get_default("foo"))
2970        self.assertIsNone(parser.get_default("bar"))
2971
2972        parser.add_argument("--foo")
2973        self.assertIsNone(parser.get_default("foo"))
2974        self.assertIsNone(parser.get_default("bar"))
2975
2976        parser.add_argument("--bar", type=int, default=42)
2977        self.assertIsNone(parser.get_default("foo"))
2978        self.assertEqual(42, parser.get_default("bar"))
2979
2980        parser.set_defaults(foo="badger")
2981        self.assertEqual("badger", parser.get_default("foo"))
2982        self.assertEqual(42, parser.get_default("bar"))
2983
2984# ==========================
2985# Namespace 'contains' tests
2986# ==========================
2987
2988class TestNamespaceContainsSimple(TestCase):
2989
2990    def test_empty(self):
2991        ns = argparse.Namespace()
2992        self.assertNotIn('', ns)
2993        self.assertNotIn('x', ns)
2994
2995    def test_non_empty(self):
2996        ns = argparse.Namespace(x=1, y=2)
2997        self.assertNotIn('', ns)
2998        self.assertIn('x', ns)
2999        self.assertIn('y', ns)
3000        self.assertNotIn('xx', ns)
3001        self.assertNotIn('z', ns)
3002
3003# =====================
3004# Help formatting tests
3005# =====================
3006
3007class TestHelpFormattingMetaclass(type):
3008
3009    def __init__(cls, name, bases, bodydict):
3010        if name == 'HelpTestCase':
3011            return
3012
3013        class AddTests(object):
3014
3015            def __init__(self, test_class, func_suffix, std_name):
3016                self.func_suffix = func_suffix
3017                self.std_name = std_name
3018
3019                for test_func in [self.test_format,
3020                                  self.test_print,
3021                                  self.test_print_file]:
3022                    test_name = '%s_%s' % (test_func.__name__, func_suffix)
3023
3024                    def test_wrapper(self, test_func=test_func):
3025                        test_func(self)
3026                    try:
3027                        test_wrapper.__name__ = test_name
3028                    except TypeError:
3029                        pass
3030                    setattr(test_class, test_name, test_wrapper)
3031
3032            def _get_parser(self, tester):
3033                parser = argparse.ArgumentParser(
3034                    *tester.parser_signature.args,
3035                    **tester.parser_signature.kwargs)
3036                for argument_sig in getattr(tester, 'argument_signatures', []):
3037                    parser.add_argument(*argument_sig.args,
3038                                        **argument_sig.kwargs)
3039                group_sigs = getattr(tester, 'argument_group_signatures', [])
3040                for group_sig, argument_sigs in group_sigs:
3041                    group = parser.add_argument_group(*group_sig.args,
3042                                                      **group_sig.kwargs)
3043                    for argument_sig in argument_sigs:
3044                        group.add_argument(*argument_sig.args,
3045                                           **argument_sig.kwargs)
3046                subparsers_sigs = getattr(tester, 'subparsers_signatures', [])
3047                if subparsers_sigs:
3048                    subparsers = parser.add_subparsers()
3049                    for subparser_sig in subparsers_sigs:
3050                        subparsers.add_parser(*subparser_sig.args,
3051                                               **subparser_sig.kwargs)
3052                return parser
3053
3054            def _test(self, tester, parser_text):
3055                expected_text = getattr(tester, self.func_suffix)
3056                expected_text = textwrap.dedent(expected_text)
3057                tester.assertEqual(expected_text, parser_text)
3058
3059            def test_format(self, tester):
3060                parser = self._get_parser(tester)
3061                format = getattr(parser, 'format_%s' % self.func_suffix)
3062                self._test(tester, format())
3063
3064            def test_print(self, tester):
3065                parser = self._get_parser(tester)
3066                print_ = getattr(parser, 'print_%s' % self.func_suffix)
3067                old_stream = getattr(sys, self.std_name)
3068                setattr(sys, self.std_name, StdIOBuffer())
3069                try:
3070                    print_()
3071                    parser_text = getattr(sys, self.std_name).getvalue()
3072                finally:
3073                    setattr(sys, self.std_name, old_stream)
3074                self._test(tester, parser_text)
3075
3076            def test_print_file(self, tester):
3077                parser = self._get_parser(tester)
3078                print_ = getattr(parser, 'print_%s' % self.func_suffix)
3079                sfile = StdIOBuffer()
3080                print_(sfile)
3081                parser_text = sfile.getvalue()
3082                self._test(tester, parser_text)
3083
3084        # add tests for {format,print}_{usage,help}
3085        for func_suffix, std_name in [('usage', 'stdout'),
3086                                      ('help', 'stdout')]:
3087            AddTests(cls, func_suffix, std_name)
3088
3089bases = TestCase,
3090HelpTestCase = TestHelpFormattingMetaclass('HelpTestCase', bases, {})
3091
3092
3093class TestHelpBiggerOptionals(HelpTestCase):
3094    """Make sure that argument help aligns when options are longer"""
3095
3096    parser_signature = Sig(prog='PROG', description='DESCRIPTION',
3097                           epilog='EPILOG')
3098    argument_signatures = [
3099        Sig('-v', '--version', action='version', version='0.1'),
3100        Sig('-x', action='store_true', help='X HELP'),
3101        Sig('--y', help='Y HELP'),
3102        Sig('foo', help='FOO HELP'),
3103        Sig('bar', help='BAR HELP'),
3104    ]
3105    argument_group_signatures = []
3106    usage = '''\
3107        usage: PROG [-h] [-v] [-x] [--y Y] foo bar
3108        '''
3109    help = usage + '''\
3110
3111        DESCRIPTION
3112
3113        positional arguments:
3114          foo            FOO HELP
3115          bar            BAR HELP
3116
3117        optional arguments:
3118          -h, --help     show this help message and exit
3119          -v, --version  show program's version number and exit
3120          -x             X HELP
3121          --y Y          Y HELP
3122
3123        EPILOG
3124    '''
3125    version = '''\
3126        0.1
3127        '''
3128
3129class TestShortColumns(HelpTestCase):
3130    '''Test extremely small number of columns.
3131
3132    TestCase prevents "COLUMNS" from being too small in the tests themselves,
3133    but we don't want any exceptions thrown in such cases. Only ugly representation.
3134    '''
3135    def setUp(self):
3136        env = support.EnvironmentVarGuard()
3137        env.set("COLUMNS", '15')
3138        self.addCleanup(env.__exit__)
3139
3140    parser_signature            = TestHelpBiggerOptionals.parser_signature
3141    argument_signatures         = TestHelpBiggerOptionals.argument_signatures
3142    argument_group_signatures   = TestHelpBiggerOptionals.argument_group_signatures
3143    usage = '''\
3144        usage: PROG
3145               [-h]
3146               [-v]
3147               [-x]
3148               [--y Y]
3149               foo
3150               bar
3151        '''
3152    help = usage + '''\
3153
3154        DESCRIPTION
3155
3156        positional arguments:
3157          foo
3158            FOO HELP
3159          bar
3160            BAR HELP
3161
3162        optional arguments:
3163          -h, --help
3164            show this
3165            help
3166            message and
3167            exit
3168          -v, --version
3169            show
3170            program's
3171            version
3172            number and
3173            exit
3174          -x
3175            X HELP
3176          --y Y
3177            Y HELP
3178
3179        EPILOG
3180    '''
3181    version                     = TestHelpBiggerOptionals.version
3182
3183
3184class TestHelpBiggerOptionalGroups(HelpTestCase):
3185    """Make sure that argument help aligns when options are longer"""
3186
3187    parser_signature = Sig(prog='PROG', description='DESCRIPTION',
3188                           epilog='EPILOG')
3189    argument_signatures = [
3190        Sig('-v', '--version', action='version', version='0.1'),
3191        Sig('-x', action='store_true', help='X HELP'),
3192        Sig('--y', help='Y HELP'),
3193        Sig('foo', help='FOO HELP'),
3194        Sig('bar', help='BAR HELP'),
3195    ]
3196    argument_group_signatures = [
3197        (Sig('GROUP TITLE', description='GROUP DESCRIPTION'), [
3198            Sig('baz', help='BAZ HELP'),
3199            Sig('-z', nargs='+', help='Z HELP')]),
3200    ]
3201    usage = '''\
3202        usage: PROG [-h] [-v] [-x] [--y Y] [-z Z [Z ...]] foo bar baz
3203        '''
3204    help = usage + '''\
3205
3206        DESCRIPTION
3207
3208        positional arguments:
3209          foo            FOO HELP
3210          bar            BAR HELP
3211
3212        optional arguments:
3213          -h, --help     show this help message and exit
3214          -v, --version  show program's version number and exit
3215          -x             X HELP
3216          --y Y          Y HELP
3217
3218        GROUP TITLE:
3219          GROUP DESCRIPTION
3220
3221          baz            BAZ HELP
3222          -z Z [Z ...]   Z HELP
3223
3224        EPILOG
3225    '''
3226    version = '''\
3227        0.1
3228        '''
3229
3230
3231class TestHelpBiggerPositionals(HelpTestCase):
3232    """Make sure that help aligns when arguments are longer"""
3233
3234    parser_signature = Sig(usage='USAGE', description='DESCRIPTION')
3235    argument_signatures = [
3236        Sig('-x', action='store_true', help='X HELP'),
3237        Sig('--y', help='Y HELP'),
3238        Sig('ekiekiekifekang', help='EKI HELP'),
3239        Sig('bar', help='BAR HELP'),
3240    ]
3241    argument_group_signatures = []
3242    usage = '''\
3243        usage: USAGE
3244        '''
3245    help = usage + '''\
3246
3247        DESCRIPTION
3248
3249        positional arguments:
3250          ekiekiekifekang  EKI HELP
3251          bar              BAR HELP
3252
3253        optional arguments:
3254          -h, --help       show this help message and exit
3255          -x               X HELP
3256          --y Y            Y HELP
3257        '''
3258
3259    version = ''
3260
3261
3262class TestHelpReformatting(HelpTestCase):
3263    """Make sure that text after short names starts on the first line"""
3264
3265    parser_signature = Sig(
3266        prog='PROG',
3267        description='   oddly    formatted\n'
3268                    'description\n'
3269                    '\n'
3270                    'that is so long that it should go onto multiple '
3271                    'lines when wrapped')
3272    argument_signatures = [
3273        Sig('-x', metavar='XX', help='oddly\n'
3274                                     '    formatted -x help'),
3275        Sig('y', metavar='yyy', help='normal y help'),
3276    ]
3277    argument_group_signatures = [
3278        (Sig('title', description='\n'
3279                                  '    oddly formatted group\n'
3280                                  '\n'
3281                                  'description'),
3282         [Sig('-a', action='store_true',
3283              help=' oddly \n'
3284                   'formatted    -a  help  \n'
3285                   '    again, so long that it should be wrapped over '
3286                   'multiple lines')]),
3287    ]
3288    usage = '''\
3289        usage: PROG [-h] [-x XX] [-a] yyy
3290        '''
3291    help = usage + '''\
3292
3293        oddly formatted description that is so long that it should go onto \
3294multiple
3295        lines when wrapped
3296
3297        positional arguments:
3298          yyy         normal y help
3299
3300        optional arguments:
3301          -h, --help  show this help message and exit
3302          -x XX       oddly formatted -x help
3303
3304        title:
3305          oddly formatted group description
3306
3307          -a          oddly formatted -a help again, so long that it should \
3308be wrapped
3309                      over multiple lines
3310        '''
3311    version = ''
3312
3313
3314class TestHelpWrappingShortNames(HelpTestCase):
3315    """Make sure that text after short names starts on the first line"""
3316
3317    parser_signature = Sig(prog='PROG', description= 'D\nD' * 30)
3318    argument_signatures = [
3319        Sig('-x', metavar='XX', help='XHH HX' * 20),
3320        Sig('y', metavar='yyy', help='YH YH' * 20),
3321    ]
3322    argument_group_signatures = [
3323        (Sig('ALPHAS'), [
3324            Sig('-a', action='store_true', help='AHHH HHA' * 10)]),
3325    ]
3326    usage = '''\
3327        usage: PROG [-h] [-x XX] [-a] yyy
3328        '''
3329    help = usage + '''\
3330
3331        D DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD \
3332DD DD DD
3333        DD DD DD DD D
3334
3335        positional arguments:
3336          yyy         YH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH \
3337YHYH YHYH
3338                      YHYH YHYH YHYH YHYH YHYH YHYH YHYH YH
3339
3340        optional arguments:
3341          -h, --help  show this help message and exit
3342          -x XX       XHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH \
3343HXXHH HXXHH
3344                      HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HX
3345
3346        ALPHAS:
3347          -a          AHHH HHAAHHH HHAAHHH HHAAHHH HHAAHHH HHAAHHH HHAAHHH \
3348HHAAHHH
3349                      HHAAHHH HHAAHHH HHA
3350        '''
3351    version = ''
3352
3353
3354class TestHelpWrappingLongNames(HelpTestCase):
3355    """Make sure that text after long names starts on the next line"""
3356
3357    parser_signature = Sig(usage='USAGE', description= 'D D' * 30)
3358    argument_signatures = [
3359        Sig('-v', '--version', action='version', version='V V' * 30),
3360        Sig('-x', metavar='X' * 25, help='XH XH' * 20),
3361        Sig('y', metavar='y' * 25, help='YH YH' * 20),
3362    ]
3363    argument_group_signatures = [
3364        (Sig('ALPHAS'), [
3365            Sig('-a', metavar='A' * 25, help='AH AH' * 20),
3366            Sig('z', metavar='z' * 25, help='ZH ZH' * 20)]),
3367    ]
3368    usage = '''\
3369        usage: USAGE
3370        '''
3371    help = usage + '''\
3372
3373        D DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD \
3374DD DD DD
3375        DD DD DD DD D
3376
3377        positional arguments:
3378          yyyyyyyyyyyyyyyyyyyyyyyyy
3379                                YH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH \
3380YHYH YHYH
3381                                YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YH
3382
3383        optional arguments:
3384          -h, --help            show this help message and exit
3385          -v, --version         show program's version number and exit
3386          -x XXXXXXXXXXXXXXXXXXXXXXXXX
3387                                XH XHXH XHXH XHXH XHXH XHXH XHXH XHXH XHXH \
3388XHXH XHXH
3389                                XHXH XHXH XHXH XHXH XHXH XHXH XHXH XHXH XHXH XH
3390
3391        ALPHAS:
3392          -a AAAAAAAAAAAAAAAAAAAAAAAAA
3393                                AH AHAH AHAH AHAH AHAH AHAH AHAH AHAH AHAH \
3394AHAH AHAH
3395                                AHAH AHAH AHAH AHAH AHAH AHAH AHAH AHAH AHAH AH
3396          zzzzzzzzzzzzzzzzzzzzzzzzz
3397                                ZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH \
3398ZHZH ZHZH
3399                                ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZH
3400        '''
3401    version = '''\
3402        V VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV \
3403VV VV VV
3404        VV VV VV VV V
3405        '''
3406
3407
3408class TestHelpUsage(HelpTestCase):
3409    """Test basic usage messages"""
3410
3411    parser_signature = Sig(prog='PROG')
3412    argument_signatures = [
3413        Sig('-w', nargs='+', help='w'),
3414        Sig('-x', nargs='*', help='x'),
3415        Sig('a', help='a'),
3416        Sig('b', help='b', nargs=2),
3417        Sig('c', help='c', nargs='?'),
3418    ]
3419    argument_group_signatures = [
3420        (Sig('group'), [
3421            Sig('-y', nargs='?', help='y'),
3422            Sig('-z', nargs=3, help='z'),
3423            Sig('d', help='d', nargs='*'),
3424            Sig('e', help='e', nargs='+'),
3425        ])
3426    ]
3427    usage = '''\
3428        usage: PROG [-h] [-w W [W ...]] [-x [X [X ...]]] [-y [Y]] [-z Z Z Z]
3429                    a b b [c] [d [d ...]] e [e ...]
3430        '''
3431    help = usage + '''\
3432
3433        positional arguments:
3434          a               a
3435          b               b
3436          c               c
3437
3438        optional arguments:
3439          -h, --help      show this help message and exit
3440          -w W [W ...]    w
3441          -x [X [X ...]]  x
3442
3443        group:
3444          -y [Y]          y
3445          -z Z Z Z        z
3446          d               d
3447          e               e
3448        '''
3449    version = ''
3450
3451
3452class TestHelpOnlyUserGroups(HelpTestCase):
3453    """Test basic usage messages"""
3454
3455    parser_signature = Sig(prog='PROG', add_help=False)
3456    argument_signatures = []
3457    argument_group_signatures = [
3458        (Sig('xxxx'), [
3459            Sig('-x', help='x'),
3460            Sig('a', help='a'),
3461        ]),
3462        (Sig('yyyy'), [
3463            Sig('b', help='b'),
3464            Sig('-y', help='y'),
3465        ]),
3466    ]
3467    usage = '''\
3468        usage: PROG [-x X] [-y Y] a b
3469        '''
3470    help = usage + '''\
3471
3472        xxxx:
3473          -x X  x
3474          a     a
3475
3476        yyyy:
3477          b     b
3478          -y Y  y
3479        '''
3480    version = ''
3481
3482
3483class TestHelpUsageLongProg(HelpTestCase):
3484    """Test usage messages where the prog is long"""
3485
3486    parser_signature = Sig(prog='P' * 60)
3487    argument_signatures = [
3488        Sig('-w', metavar='W'),
3489        Sig('-x', metavar='X'),
3490        Sig('a'),
3491        Sig('b'),
3492    ]
3493    argument_group_signatures = []
3494    usage = '''\
3495        usage: PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
3496               [-h] [-w W] [-x X] a b
3497        '''
3498    help = usage + '''\
3499
3500        positional arguments:
3501          a
3502          b
3503
3504        optional arguments:
3505          -h, --help  show this help message and exit
3506          -w W
3507          -x X
3508        '''
3509    version = ''
3510
3511
3512class TestHelpUsageLongProgOptionsWrap(HelpTestCase):
3513    """Test usage messages where the prog is long and the optionals wrap"""
3514
3515    parser_signature = Sig(prog='P' * 60)
3516    argument_signatures = [
3517        Sig('-w', metavar='W' * 25),
3518        Sig('-x', metavar='X' * 25),
3519        Sig('-y', metavar='Y' * 25),
3520        Sig('-z', metavar='Z' * 25),
3521        Sig('a'),
3522        Sig('b'),
3523    ]
3524    argument_group_signatures = []
3525    usage = '''\
3526        usage: PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
3527               [-h] [-w WWWWWWWWWWWWWWWWWWWWWWWWW] \
3528[-x XXXXXXXXXXXXXXXXXXXXXXXXX]
3529               [-y YYYYYYYYYYYYYYYYYYYYYYYYY] [-z ZZZZZZZZZZZZZZZZZZZZZZZZZ]
3530               a b
3531        '''
3532    help = usage + '''\
3533
3534        positional arguments:
3535          a
3536          b
3537
3538        optional arguments:
3539          -h, --help            show this help message and exit
3540          -w WWWWWWWWWWWWWWWWWWWWWWWWW
3541          -x XXXXXXXXXXXXXXXXXXXXXXXXX
3542          -y YYYYYYYYYYYYYYYYYYYYYYYYY
3543          -z ZZZZZZZZZZZZZZZZZZZZZZZZZ
3544        '''
3545    version = ''
3546
3547
3548class TestHelpUsageLongProgPositionalsWrap(HelpTestCase):
3549    """Test usage messages where the prog is long and the positionals wrap"""
3550
3551    parser_signature = Sig(prog='P' * 60, add_help=False)
3552    argument_signatures = [
3553        Sig('a' * 25),
3554        Sig('b' * 25),
3555        Sig('c' * 25),
3556    ]
3557    argument_group_signatures = []
3558    usage = '''\
3559        usage: PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
3560               aaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbb
3561               ccccccccccccccccccccccccc
3562        '''
3563    help = usage + '''\
3564
3565        positional arguments:
3566          aaaaaaaaaaaaaaaaaaaaaaaaa
3567          bbbbbbbbbbbbbbbbbbbbbbbbb
3568          ccccccccccccccccccccccccc
3569        '''
3570    version = ''
3571
3572
3573class TestHelpUsageOptionalsWrap(HelpTestCase):
3574    """Test usage messages where the optionals wrap"""
3575
3576    parser_signature = Sig(prog='PROG')
3577    argument_signatures = [
3578        Sig('-w', metavar='W' * 25),
3579        Sig('-x', metavar='X' * 25),
3580        Sig('-y', metavar='Y' * 25),
3581        Sig('-z', metavar='Z' * 25),
3582        Sig('a'),
3583        Sig('b'),
3584        Sig('c'),
3585    ]
3586    argument_group_signatures = []
3587    usage = '''\
3588        usage: PROG [-h] [-w WWWWWWWWWWWWWWWWWWWWWWWWW] \
3589[-x XXXXXXXXXXXXXXXXXXXXXXXXX]
3590                    [-y YYYYYYYYYYYYYYYYYYYYYYYYY] \
3591[-z ZZZZZZZZZZZZZZZZZZZZZZZZZ]
3592                    a b c
3593        '''
3594    help = usage + '''\
3595
3596        positional arguments:
3597          a
3598          b
3599          c
3600
3601        optional arguments:
3602          -h, --help            show this help message and exit
3603          -w WWWWWWWWWWWWWWWWWWWWWWWWW
3604          -x XXXXXXXXXXXXXXXXXXXXXXXXX
3605          -y YYYYYYYYYYYYYYYYYYYYYYYYY
3606          -z ZZZZZZZZZZZZZZZZZZZZZZZZZ
3607        '''
3608    version = ''
3609
3610
3611class TestHelpUsagePositionalsWrap(HelpTestCase):
3612    """Test usage messages where the positionals wrap"""
3613
3614    parser_signature = Sig(prog='PROG')
3615    argument_signatures = [
3616        Sig('-x'),
3617        Sig('-y'),
3618        Sig('-z'),
3619        Sig('a' * 25),
3620        Sig('b' * 25),
3621        Sig('c' * 25),
3622    ]
3623    argument_group_signatures = []
3624    usage = '''\
3625        usage: PROG [-h] [-x X] [-y Y] [-z Z]
3626                    aaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbb
3627                    ccccccccccccccccccccccccc
3628        '''
3629    help = usage + '''\
3630
3631        positional arguments:
3632          aaaaaaaaaaaaaaaaaaaaaaaaa
3633          bbbbbbbbbbbbbbbbbbbbbbbbb
3634          ccccccccccccccccccccccccc
3635
3636        optional arguments:
3637          -h, --help            show this help message and exit
3638          -x X
3639          -y Y
3640          -z Z
3641        '''
3642    version = ''
3643
3644
3645class TestHelpUsageOptionalsPositionalsWrap(HelpTestCase):
3646    """Test usage messages where the optionals and positionals wrap"""
3647
3648    parser_signature = Sig(prog='PROG')
3649    argument_signatures = [
3650        Sig('-x', metavar='X' * 25),
3651        Sig('-y', metavar='Y' * 25),
3652        Sig('-z', metavar='Z' * 25),
3653        Sig('a' * 25),
3654        Sig('b' * 25),
3655        Sig('c' * 25),
3656    ]
3657    argument_group_signatures = []
3658    usage = '''\
3659        usage: PROG [-h] [-x XXXXXXXXXXXXXXXXXXXXXXXXX] \
3660[-y YYYYYYYYYYYYYYYYYYYYYYYYY]
3661                    [-z ZZZZZZZZZZZZZZZZZZZZZZZZZ]
3662                    aaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbb
3663                    ccccccccccccccccccccccccc
3664        '''
3665    help = usage + '''\
3666
3667        positional arguments:
3668          aaaaaaaaaaaaaaaaaaaaaaaaa
3669          bbbbbbbbbbbbbbbbbbbbbbbbb
3670          ccccccccccccccccccccccccc
3671
3672        optional arguments:
3673          -h, --help            show this help message and exit
3674          -x XXXXXXXXXXXXXXXXXXXXXXXXX
3675          -y YYYYYYYYYYYYYYYYYYYYYYYYY
3676          -z ZZZZZZZZZZZZZZZZZZZZZZZZZ
3677        '''
3678    version = ''
3679
3680
3681class TestHelpUsageOptionalsOnlyWrap(HelpTestCase):
3682    """Test usage messages where there are only optionals and they wrap"""
3683
3684    parser_signature = Sig(prog='PROG')
3685    argument_signatures = [
3686        Sig('-x', metavar='X' * 25),
3687        Sig('-y', metavar='Y' * 25),
3688        Sig('-z', metavar='Z' * 25),
3689    ]
3690    argument_group_signatures = []
3691    usage = '''\
3692        usage: PROG [-h] [-x XXXXXXXXXXXXXXXXXXXXXXXXX] \
3693[-y YYYYYYYYYYYYYYYYYYYYYYYYY]
3694                    [-z ZZZZZZZZZZZZZZZZZZZZZZZZZ]
3695        '''
3696    help = usage + '''\
3697
3698        optional arguments:
3699          -h, --help            show this help message and exit
3700          -x XXXXXXXXXXXXXXXXXXXXXXXXX
3701          -y YYYYYYYYYYYYYYYYYYYYYYYYY
3702          -z ZZZZZZZZZZZZZZZZZZZZZZZZZ
3703        '''
3704    version = ''
3705
3706
3707class TestHelpUsagePositionalsOnlyWrap(HelpTestCase):
3708    """Test usage messages where there are only positionals and they wrap"""
3709
3710    parser_signature = Sig(prog='PROG', add_help=False)
3711    argument_signatures = [
3712        Sig('a' * 25),
3713        Sig('b' * 25),
3714        Sig('c' * 25),
3715    ]
3716    argument_group_signatures = []
3717    usage = '''\
3718        usage: PROG aaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbb
3719                    ccccccccccccccccccccccccc
3720        '''
3721    help = usage + '''\
3722
3723        positional arguments:
3724          aaaaaaaaaaaaaaaaaaaaaaaaa
3725          bbbbbbbbbbbbbbbbbbbbbbbbb
3726          ccccccccccccccccccccccccc
3727        '''
3728    version = ''
3729
3730
3731class TestHelpVariableExpansion(HelpTestCase):
3732    """Test that variables are expanded properly in help messages"""
3733
3734    parser_signature = Sig(prog='PROG')
3735    argument_signatures = [
3736        Sig('-x', type=int,
3737            help='x %(prog)s %(default)s %(type)s %%'),
3738        Sig('-y', action='store_const', default=42, const='XXX',
3739            help='y %(prog)s %(default)s %(const)s'),
3740        Sig('--foo', choices='abc',
3741            help='foo %(prog)s %(default)s %(choices)s'),
3742        Sig('--bar', default='baz', choices=[1, 2], metavar='BBB',
3743            help='bar %(prog)s %(default)s %(dest)s'),
3744        Sig('spam', help='spam %(prog)s %(default)s'),
3745        Sig('badger', default=0.5, help='badger %(prog)s %(default)s'),
3746    ]
3747    argument_group_signatures = [
3748        (Sig('group'), [
3749            Sig('-a', help='a %(prog)s %(default)s'),
3750            Sig('-b', default=-1, help='b %(prog)s %(default)s'),
3751        ])
3752    ]
3753    usage = ('''\
3754        usage: PROG [-h] [-x X] [-y] [--foo {a,b,c}] [--bar BBB] [-a A] [-b B]
3755                    spam badger
3756        ''')
3757    help = usage + '''\
3758
3759        positional arguments:
3760          spam           spam PROG None
3761          badger         badger PROG 0.5
3762
3763        optional arguments:
3764          -h, --help     show this help message and exit
3765          -x X           x PROG None int %
3766          -y             y PROG 42 XXX
3767          --foo {a,b,c}  foo PROG None a, b, c
3768          --bar BBB      bar PROG baz bar
3769
3770        group:
3771          -a A           a PROG None
3772          -b B           b PROG -1
3773        '''
3774    version = ''
3775
3776
3777class TestHelpVariableExpansionUsageSupplied(HelpTestCase):
3778    """Test that variables are expanded properly when usage= is present"""
3779
3780    parser_signature = Sig(prog='PROG', usage='%(prog)s FOO')
3781    argument_signatures = []
3782    argument_group_signatures = []
3783    usage = ('''\
3784        usage: PROG FOO
3785        ''')
3786    help = usage + '''\
3787
3788        optional arguments:
3789          -h, --help  show this help message and exit
3790        '''
3791    version = ''
3792
3793
3794class TestHelpVariableExpansionNoArguments(HelpTestCase):
3795    """Test that variables are expanded properly with no arguments"""
3796
3797    parser_signature = Sig(prog='PROG', add_help=False)
3798    argument_signatures = []
3799    argument_group_signatures = []
3800    usage = ('''\
3801        usage: PROG
3802        ''')
3803    help = usage
3804    version = ''
3805
3806
3807class TestHelpSuppressUsage(HelpTestCase):
3808    """Test that items can be suppressed in usage messages"""
3809
3810    parser_signature = Sig(prog='PROG', usage=argparse.SUPPRESS)
3811    argument_signatures = [
3812        Sig('--foo', help='foo help'),
3813        Sig('spam', help='spam help'),
3814    ]
3815    argument_group_signatures = []
3816    help = '''\
3817        positional arguments:
3818          spam        spam help
3819
3820        optional arguments:
3821          -h, --help  show this help message and exit
3822          --foo FOO   foo help
3823        '''
3824    usage = ''
3825    version = ''
3826
3827
3828class TestHelpSuppressOptional(HelpTestCase):
3829    """Test that optional arguments can be suppressed in help messages"""
3830
3831    parser_signature = Sig(prog='PROG', add_help=False)
3832    argument_signatures = [
3833        Sig('--foo', help=argparse.SUPPRESS),
3834        Sig('spam', help='spam help'),
3835    ]
3836    argument_group_signatures = []
3837    usage = '''\
3838        usage: PROG spam
3839        '''
3840    help = usage + '''\
3841
3842        positional arguments:
3843          spam  spam help
3844        '''
3845    version = ''
3846
3847
3848class TestHelpSuppressOptionalGroup(HelpTestCase):
3849    """Test that optional groups can be suppressed in help messages"""
3850
3851    parser_signature = Sig(prog='PROG')
3852    argument_signatures = [
3853        Sig('--foo', help='foo help'),
3854        Sig('spam', help='spam help'),
3855    ]
3856    argument_group_signatures = [
3857        (Sig('group'), [Sig('--bar', help=argparse.SUPPRESS)]),
3858    ]
3859    usage = '''\
3860        usage: PROG [-h] [--foo FOO] spam
3861        '''
3862    help = usage + '''\
3863
3864        positional arguments:
3865          spam        spam help
3866
3867        optional arguments:
3868          -h, --help  show this help message and exit
3869          --foo FOO   foo help
3870        '''
3871    version = ''
3872
3873
3874class TestHelpSuppressPositional(HelpTestCase):
3875    """Test that positional arguments can be suppressed in help messages"""
3876
3877    parser_signature = Sig(prog='PROG')
3878    argument_signatures = [
3879        Sig('--foo', help='foo help'),
3880        Sig('spam', help=argparse.SUPPRESS),
3881    ]
3882    argument_group_signatures = []
3883    usage = '''\
3884        usage: PROG [-h] [--foo FOO]
3885        '''
3886    help = usage + '''\
3887
3888        optional arguments:
3889          -h, --help  show this help message and exit
3890          --foo FOO   foo help
3891        '''
3892    version = ''
3893
3894
3895class TestHelpRequiredOptional(HelpTestCase):
3896    """Test that required options don't look optional"""
3897
3898    parser_signature = Sig(prog='PROG')
3899    argument_signatures = [
3900        Sig('--foo', required=True, help='foo help'),
3901    ]
3902    argument_group_signatures = []
3903    usage = '''\
3904        usage: PROG [-h] --foo FOO
3905        '''
3906    help = usage + '''\
3907
3908        optional arguments:
3909          -h, --help  show this help message and exit
3910          --foo FOO   foo help
3911        '''
3912    version = ''
3913
3914
3915class TestHelpAlternatePrefixChars(HelpTestCase):
3916    """Test that options display with different prefix characters"""
3917
3918    parser_signature = Sig(prog='PROG', prefix_chars='^;', add_help=False)
3919    argument_signatures = [
3920        Sig('^^foo', action='store_true', help='foo help'),
3921        Sig(';b', ';;bar', help='bar help'),
3922    ]
3923    argument_group_signatures = []
3924    usage = '''\
3925        usage: PROG [^^foo] [;b BAR]
3926        '''
3927    help = usage + '''\
3928
3929        optional arguments:
3930          ^^foo              foo help
3931          ;b BAR, ;;bar BAR  bar help
3932        '''
3933    version = ''
3934
3935
3936class TestHelpNoHelpOptional(HelpTestCase):
3937    """Test that the --help argument can be suppressed help messages"""
3938
3939    parser_signature = Sig(prog='PROG', add_help=False)
3940    argument_signatures = [
3941        Sig('--foo', help='foo help'),
3942        Sig('spam', help='spam help'),
3943    ]
3944    argument_group_signatures = []
3945    usage = '''\
3946        usage: PROG [--foo FOO] spam
3947        '''
3948    help = usage + '''\
3949
3950        positional arguments:
3951          spam       spam help
3952
3953        optional arguments:
3954          --foo FOO  foo help
3955        '''
3956    version = ''
3957
3958
3959class TestHelpNone(HelpTestCase):
3960    """Test that no errors occur if no help is specified"""
3961
3962    parser_signature = Sig(prog='PROG')
3963    argument_signatures = [
3964        Sig('--foo'),
3965        Sig('spam'),
3966    ]
3967    argument_group_signatures = []
3968    usage = '''\
3969        usage: PROG [-h] [--foo FOO] spam
3970        '''
3971    help = usage + '''\
3972
3973        positional arguments:
3974          spam
3975
3976        optional arguments:
3977          -h, --help  show this help message and exit
3978          --foo FOO
3979        '''
3980    version = ''
3981
3982
3983class TestHelpTupleMetavar(HelpTestCase):
3984    """Test specifying metavar as a tuple"""
3985
3986    parser_signature = Sig(prog='PROG')
3987    argument_signatures = [
3988        Sig('-w', help='w', nargs='+', metavar=('W1', 'W2')),
3989        Sig('-x', help='x', nargs='*', metavar=('X1', 'X2')),
3990        Sig('-y', help='y', nargs=3, metavar=('Y1', 'Y2', 'Y3')),
3991        Sig('-z', help='z', nargs='?', metavar=('Z1', )),
3992    ]
3993    argument_group_signatures = []
3994    usage = '''\
3995        usage: PROG [-h] [-w W1 [W2 ...]] [-x [X1 [X2 ...]]] [-y Y1 Y2 Y3] \
3996[-z [Z1]]
3997        '''
3998    help = usage + '''\
3999
4000        optional arguments:
4001          -h, --help        show this help message and exit
4002          -w W1 [W2 ...]    w
4003          -x [X1 [X2 ...]]  x
4004          -y Y1 Y2 Y3       y
4005          -z [Z1]           z
4006        '''
4007    version = ''
4008
4009
4010class TestHelpRawText(HelpTestCase):
4011    """Test the RawTextHelpFormatter"""
4012
4013    parser_signature = Sig(
4014        prog='PROG', formatter_class=argparse.RawTextHelpFormatter,
4015        description='Keep the formatting\n'
4016                    '    exactly as it is written\n'
4017                    '\n'
4018                    'here\n')
4019
4020    argument_signatures = [
4021        Sig('--foo', help='    foo help should also\n'
4022                          'appear as given here'),
4023        Sig('spam', help='spam help'),
4024    ]
4025    argument_group_signatures = [
4026        (Sig('title', description='    This text\n'
4027                                  '  should be indented\n'
4028                                  '    exactly like it is here\n'),
4029         [Sig('--bar', help='bar help')]),
4030    ]
4031    usage = '''\
4032        usage: PROG [-h] [--foo FOO] [--bar BAR] spam
4033        '''
4034    help = usage + '''\
4035
4036        Keep the formatting
4037            exactly as it is written
4038
4039        here
4040
4041        positional arguments:
4042          spam        spam help
4043
4044        optional arguments:
4045          -h, --help  show this help message and exit
4046          --foo FOO       foo help should also
4047                      appear as given here
4048
4049        title:
4050              This text
4051            should be indented
4052              exactly like it is here
4053
4054          --bar BAR   bar help
4055        '''
4056    version = ''
4057
4058
4059class TestHelpRawDescription(HelpTestCase):
4060    """Test the RawTextHelpFormatter"""
4061
4062    parser_signature = Sig(
4063        prog='PROG', formatter_class=argparse.RawDescriptionHelpFormatter,
4064        description='Keep the formatting\n'
4065                    '    exactly as it is written\n'
4066                    '\n'
4067                    'here\n')
4068
4069    argument_signatures = [
4070        Sig('--foo', help='  foo help should not\n'
4071                          '    retain this odd formatting'),
4072        Sig('spam', help='spam help'),
4073    ]
4074    argument_group_signatures = [
4075        (Sig('title', description='    This text\n'
4076                                  '  should be indented\n'
4077                                  '    exactly like it is here\n'),
4078         [Sig('--bar', help='bar help')]),
4079    ]
4080    usage = '''\
4081        usage: PROG [-h] [--foo FOO] [--bar BAR] spam
4082        '''
4083    help = usage + '''\
4084
4085        Keep the formatting
4086            exactly as it is written
4087
4088        here
4089
4090        positional arguments:
4091          spam        spam help
4092
4093        optional arguments:
4094          -h, --help  show this help message and exit
4095          --foo FOO   foo help should not retain this odd formatting
4096
4097        title:
4098              This text
4099            should be indented
4100              exactly like it is here
4101
4102          --bar BAR   bar help
4103        '''
4104    version = ''
4105
4106
4107class TestHelpArgumentDefaults(HelpTestCase):
4108    """Test the ArgumentDefaultsHelpFormatter"""
4109
4110    parser_signature = Sig(
4111        prog='PROG', formatter_class=argparse.ArgumentDefaultsHelpFormatter,
4112        description='description')
4113
4114    argument_signatures = [
4115        Sig('--foo', help='foo help - oh and by the way, %(default)s'),
4116        Sig('--bar', action='store_true', help='bar help'),
4117        Sig('spam', help='spam help'),
4118        Sig('badger', nargs='?', default='wooden', help='badger help'),
4119    ]
4120    argument_group_signatures = [
4121        (Sig('title', description='description'),
4122         [Sig('--baz', type=int, default=42, help='baz help')]),
4123    ]
4124    usage = '''\
4125        usage: PROG [-h] [--foo FOO] [--bar] [--baz BAZ] spam [badger]
4126        '''
4127    help = usage + '''\
4128
4129        description
4130
4131        positional arguments:
4132          spam        spam help
4133          badger      badger help (default: wooden)
4134
4135        optional arguments:
4136          -h, --help  show this help message and exit
4137          --foo FOO   foo help - oh and by the way, None
4138          --bar       bar help (default: False)
4139
4140        title:
4141          description
4142
4143          --baz BAZ   baz help (default: 42)
4144        '''
4145    version = ''
4146
4147class TestHelpVersionAction(HelpTestCase):
4148    """Test the default help for the version action"""
4149
4150    parser_signature = Sig(prog='PROG', description='description')
4151    argument_signatures = [Sig('-V', '--version', action='version', version='3.6')]
4152    argument_group_signatures = []
4153    usage = '''\
4154        usage: PROG [-h] [-V]
4155        '''
4156    help = usage + '''\
4157
4158        description
4159
4160        optional arguments:
4161          -h, --help     show this help message and exit
4162          -V, --version  show program's version number and exit
4163        '''
4164    version = ''
4165
4166
4167class TestHelpVersionActionSuppress(HelpTestCase):
4168    """Test that the --version argument can be suppressed in help messages"""
4169
4170    parser_signature = Sig(prog='PROG')
4171    argument_signatures = [
4172        Sig('-v', '--version', action='version', version='1.0',
4173            help=argparse.SUPPRESS),
4174        Sig('--foo', help='foo help'),
4175        Sig('spam', help='spam help'),
4176    ]
4177    argument_group_signatures = []
4178    usage = '''\
4179        usage: PROG [-h] [--foo FOO] spam
4180        '''
4181    help = usage + '''\
4182
4183        positional arguments:
4184          spam        spam help
4185
4186        optional arguments:
4187          -h, --help  show this help message and exit
4188          --foo FOO   foo help
4189        '''
4190
4191
4192class TestHelpSubparsersOrdering(HelpTestCase):
4193    """Test ordering of subcommands in help matches the code"""
4194    parser_signature = Sig(prog='PROG',
4195                           description='display some subcommands')
4196    argument_signatures = [Sig('-v', '--version', action='version', version='0.1')]
4197
4198    subparsers_signatures = [Sig(name=name)
4199                             for name in ('a', 'b', 'c', 'd', 'e')]
4200
4201    usage = '''\
4202        usage: PROG [-h] [-v] {a,b,c,d,e} ...
4203        '''
4204
4205    help = usage + '''\
4206
4207        display some subcommands
4208
4209        positional arguments:
4210          {a,b,c,d,e}
4211
4212        optional arguments:
4213          -h, --help     show this help message and exit
4214          -v, --version  show program's version number and exit
4215        '''
4216
4217    version = '''\
4218        0.1
4219        '''
4220
4221class TestHelpSubparsersWithHelpOrdering(HelpTestCase):
4222    """Test ordering of subcommands in help matches the code"""
4223    parser_signature = Sig(prog='PROG',
4224                           description='display some subcommands')
4225    argument_signatures = [Sig('-v', '--version', action='version', version='0.1')]
4226
4227    subcommand_data = (('a', 'a subcommand help'),
4228                       ('b', 'b subcommand help'),
4229                       ('c', 'c subcommand help'),
4230                       ('d', 'd subcommand help'),
4231                       ('e', 'e subcommand help'),
4232                       )
4233
4234    subparsers_signatures = [Sig(name=name, help=help)
4235                             for name, help in subcommand_data]
4236
4237    usage = '''\
4238        usage: PROG [-h] [-v] {a,b,c,d,e} ...
4239        '''
4240
4241    help = usage + '''\
4242
4243        display some subcommands
4244
4245        positional arguments:
4246          {a,b,c,d,e}
4247            a            a subcommand help
4248            b            b subcommand help
4249            c            c subcommand help
4250            d            d subcommand help
4251            e            e subcommand help
4252
4253        optional arguments:
4254          -h, --help     show this help message and exit
4255          -v, --version  show program's version number and exit
4256        '''
4257
4258    version = '''\
4259        0.1
4260        '''
4261
4262
4263
4264class TestHelpMetavarTypeFormatter(HelpTestCase):
4265    """"""
4266
4267    def custom_type(string):
4268        return string
4269
4270    parser_signature = Sig(prog='PROG', description='description',
4271                           formatter_class=argparse.MetavarTypeHelpFormatter)
4272    argument_signatures = [Sig('a', type=int),
4273                           Sig('-b', type=custom_type),
4274                           Sig('-c', type=float, metavar='SOME FLOAT')]
4275    argument_group_signatures = []
4276    usage = '''\
4277        usage: PROG [-h] [-b custom_type] [-c SOME FLOAT] int
4278        '''
4279    help = usage + '''\
4280
4281        description
4282
4283        positional arguments:
4284          int
4285
4286        optional arguments:
4287          -h, --help      show this help message and exit
4288          -b custom_type
4289          -c SOME FLOAT
4290        '''
4291    version = ''
4292
4293
4294# =====================================
4295# Optional/Positional constructor tests
4296# =====================================
4297
4298class TestInvalidArgumentConstructors(TestCase):
4299    """Test a bunch of invalid Argument constructors"""
4300
4301    def assertTypeError(self, *args, **kwargs):
4302        parser = argparse.ArgumentParser()
4303        self.assertRaises(TypeError, parser.add_argument,
4304                          *args, **kwargs)
4305
4306    def assertValueError(self, *args, **kwargs):
4307        parser = argparse.ArgumentParser()
4308        self.assertRaises(ValueError, parser.add_argument,
4309                          *args, **kwargs)
4310
4311    def test_invalid_keyword_arguments(self):
4312        self.assertTypeError('-x', bar=None)
4313        self.assertTypeError('-y', callback='foo')
4314        self.assertTypeError('-y', callback_args=())
4315        self.assertTypeError('-y', callback_kwargs={})
4316
4317    def test_missing_destination(self):
4318        self.assertTypeError()
4319        for action in ['append', 'store']:
4320            self.assertTypeError(action=action)
4321
4322    def test_invalid_option_strings(self):
4323        self.assertValueError('--')
4324        self.assertValueError('---')
4325
4326    def test_invalid_type(self):
4327        self.assertValueError('--foo', type='int')
4328        self.assertValueError('--foo', type=(int, float))
4329
4330    def test_invalid_action(self):
4331        self.assertValueError('-x', action='foo')
4332        self.assertValueError('foo', action='baz')
4333        self.assertValueError('--foo', action=('store', 'append'))
4334        parser = argparse.ArgumentParser()
4335        with self.assertRaises(ValueError) as cm:
4336            parser.add_argument("--foo", action="store-true")
4337        self.assertIn('unknown action', str(cm.exception))
4338
4339    def test_multiple_dest(self):
4340        parser = argparse.ArgumentParser()
4341        parser.add_argument(dest='foo')
4342        with self.assertRaises(ValueError) as cm:
4343            parser.add_argument('bar', dest='baz')
4344        self.assertIn('dest supplied twice for positional argument',
4345                      str(cm.exception))
4346
4347    def test_no_argument_actions(self):
4348        for action in ['store_const', 'store_true', 'store_false',
4349                       'append_const', 'count']:
4350            for attrs in [dict(type=int), dict(nargs='+'),
4351                          dict(choices='ab')]:
4352                self.assertTypeError('-x', action=action, **attrs)
4353
4354    def test_no_argument_no_const_actions(self):
4355        # options with zero arguments
4356        for action in ['store_true', 'store_false', 'count']:
4357
4358            # const is always disallowed
4359            self.assertTypeError('-x', const='foo', action=action)
4360
4361            # nargs is always disallowed
4362            self.assertTypeError('-x', nargs='*', action=action)
4363
4364    def test_more_than_one_argument_actions(self):
4365        for action in ['store', 'append']:
4366
4367            # nargs=0 is disallowed
4368            self.assertValueError('-x', nargs=0, action=action)
4369            self.assertValueError('spam', nargs=0, action=action)
4370
4371            # const is disallowed with non-optional arguments
4372            for nargs in [1, '*', '+']:
4373                self.assertValueError('-x', const='foo',
4374                                      nargs=nargs, action=action)
4375                self.assertValueError('spam', const='foo',
4376                                      nargs=nargs, action=action)
4377
4378    def test_required_const_actions(self):
4379        for action in ['store_const', 'append_const']:
4380
4381            # nargs is always disallowed
4382            self.assertTypeError('-x', nargs='+', action=action)
4383
4384    def test_parsers_action_missing_params(self):
4385        self.assertTypeError('command', action='parsers')
4386        self.assertTypeError('command', action='parsers', prog='PROG')
4387        self.assertTypeError('command', action='parsers',
4388                             parser_class=argparse.ArgumentParser)
4389
4390    def test_required_positional(self):
4391        self.assertTypeError('foo', required=True)
4392
4393    def test_user_defined_action(self):
4394
4395        class Success(Exception):
4396            pass
4397
4398        class Action(object):
4399
4400            def __init__(self,
4401                         option_strings,
4402                         dest,
4403                         const,
4404                         default,
4405                         required=False):
4406                if dest == 'spam':
4407                    if const is Success:
4408                        if default is Success:
4409                            raise Success()
4410
4411            def __call__(self, *args, **kwargs):
4412                pass
4413
4414        parser = argparse.ArgumentParser()
4415        self.assertRaises(Success, parser.add_argument, '--spam',
4416                          action=Action, default=Success, const=Success)
4417        self.assertRaises(Success, parser.add_argument, 'spam',
4418                          action=Action, default=Success, const=Success)
4419
4420# ================================
4421# Actions returned by add_argument
4422# ================================
4423
4424class TestActionsReturned(TestCase):
4425
4426    def test_dest(self):
4427        parser = argparse.ArgumentParser()
4428        action = parser.add_argument('--foo')
4429        self.assertEqual(action.dest, 'foo')
4430        action = parser.add_argument('-b', '--bar')
4431        self.assertEqual(action.dest, 'bar')
4432        action = parser.add_argument('-x', '-y')
4433        self.assertEqual(action.dest, 'x')
4434
4435    def test_misc(self):
4436        parser = argparse.ArgumentParser()
4437        action = parser.add_argument('--foo', nargs='?', const=42,
4438                                     default=84, type=int, choices=[1, 2],
4439                                     help='FOO', metavar='BAR', dest='baz')
4440        self.assertEqual(action.nargs, '?')
4441        self.assertEqual(action.const, 42)
4442        self.assertEqual(action.default, 84)
4443        self.assertEqual(action.type, int)
4444        self.assertEqual(action.choices, [1, 2])
4445        self.assertEqual(action.help, 'FOO')
4446        self.assertEqual(action.metavar, 'BAR')
4447        self.assertEqual(action.dest, 'baz')
4448
4449
4450# ================================
4451# Argument conflict handling tests
4452# ================================
4453
4454class TestConflictHandling(TestCase):
4455
4456    def test_bad_type(self):
4457        self.assertRaises(ValueError, argparse.ArgumentParser,
4458                          conflict_handler='foo')
4459
4460    def test_conflict_error(self):
4461        parser = argparse.ArgumentParser()
4462        parser.add_argument('-x')
4463        self.assertRaises(argparse.ArgumentError,
4464                          parser.add_argument, '-x')
4465        parser.add_argument('--spam')
4466        self.assertRaises(argparse.ArgumentError,
4467                          parser.add_argument, '--spam')
4468
4469    def test_resolve_error(self):
4470        get_parser = argparse.ArgumentParser
4471        parser = get_parser(prog='PROG', conflict_handler='resolve')
4472
4473        parser.add_argument('-x', help='OLD X')
4474        parser.add_argument('-x', help='NEW X')
4475        self.assertEqual(parser.format_help(), textwrap.dedent('''\
4476            usage: PROG [-h] [-x X]
4477
4478            optional arguments:
4479              -h, --help  show this help message and exit
4480              -x X        NEW X
4481            '''))
4482
4483        parser.add_argument('--spam', metavar='OLD_SPAM')
4484        parser.add_argument('--spam', metavar='NEW_SPAM')
4485        self.assertEqual(parser.format_help(), textwrap.dedent('''\
4486            usage: PROG [-h] [-x X] [--spam NEW_SPAM]
4487
4488            optional arguments:
4489              -h, --help       show this help message and exit
4490              -x X             NEW X
4491              --spam NEW_SPAM
4492            '''))
4493
4494
4495# =============================
4496# Help and Version option tests
4497# =============================
4498
4499class TestOptionalsHelpVersionActions(TestCase):
4500    """Test the help and version actions"""
4501
4502    def assertPrintHelpExit(self, parser, args_str):
4503        with self.assertRaises(ArgumentParserError) as cm:
4504            parser.parse_args(args_str.split())
4505        self.assertEqual(parser.format_help(), cm.exception.stdout)
4506
4507    def assertArgumentParserError(self, parser, *args):
4508        self.assertRaises(ArgumentParserError, parser.parse_args, args)
4509
4510    def test_version(self):
4511        parser = ErrorRaisingArgumentParser()
4512        parser.add_argument('-v', '--version', action='version', version='1.0')
4513        self.assertPrintHelpExit(parser, '-h')
4514        self.assertPrintHelpExit(parser, '--help')
4515        self.assertRaises(AttributeError, getattr, parser, 'format_version')
4516
4517    def test_version_format(self):
4518        parser = ErrorRaisingArgumentParser(prog='PPP')
4519        parser.add_argument('-v', '--version', action='version', version='%(prog)s 3.5')
4520        with self.assertRaises(ArgumentParserError) as cm:
4521            parser.parse_args(['-v'])
4522        self.assertEqual('PPP 3.5\n', cm.exception.stdout)
4523
4524    def test_version_no_help(self):
4525        parser = ErrorRaisingArgumentParser(add_help=False)
4526        parser.add_argument('-v', '--version', action='version', version='1.0')
4527        self.assertArgumentParserError(parser, '-h')
4528        self.assertArgumentParserError(parser, '--help')
4529        self.assertRaises(AttributeError, getattr, parser, 'format_version')
4530
4531    def test_version_action(self):
4532        parser = ErrorRaisingArgumentParser(prog='XXX')
4533        parser.add_argument('-V', action='version', version='%(prog)s 3.7')
4534        with self.assertRaises(ArgumentParserError) as cm:
4535            parser.parse_args(['-V'])
4536        self.assertEqual('XXX 3.7\n', cm.exception.stdout)
4537
4538    def test_no_help(self):
4539        parser = ErrorRaisingArgumentParser(add_help=False)
4540        self.assertArgumentParserError(parser, '-h')
4541        self.assertArgumentParserError(parser, '--help')
4542        self.assertArgumentParserError(parser, '-v')
4543        self.assertArgumentParserError(parser, '--version')
4544
4545    def test_alternate_help_version(self):
4546        parser = ErrorRaisingArgumentParser()
4547        parser.add_argument('-x', action='help')
4548        parser.add_argument('-y', action='version')
4549        self.assertPrintHelpExit(parser, '-x')
4550        self.assertArgumentParserError(parser, '-v')
4551        self.assertArgumentParserError(parser, '--version')
4552        self.assertRaises(AttributeError, getattr, parser, 'format_version')
4553
4554    def test_help_version_extra_arguments(self):
4555        parser = ErrorRaisingArgumentParser()
4556        parser.add_argument('--version', action='version', version='1.0')
4557        parser.add_argument('-x', action='store_true')
4558        parser.add_argument('y')
4559
4560        # try all combinations of valid prefixes and suffixes
4561        valid_prefixes = ['', '-x', 'foo', '-x bar', 'baz -x']
4562        valid_suffixes = valid_prefixes + ['--bad-option', 'foo bar baz']
4563        for prefix in valid_prefixes:
4564            for suffix in valid_suffixes:
4565                format = '%s %%s %s' % (prefix, suffix)
4566            self.assertPrintHelpExit(parser, format % '-h')
4567            self.assertPrintHelpExit(parser, format % '--help')
4568            self.assertRaises(AttributeError, getattr, parser, 'format_version')
4569
4570
4571# ======================
4572# str() and repr() tests
4573# ======================
4574
4575class TestStrings(TestCase):
4576    """Test str()  and repr() on Optionals and Positionals"""
4577
4578    def assertStringEqual(self, obj, result_string):
4579        for func in [str, repr]:
4580            self.assertEqual(func(obj), result_string)
4581
4582    def test_optional(self):
4583        option = argparse.Action(
4584            option_strings=['--foo', '-a', '-b'],
4585            dest='b',
4586            type='int',
4587            nargs='+',
4588            default=42,
4589            choices=[1, 2, 3],
4590            help='HELP',
4591            metavar='METAVAR')
4592        string = (
4593            "Action(option_strings=['--foo', '-a', '-b'], dest='b', "
4594            "nargs='+', const=None, default=42, type='int', "
4595            "choices=[1, 2, 3], help='HELP', metavar='METAVAR')")
4596        self.assertStringEqual(option, string)
4597
4598    def test_argument(self):
4599        argument = argparse.Action(
4600            option_strings=[],
4601            dest='x',
4602            type=float,
4603            nargs='?',
4604            default=2.5,
4605            choices=[0.5, 1.5, 2.5],
4606            help='H HH H',
4607            metavar='MV MV MV')
4608        string = (
4609            "Action(option_strings=[], dest='x', nargs='?', "
4610            "const=None, default=2.5, type=%r, choices=[0.5, 1.5, 2.5], "
4611            "help='H HH H', metavar='MV MV MV')" % float)
4612        self.assertStringEqual(argument, string)
4613
4614    def test_namespace(self):
4615        ns = argparse.Namespace(foo=42, bar='spam')
4616        string = "Namespace(bar='spam', foo=42)"
4617        self.assertStringEqual(ns, string)
4618
4619    def test_namespace_starkwargs_notidentifier(self):
4620        ns = argparse.Namespace(**{'"': 'quote'})
4621        string = """Namespace(**{'"': 'quote'})"""
4622        self.assertStringEqual(ns, string)
4623
4624    def test_namespace_kwargs_and_starkwargs_notidentifier(self):
4625        ns = argparse.Namespace(a=1, **{'"': 'quote'})
4626        string = """Namespace(a=1, **{'"': 'quote'})"""
4627        self.assertStringEqual(ns, string)
4628
4629    def test_namespace_starkwargs_identifier(self):
4630        ns = argparse.Namespace(**{'valid': True})
4631        string = "Namespace(valid=True)"
4632        self.assertStringEqual(ns, string)
4633
4634    def test_parser(self):
4635        parser = argparse.ArgumentParser(prog='PROG')
4636        string = (
4637            "ArgumentParser(prog='PROG', usage=None, description=None, "
4638            "formatter_class=%r, conflict_handler='error', "
4639            "add_help=True)" % argparse.HelpFormatter)
4640        self.assertStringEqual(parser, string)
4641
4642# ===============
4643# Namespace tests
4644# ===============
4645
4646class TestNamespace(TestCase):
4647
4648    def test_constructor(self):
4649        ns = argparse.Namespace()
4650        self.assertRaises(AttributeError, getattr, ns, 'x')
4651
4652        ns = argparse.Namespace(a=42, b='spam')
4653        self.assertEqual(ns.a, 42)
4654        self.assertEqual(ns.b, 'spam')
4655
4656    def test_equality(self):
4657        ns1 = argparse.Namespace(a=1, b=2)
4658        ns2 = argparse.Namespace(b=2, a=1)
4659        ns3 = argparse.Namespace(a=1)
4660        ns4 = argparse.Namespace(b=2)
4661
4662        self.assertEqual(ns1, ns2)
4663        self.assertNotEqual(ns1, ns3)
4664        self.assertNotEqual(ns1, ns4)
4665        self.assertNotEqual(ns2, ns3)
4666        self.assertNotEqual(ns2, ns4)
4667        self.assertTrue(ns1 != ns3)
4668        self.assertTrue(ns1 != ns4)
4669        self.assertTrue(ns2 != ns3)
4670        self.assertTrue(ns2 != ns4)
4671
4672    def test_equality_returns_notimplemented(self):
4673        # See issue 21481
4674        ns = argparse.Namespace(a=1, b=2)
4675        self.assertIs(ns.__eq__(None), NotImplemented)
4676        self.assertIs(ns.__ne__(None), NotImplemented)
4677
4678
4679# ===================
4680# File encoding tests
4681# ===================
4682
4683class TestEncoding(TestCase):
4684
4685    def _test_module_encoding(self, path):
4686        path, _ = os.path.splitext(path)
4687        path += ".py"
4688        with open(path, 'r', encoding='utf-8') as f:
4689            f.read()
4690
4691    def test_argparse_module_encoding(self):
4692        self._test_module_encoding(argparse.__file__)
4693
4694    def test_test_argparse_module_encoding(self):
4695        self._test_module_encoding(__file__)
4696
4697# ===================
4698# ArgumentError tests
4699# ===================
4700
4701class TestArgumentError(TestCase):
4702
4703    def test_argument_error(self):
4704        msg = "my error here"
4705        error = argparse.ArgumentError(None, msg)
4706        self.assertEqual(str(error), msg)
4707
4708# =======================
4709# ArgumentTypeError tests
4710# =======================
4711
4712class TestArgumentTypeError(TestCase):
4713
4714    def test_argument_type_error(self):
4715
4716        def spam(string):
4717            raise argparse.ArgumentTypeError('spam!')
4718
4719        parser = ErrorRaisingArgumentParser(prog='PROG', add_help=False)
4720        parser.add_argument('x', type=spam)
4721        with self.assertRaises(ArgumentParserError) as cm:
4722            parser.parse_args(['XXX'])
4723        self.assertEqual('usage: PROG x\nPROG: error: argument x: spam!\n',
4724                         cm.exception.stderr)
4725
4726# =========================
4727# MessageContentError tests
4728# =========================
4729
4730class TestMessageContentError(TestCase):
4731
4732    def test_missing_argument_name_in_message(self):
4733        parser = ErrorRaisingArgumentParser(prog='PROG', usage='')
4734        parser.add_argument('req_pos', type=str)
4735        parser.add_argument('-req_opt', type=int, required=True)
4736        parser.add_argument('need_one', type=str, nargs='+')
4737
4738        with self.assertRaises(ArgumentParserError) as cm:
4739            parser.parse_args([])
4740        msg = str(cm.exception)
4741        self.assertRegex(msg, 'req_pos')
4742        self.assertRegex(msg, 'req_opt')
4743        self.assertRegex(msg, 'need_one')
4744        with self.assertRaises(ArgumentParserError) as cm:
4745            parser.parse_args(['myXargument'])
4746        msg = str(cm.exception)
4747        self.assertNotIn(msg, 'req_pos')
4748        self.assertRegex(msg, 'req_opt')
4749        self.assertRegex(msg, 'need_one')
4750        with self.assertRaises(ArgumentParserError) as cm:
4751            parser.parse_args(['myXargument', '-req_opt=1'])
4752        msg = str(cm.exception)
4753        self.assertNotIn(msg, 'req_pos')
4754        self.assertNotIn(msg, 'req_opt')
4755        self.assertRegex(msg, 'need_one')
4756
4757    def test_optional_optional_not_in_message(self):
4758        parser = ErrorRaisingArgumentParser(prog='PROG', usage='')
4759        parser.add_argument('req_pos', type=str)
4760        parser.add_argument('--req_opt', type=int, required=True)
4761        parser.add_argument('--opt_opt', type=bool, nargs='?',
4762                            default=True)
4763        with self.assertRaises(ArgumentParserError) as cm:
4764            parser.parse_args([])
4765        msg = str(cm.exception)
4766        self.assertRegex(msg, 'req_pos')
4767        self.assertRegex(msg, 'req_opt')
4768        self.assertNotIn(msg, 'opt_opt')
4769        with self.assertRaises(ArgumentParserError) as cm:
4770            parser.parse_args(['--req_opt=1'])
4771        msg = str(cm.exception)
4772        self.assertRegex(msg, 'req_pos')
4773        self.assertNotIn(msg, 'req_opt')
4774        self.assertNotIn(msg, 'opt_opt')
4775
4776    def test_optional_positional_not_in_message(self):
4777        parser = ErrorRaisingArgumentParser(prog='PROG', usage='')
4778        parser.add_argument('req_pos')
4779        parser.add_argument('optional_positional', nargs='?', default='eggs')
4780        with self.assertRaises(ArgumentParserError) as cm:
4781            parser.parse_args([])
4782        msg = str(cm.exception)
4783        self.assertRegex(msg, 'req_pos')
4784        self.assertNotIn(msg, 'optional_positional')
4785
4786
4787# ================================================
4788# Check that the type function is called only once
4789# ================================================
4790
4791class TestTypeFunctionCallOnlyOnce(TestCase):
4792
4793    def test_type_function_call_only_once(self):
4794        def spam(string_to_convert):
4795            self.assertEqual(string_to_convert, 'spam!')
4796            return 'foo_converted'
4797
4798        parser = argparse.ArgumentParser()
4799        parser.add_argument('--foo', type=spam, default='bar')
4800        args = parser.parse_args('--foo spam!'.split())
4801        self.assertEqual(NS(foo='foo_converted'), args)
4802
4803# ==================================================================
4804# Check semantics regarding the default argument and type conversion
4805# ==================================================================
4806
4807class TestTypeFunctionCalledOnDefault(TestCase):
4808
4809    def test_type_function_call_with_non_string_default(self):
4810        def spam(int_to_convert):
4811            self.assertEqual(int_to_convert, 0)
4812            return 'foo_converted'
4813
4814        parser = argparse.ArgumentParser()
4815        parser.add_argument('--foo', type=spam, default=0)
4816        args = parser.parse_args([])
4817        # foo should *not* be converted because its default is not a string.
4818        self.assertEqual(NS(foo=0), args)
4819
4820    def test_type_function_call_with_string_default(self):
4821        def spam(int_to_convert):
4822            return 'foo_converted'
4823
4824        parser = argparse.ArgumentParser()
4825        parser.add_argument('--foo', type=spam, default='0')
4826        args = parser.parse_args([])
4827        # foo is converted because its default is a string.
4828        self.assertEqual(NS(foo='foo_converted'), args)
4829
4830    def test_no_double_type_conversion_of_default(self):
4831        def extend(str_to_convert):
4832            return str_to_convert + '*'
4833
4834        parser = argparse.ArgumentParser()
4835        parser.add_argument('--test', type=extend, default='*')
4836        args = parser.parse_args([])
4837        # The test argument will be two stars, one coming from the default
4838        # value and one coming from the type conversion being called exactly
4839        # once.
4840        self.assertEqual(NS(test='**'), args)
4841
4842    def test_issue_15906(self):
4843        # Issue #15906: When action='append', type=str, default=[] are
4844        # providing, the dest value was the string representation "[]" when it
4845        # should have been an empty list.
4846        parser = argparse.ArgumentParser()
4847        parser.add_argument('--test', dest='test', type=str,
4848                            default=[], action='append')
4849        args = parser.parse_args([])
4850        self.assertEqual(args.test, [])
4851
4852# ======================
4853# parse_known_args tests
4854# ======================
4855
4856class TestParseKnownArgs(TestCase):
4857
4858    def test_arguments_tuple(self):
4859        parser = argparse.ArgumentParser()
4860        parser.parse_args(())
4861
4862    def test_arguments_list(self):
4863        parser = argparse.ArgumentParser()
4864        parser.parse_args([])
4865
4866    def test_arguments_tuple_positional(self):
4867        parser = argparse.ArgumentParser()
4868        parser.add_argument('x')
4869        parser.parse_args(('x',))
4870
4871    def test_arguments_list_positional(self):
4872        parser = argparse.ArgumentParser()
4873        parser.add_argument('x')
4874        parser.parse_args(['x'])
4875
4876    def test_optionals(self):
4877        parser = argparse.ArgumentParser()
4878        parser.add_argument('--foo')
4879        args, extras = parser.parse_known_args('--foo F --bar --baz'.split())
4880        self.assertEqual(NS(foo='F'), args)
4881        self.assertEqual(['--bar', '--baz'], extras)
4882
4883    def test_mixed(self):
4884        parser = argparse.ArgumentParser()
4885        parser.add_argument('-v', nargs='?', const=1, type=int)
4886        parser.add_argument('--spam', action='store_false')
4887        parser.add_argument('badger')
4888
4889        argv = ["B", "C", "--foo", "-v", "3", "4"]
4890        args, extras = parser.parse_known_args(argv)
4891        self.assertEqual(NS(v=3, spam=True, badger="B"), args)
4892        self.assertEqual(["C", "--foo", "4"], extras)
4893
4894# ===========================
4895# parse_intermixed_args tests
4896# ===========================
4897
4898class TestIntermixedArgs(TestCase):
4899    def test_basic(self):
4900        # test parsing intermixed optionals and positionals
4901        parser = argparse.ArgumentParser(prog='PROG')
4902        parser.add_argument('--foo', dest='foo')
4903        bar = parser.add_argument('--bar', dest='bar', required=True)
4904        parser.add_argument('cmd')
4905        parser.add_argument('rest', nargs='*', type=int)
4906        argv = 'cmd --foo x 1 --bar y 2 3'.split()
4907        args = parser.parse_intermixed_args(argv)
4908        # rest gets [1,2,3] despite the foo and bar strings
4909        self.assertEqual(NS(bar='y', cmd='cmd', foo='x', rest=[1, 2, 3]), args)
4910
4911        args, extras = parser.parse_known_args(argv)
4912        # cannot parse the '1,2,3'
4913        self.assertEqual(NS(bar='y', cmd='cmd', foo='x', rest=[]), args)
4914        self.assertEqual(["1", "2", "3"], extras)
4915
4916        argv = 'cmd --foo x 1 --error 2 --bar y 3'.split()
4917        args, extras = parser.parse_known_intermixed_args(argv)
4918        # unknown optionals go into extras
4919        self.assertEqual(NS(bar='y', cmd='cmd', foo='x', rest=[1]), args)
4920        self.assertEqual(['--error', '2', '3'], extras)
4921
4922        # restores attributes that were temporarily changed
4923        self.assertIsNone(parser.usage)
4924        self.assertEqual(bar.required, True)
4925
4926    def test_remainder(self):
4927        # Intermixed and remainder are incompatible
4928        parser = ErrorRaisingArgumentParser(prog='PROG')
4929        parser.add_argument('-z')
4930        parser.add_argument('x')
4931        parser.add_argument('y', nargs='...')
4932        argv = 'X A B -z Z'.split()
4933        # intermixed fails with '...' (also 'A...')
4934        # self.assertRaises(TypeError, parser.parse_intermixed_args, argv)
4935        with self.assertRaises(TypeError) as cm:
4936            parser.parse_intermixed_args(argv)
4937        self.assertRegex(str(cm.exception), r'\.\.\.')
4938
4939    def test_exclusive(self):
4940        # mutually exclusive group; intermixed works fine
4941        parser = ErrorRaisingArgumentParser(prog='PROG')
4942        group = parser.add_mutually_exclusive_group(required=True)
4943        group.add_argument('--foo', action='store_true', help='FOO')
4944        group.add_argument('--spam', help='SPAM')
4945        parser.add_argument('badger', nargs='*', default='X', help='BADGER')
4946        args = parser.parse_intermixed_args('1 --foo 2'.split())
4947        self.assertEqual(NS(badger=['1', '2'], foo=True, spam=None), args)
4948        self.assertRaises(ArgumentParserError, parser.parse_intermixed_args, '1 2'.split())
4949        self.assertEqual(group.required, True)
4950
4951    def test_exclusive_incompatible(self):
4952        # mutually exclusive group including positional - fail
4953        parser = ErrorRaisingArgumentParser(prog='PROG')
4954        group = parser.add_mutually_exclusive_group(required=True)
4955        group.add_argument('--foo', action='store_true', help='FOO')
4956        group.add_argument('--spam', help='SPAM')
4957        group.add_argument('badger', nargs='*', default='X', help='BADGER')
4958        self.assertRaises(TypeError, parser.parse_intermixed_args, [])
4959        self.assertEqual(group.required, True)
4960
4961class TestIntermixedMessageContentError(TestCase):
4962    # case where Intermixed gives different error message
4963    # error is raised by 1st parsing step
4964    def test_missing_argument_name_in_message(self):
4965        parser = ErrorRaisingArgumentParser(prog='PROG', usage='')
4966        parser.add_argument('req_pos', type=str)
4967        parser.add_argument('-req_opt', type=int, required=True)
4968
4969        with self.assertRaises(ArgumentParserError) as cm:
4970            parser.parse_args([])
4971        msg = str(cm.exception)
4972        self.assertRegex(msg, 'req_pos')
4973        self.assertRegex(msg, 'req_opt')
4974
4975        with self.assertRaises(ArgumentParserError) as cm:
4976            parser.parse_intermixed_args([])
4977        msg = str(cm.exception)
4978        self.assertNotRegex(msg, 'req_pos')
4979        self.assertRegex(msg, 'req_opt')
4980
4981# ==========================
4982# add_argument metavar tests
4983# ==========================
4984
4985class TestAddArgumentMetavar(TestCase):
4986
4987    EXPECTED_MESSAGE = "length of metavar tuple does not match nargs"
4988
4989    def do_test_no_exception(self, nargs, metavar):
4990        parser = argparse.ArgumentParser()
4991        parser.add_argument("--foo", nargs=nargs, metavar=metavar)
4992
4993    def do_test_exception(self, nargs, metavar):
4994        parser = argparse.ArgumentParser()
4995        with self.assertRaises(ValueError) as cm:
4996            parser.add_argument("--foo", nargs=nargs, metavar=metavar)
4997        self.assertEqual(cm.exception.args[0], self.EXPECTED_MESSAGE)
4998
4999    # Unit tests for different values of metavar when nargs=None
5000
5001    def test_nargs_None_metavar_string(self):
5002        self.do_test_no_exception(nargs=None, metavar="1")
5003
5004    def test_nargs_None_metavar_length0(self):
5005        self.do_test_exception(nargs=None, metavar=tuple())
5006
5007    def test_nargs_None_metavar_length1(self):
5008        self.do_test_no_exception(nargs=None, metavar=("1",))
5009
5010    def test_nargs_None_metavar_length2(self):
5011        self.do_test_exception(nargs=None, metavar=("1", "2"))
5012
5013    def test_nargs_None_metavar_length3(self):
5014        self.do_test_exception(nargs=None, metavar=("1", "2", "3"))
5015
5016    # Unit tests for different values of metavar when nargs=?
5017
5018    def test_nargs_optional_metavar_string(self):
5019        self.do_test_no_exception(nargs="?", metavar="1")
5020
5021    def test_nargs_optional_metavar_length0(self):
5022        self.do_test_exception(nargs="?", metavar=tuple())
5023
5024    def test_nargs_optional_metavar_length1(self):
5025        self.do_test_no_exception(nargs="?", metavar=("1",))
5026
5027    def test_nargs_optional_metavar_length2(self):
5028        self.do_test_exception(nargs="?", metavar=("1", "2"))
5029
5030    def test_nargs_optional_metavar_length3(self):
5031        self.do_test_exception(nargs="?", metavar=("1", "2", "3"))
5032
5033    # Unit tests for different values of metavar when nargs=*
5034
5035    def test_nargs_zeroormore_metavar_string(self):
5036        self.do_test_no_exception(nargs="*", metavar="1")
5037
5038    def test_nargs_zeroormore_metavar_length0(self):
5039        self.do_test_exception(nargs="*", metavar=tuple())
5040
5041    def test_nargs_zeroormore_metavar_length1(self):
5042        self.do_test_exception(nargs="*", metavar=("1",))
5043
5044    def test_nargs_zeroormore_metavar_length2(self):
5045        self.do_test_no_exception(nargs="*", metavar=("1", "2"))
5046
5047    def test_nargs_zeroormore_metavar_length3(self):
5048        self.do_test_exception(nargs="*", metavar=("1", "2", "3"))
5049
5050    # Unit tests for different values of metavar when nargs=+
5051
5052    def test_nargs_oneormore_metavar_string(self):
5053        self.do_test_no_exception(nargs="+", metavar="1")
5054
5055    def test_nargs_oneormore_metavar_length0(self):
5056        self.do_test_exception(nargs="+", metavar=tuple())
5057
5058    def test_nargs_oneormore_metavar_length1(self):
5059        self.do_test_exception(nargs="+", metavar=("1",))
5060
5061    def test_nargs_oneormore_metavar_length2(self):
5062        self.do_test_no_exception(nargs="+", metavar=("1", "2"))
5063
5064    def test_nargs_oneormore_metavar_length3(self):
5065        self.do_test_exception(nargs="+", metavar=("1", "2", "3"))
5066
5067    # Unit tests for different values of metavar when nargs=...
5068
5069    def test_nargs_remainder_metavar_string(self):
5070        self.do_test_no_exception(nargs="...", metavar="1")
5071
5072    def test_nargs_remainder_metavar_length0(self):
5073        self.do_test_no_exception(nargs="...", metavar=tuple())
5074
5075    def test_nargs_remainder_metavar_length1(self):
5076        self.do_test_no_exception(nargs="...", metavar=("1",))
5077
5078    def test_nargs_remainder_metavar_length2(self):
5079        self.do_test_no_exception(nargs="...", metavar=("1", "2"))
5080
5081    def test_nargs_remainder_metavar_length3(self):
5082        self.do_test_no_exception(nargs="...", metavar=("1", "2", "3"))
5083
5084    # Unit tests for different values of metavar when nargs=A...
5085
5086    def test_nargs_parser_metavar_string(self):
5087        self.do_test_no_exception(nargs="A...", metavar="1")
5088
5089    def test_nargs_parser_metavar_length0(self):
5090        self.do_test_exception(nargs="A...", metavar=tuple())
5091
5092    def test_nargs_parser_metavar_length1(self):
5093        self.do_test_no_exception(nargs="A...", metavar=("1",))
5094
5095    def test_nargs_parser_metavar_length2(self):
5096        self.do_test_exception(nargs="A...", metavar=("1", "2"))
5097
5098    def test_nargs_parser_metavar_length3(self):
5099        self.do_test_exception(nargs="A...", metavar=("1", "2", "3"))
5100
5101    # Unit tests for different values of metavar when nargs=1
5102
5103    def test_nargs_1_metavar_string(self):
5104        self.do_test_no_exception(nargs=1, metavar="1")
5105
5106    def test_nargs_1_metavar_length0(self):
5107        self.do_test_exception(nargs=1, metavar=tuple())
5108
5109    def test_nargs_1_metavar_length1(self):
5110        self.do_test_no_exception(nargs=1, metavar=("1",))
5111
5112    def test_nargs_1_metavar_length2(self):
5113        self.do_test_exception(nargs=1, metavar=("1", "2"))
5114
5115    def test_nargs_1_metavar_length3(self):
5116        self.do_test_exception(nargs=1, metavar=("1", "2", "3"))
5117
5118    # Unit tests for different values of metavar when nargs=2
5119
5120    def test_nargs_2_metavar_string(self):
5121        self.do_test_no_exception(nargs=2, metavar="1")
5122
5123    def test_nargs_2_metavar_length0(self):
5124        self.do_test_exception(nargs=2, metavar=tuple())
5125
5126    def test_nargs_2_metavar_length1(self):
5127        self.do_test_exception(nargs=2, metavar=("1",))
5128
5129    def test_nargs_2_metavar_length2(self):
5130        self.do_test_no_exception(nargs=2, metavar=("1", "2"))
5131
5132    def test_nargs_2_metavar_length3(self):
5133        self.do_test_exception(nargs=2, metavar=("1", "2", "3"))
5134
5135    # Unit tests for different values of metavar when nargs=3
5136
5137    def test_nargs_3_metavar_string(self):
5138        self.do_test_no_exception(nargs=3, metavar="1")
5139
5140    def test_nargs_3_metavar_length0(self):
5141        self.do_test_exception(nargs=3, metavar=tuple())
5142
5143    def test_nargs_3_metavar_length1(self):
5144        self.do_test_exception(nargs=3, metavar=("1",))
5145
5146    def test_nargs_3_metavar_length2(self):
5147        self.do_test_exception(nargs=3, metavar=("1", "2"))
5148
5149    def test_nargs_3_metavar_length3(self):
5150        self.do_test_no_exception(nargs=3, metavar=("1", "2", "3"))
5151
5152# ============================
5153# from argparse import * tests
5154# ============================
5155
5156class TestImportStar(TestCase):
5157
5158    def test(self):
5159        for name in argparse.__all__:
5160            self.assertTrue(hasattr(argparse, name))
5161
5162    def test_all_exports_everything_but_modules(self):
5163        items = [
5164            name
5165            for name, value in vars(argparse).items()
5166            if not (name.startswith("_") or name == 'ngettext')
5167            if not inspect.ismodule(value)
5168        ]
5169        self.assertEqual(sorted(items), sorted(argparse.__all__))
5170
5171
5172class TestWrappingMetavar(TestCase):
5173
5174    def setUp(self):
5175        self.parser = ErrorRaisingArgumentParser(
5176            'this_is_spammy_prog_with_a_long_name_sorry_about_the_name'
5177        )
5178        # this metavar was triggering library assertion errors due to usage
5179        # message formatting incorrectly splitting on the ] chars within
5180        metavar = '<http[s]://example:1234>'
5181        self.parser.add_argument('--proxy', metavar=metavar)
5182
5183    def test_help_with_metavar(self):
5184        help_text = self.parser.format_help()
5185        self.assertEqual(help_text, textwrap.dedent('''\
5186            usage: this_is_spammy_prog_with_a_long_name_sorry_about_the_name
5187                   [-h] [--proxy <http[s]://example:1234>]
5188
5189            optional arguments:
5190              -h, --help            show this help message and exit
5191              --proxy <http[s]://example:1234>
5192            '''))
5193
5194
5195def test_main():
5196    support.run_unittest(__name__)
5197    # Remove global references to avoid looking like we have refleaks.
5198    RFile.seen = {}
5199    WFile.seen = set()
5200
5201
5202
5203if __name__ == '__main__':
5204    test_main()
5205