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
1462
1463class RFile(object):
1464    seen = {}
1465
1466    def __init__(self, name):
1467        self.name = name
1468
1469    def __eq__(self, other):
1470        if other in self.seen:
1471            text = self.seen[other]
1472        else:
1473            text = self.seen[other] = other.read()
1474            other.close()
1475        if not isinstance(text, str):
1476            text = text.decode('ascii')
1477        return self.name == other.name == text
1478
1479
1480class TestFileTypeR(TempDirMixin, ParserTestCase):
1481    """Test the FileType option/argument type for reading files"""
1482
1483    def setUp(self):
1484        super(TestFileTypeR, self).setUp()
1485        for file_name in ['foo', 'bar']:
1486            file = open(os.path.join(self.temp_dir, file_name), 'w')
1487            file.write(file_name)
1488            file.close()
1489        self.create_readonly_file('readonly')
1490
1491    argument_signatures = [
1492        Sig('-x', type=argparse.FileType()),
1493        Sig('spam', type=argparse.FileType('r')),
1494    ]
1495    failures = ['-x', '', 'non-existent-file.txt']
1496    successes = [
1497        ('foo', NS(x=None, spam=RFile('foo'))),
1498        ('-x foo bar', NS(x=RFile('foo'), spam=RFile('bar'))),
1499        ('bar -x foo', NS(x=RFile('foo'), spam=RFile('bar'))),
1500        ('-x - -', NS(x=sys.stdin, spam=sys.stdin)),
1501        ('readonly', NS(x=None, spam=RFile('readonly'))),
1502    ]
1503
1504class TestFileTypeDefaults(TempDirMixin, ParserTestCase):
1505    """Test that a file is not created unless the default is needed"""
1506    def setUp(self):
1507        super(TestFileTypeDefaults, self).setUp()
1508        file = open(os.path.join(self.temp_dir, 'good'), 'w')
1509        file.write('good')
1510        file.close()
1511
1512    argument_signatures = [
1513        Sig('-c', type=argparse.FileType('r'), default='no-file.txt'),
1514    ]
1515    # should provoke no such file error
1516    failures = ['']
1517    # should not provoke error because default file is created
1518    successes = [('-c good', NS(c=RFile('good')))]
1519
1520
1521class TestFileTypeRB(TempDirMixin, ParserTestCase):
1522    """Test the FileType option/argument type for reading files"""
1523
1524    def setUp(self):
1525        super(TestFileTypeRB, self).setUp()
1526        for file_name in ['foo', 'bar']:
1527            file = open(os.path.join(self.temp_dir, file_name), 'w')
1528            file.write(file_name)
1529            file.close()
1530
1531    argument_signatures = [
1532        Sig('-x', type=argparse.FileType('rb')),
1533        Sig('spam', type=argparse.FileType('rb')),
1534    ]
1535    failures = ['-x', '']
1536    successes = [
1537        ('foo', NS(x=None, spam=RFile('foo'))),
1538        ('-x foo bar', NS(x=RFile('foo'), spam=RFile('bar'))),
1539        ('bar -x foo', NS(x=RFile('foo'), spam=RFile('bar'))),
1540        ('-x - -', NS(x=sys.stdin, spam=sys.stdin)),
1541    ]
1542
1543
1544class WFile(object):
1545    seen = set()
1546
1547    def __init__(self, name):
1548        self.name = name
1549
1550    def __eq__(self, other):
1551        if other not in self.seen:
1552            text = 'Check that file is writable.'
1553            if 'b' in other.mode:
1554                text = text.encode('ascii')
1555            other.write(text)
1556            other.close()
1557            self.seen.add(other)
1558        return self.name == other.name
1559
1560
1561@unittest.skipIf(hasattr(os, 'geteuid') and os.geteuid() == 0,
1562                 "non-root user required")
1563class TestFileTypeW(TempDirMixin, ParserTestCase):
1564    """Test the FileType option/argument type for writing files"""
1565
1566    def setUp(self):
1567        super(TestFileTypeW, self).setUp()
1568        self.create_readonly_file('readonly')
1569
1570    argument_signatures = [
1571        Sig('-x', type=argparse.FileType('w')),
1572        Sig('spam', type=argparse.FileType('w')),
1573    ]
1574    failures = ['-x', '', 'readonly']
1575    successes = [
1576        ('foo', NS(x=None, spam=WFile('foo'))),
1577        ('-x foo bar', NS(x=WFile('foo'), spam=WFile('bar'))),
1578        ('bar -x foo', NS(x=WFile('foo'), spam=WFile('bar'))),
1579        ('-x - -', NS(x=sys.stdout, spam=sys.stdout)),
1580    ]
1581
1582
1583class TestFileTypeWB(TempDirMixin, ParserTestCase):
1584
1585    argument_signatures = [
1586        Sig('-x', type=argparse.FileType('wb')),
1587        Sig('spam', type=argparse.FileType('wb')),
1588    ]
1589    failures = ['-x', '']
1590    successes = [
1591        ('foo', NS(x=None, spam=WFile('foo'))),
1592        ('-x foo bar', NS(x=WFile('foo'), spam=WFile('bar'))),
1593        ('bar -x foo', NS(x=WFile('foo'), spam=WFile('bar'))),
1594        ('-x - -', NS(x=sys.stdout, spam=sys.stdout)),
1595    ]
1596
1597
1598class TestFileTypeOpenArgs(TestCase):
1599    """Test that open (the builtin) is correctly called"""
1600
1601    def test_open_args(self):
1602        FT = argparse.FileType
1603        cases = [
1604            (FT('rb'), ('rb', -1, None, None)),
1605            (FT('w', 1), ('w', 1, None, None)),
1606            (FT('w', errors='replace'), ('w', -1, None, 'replace')),
1607            (FT('wb', encoding='big5'), ('wb', -1, 'big5', None)),
1608            (FT('w', 0, 'l1', 'strict'), ('w', 0, 'l1', 'strict')),
1609        ]
1610        with mock.patch('builtins.open') as m:
1611            for type, args in cases:
1612                type('foo')
1613                m.assert_called_with('foo', *args)
1614
1615
1616class TestTypeCallable(ParserTestCase):
1617    """Test some callables as option/argument types"""
1618
1619    argument_signatures = [
1620        Sig('--eggs', type=complex),
1621        Sig('spam', type=float),
1622    ]
1623    failures = ['a', '42j', '--eggs a', '--eggs 2i']
1624    successes = [
1625        ('--eggs=42 42', NS(eggs=42, spam=42.0)),
1626        ('--eggs 2j -- -1.5', NS(eggs=2j, spam=-1.5)),
1627        ('1024.675', NS(eggs=None, spam=1024.675)),
1628    ]
1629
1630
1631class TestTypeUserDefined(ParserTestCase):
1632    """Test a user-defined option/argument type"""
1633
1634    class MyType(TestCase):
1635
1636        def __init__(self, value):
1637            self.value = value
1638
1639        def __eq__(self, other):
1640            return (type(self), self.value) == (type(other), other.value)
1641
1642    argument_signatures = [
1643        Sig('-x', type=MyType),
1644        Sig('spam', type=MyType),
1645    ]
1646    failures = []
1647    successes = [
1648        ('a -x b', NS(x=MyType('b'), spam=MyType('a'))),
1649        ('-xf g', NS(x=MyType('f'), spam=MyType('g'))),
1650    ]
1651
1652
1653class TestTypeClassicClass(ParserTestCase):
1654    """Test a classic class type"""
1655
1656    class C:
1657
1658        def __init__(self, value):
1659            self.value = value
1660
1661        def __eq__(self, other):
1662            return (type(self), self.value) == (type(other), other.value)
1663
1664    argument_signatures = [
1665        Sig('-x', type=C),
1666        Sig('spam', type=C),
1667    ]
1668    failures = []
1669    successes = [
1670        ('a -x b', NS(x=C('b'), spam=C('a'))),
1671        ('-xf g', NS(x=C('f'), spam=C('g'))),
1672    ]
1673
1674
1675class TestTypeRegistration(TestCase):
1676    """Test a user-defined type by registering it"""
1677
1678    def test(self):
1679
1680        def get_my_type(string):
1681            return 'my_type{%s}' % string
1682
1683        parser = argparse.ArgumentParser()
1684        parser.register('type', 'my_type', get_my_type)
1685        parser.add_argument('-x', type='my_type')
1686        parser.add_argument('y', type='my_type')
1687
1688        self.assertEqual(parser.parse_args('1'.split()),
1689                         NS(x=None, y='my_type{1}'))
1690        self.assertEqual(parser.parse_args('-x 1 42'.split()),
1691                         NS(x='my_type{1}', y='my_type{42}'))
1692
1693
1694# ============
1695# Action tests
1696# ============
1697
1698class TestActionUserDefined(ParserTestCase):
1699    """Test a user-defined option/argument action"""
1700
1701    class OptionalAction(argparse.Action):
1702
1703        def __call__(self, parser, namespace, value, option_string=None):
1704            try:
1705                # check destination and option string
1706                assert self.dest == 'spam', 'dest: %s' % self.dest
1707                assert option_string == '-s', 'flag: %s' % option_string
1708                # when option is before argument, badger=2, and when
1709                # option is after argument, badger=<whatever was set>
1710                expected_ns = NS(spam=0.25)
1711                if value in [0.125, 0.625]:
1712                    expected_ns.badger = 2
1713                elif value in [2.0]:
1714                    expected_ns.badger = 84
1715                else:
1716                    raise AssertionError('value: %s' % value)
1717                assert expected_ns == namespace, ('expected %s, got %s' %
1718                                                  (expected_ns, namespace))
1719            except AssertionError:
1720                e = sys.exc_info()[1]
1721                raise ArgumentParserError('opt_action failed: %s' % e)
1722            setattr(namespace, 'spam', value)
1723
1724    class PositionalAction(argparse.Action):
1725
1726        def __call__(self, parser, namespace, value, option_string=None):
1727            try:
1728                assert option_string is None, ('option_string: %s' %
1729                                               option_string)
1730                # check destination
1731                assert self.dest == 'badger', 'dest: %s' % self.dest
1732                # when argument is before option, spam=0.25, and when
1733                # option is after argument, spam=<whatever was set>
1734                expected_ns = NS(badger=2)
1735                if value in [42, 84]:
1736                    expected_ns.spam = 0.25
1737                elif value in [1]:
1738                    expected_ns.spam = 0.625
1739                elif value in [2]:
1740                    expected_ns.spam = 0.125
1741                else:
1742                    raise AssertionError('value: %s' % value)
1743                assert expected_ns == namespace, ('expected %s, got %s' %
1744                                                  (expected_ns, namespace))
1745            except AssertionError:
1746                e = sys.exc_info()[1]
1747                raise ArgumentParserError('arg_action failed: %s' % e)
1748            setattr(namespace, 'badger', value)
1749
1750    argument_signatures = [
1751        Sig('-s', dest='spam', action=OptionalAction,
1752            type=float, default=0.25),
1753        Sig('badger', action=PositionalAction,
1754            type=int, nargs='?', default=2),
1755    ]
1756    failures = []
1757    successes = [
1758        ('-s0.125', NS(spam=0.125, badger=2)),
1759        ('42', NS(spam=0.25, badger=42)),
1760        ('-s 0.625 1', NS(spam=0.625, badger=1)),
1761        ('84 -s2', NS(spam=2.0, badger=84)),
1762    ]
1763
1764
1765class TestActionRegistration(TestCase):
1766    """Test a user-defined action supplied by registering it"""
1767
1768    class MyAction(argparse.Action):
1769
1770        def __call__(self, parser, namespace, values, option_string=None):
1771            setattr(namespace, self.dest, 'foo[%s]' % values)
1772
1773    def test(self):
1774
1775        parser = argparse.ArgumentParser()
1776        parser.register('action', 'my_action', self.MyAction)
1777        parser.add_argument('badger', action='my_action')
1778
1779        self.assertEqual(parser.parse_args(['1']), NS(badger='foo[1]'))
1780        self.assertEqual(parser.parse_args(['42']), NS(badger='foo[42]'))
1781
1782
1783# ================
1784# Subparsers tests
1785# ================
1786
1787class TestAddSubparsers(TestCase):
1788    """Test the add_subparsers method"""
1789
1790    def assertArgumentParserError(self, *args, **kwargs):
1791        self.assertRaises(ArgumentParserError, *args, **kwargs)
1792
1793    def _get_parser(self, subparser_help=False, prefix_chars=None,
1794                    aliases=False):
1795        # create a parser with a subparsers argument
1796        if prefix_chars:
1797            parser = ErrorRaisingArgumentParser(
1798                prog='PROG', description='main description', prefix_chars=prefix_chars)
1799            parser.add_argument(
1800                prefix_chars[0] * 2 + 'foo', action='store_true', help='foo help')
1801        else:
1802            parser = ErrorRaisingArgumentParser(
1803                prog='PROG', description='main description')
1804            parser.add_argument(
1805                '--foo', action='store_true', help='foo help')
1806        parser.add_argument(
1807            'bar', type=float, help='bar help')
1808
1809        # check that only one subparsers argument can be added
1810        subparsers_kwargs = {}
1811        if aliases:
1812            subparsers_kwargs['metavar'] = 'COMMAND'
1813            subparsers_kwargs['title'] = 'commands'
1814        else:
1815            subparsers_kwargs['help'] = 'command help'
1816        subparsers = parser.add_subparsers(**subparsers_kwargs)
1817        self.assertArgumentParserError(parser.add_subparsers)
1818
1819        # add first sub-parser
1820        parser1_kwargs = dict(description='1 description')
1821        if subparser_help:
1822            parser1_kwargs['help'] = '1 help'
1823        if aliases:
1824            parser1_kwargs['aliases'] = ['1alias1', '1alias2']
1825        parser1 = subparsers.add_parser('1', **parser1_kwargs)
1826        parser1.add_argument('-w', type=int, help='w help')
1827        parser1.add_argument('x', choices='abc', help='x help')
1828
1829        # add second sub-parser
1830        parser2_kwargs = dict(description='2 description')
1831        if subparser_help:
1832            parser2_kwargs['help'] = '2 help'
1833        parser2 = subparsers.add_parser('2', **parser2_kwargs)
1834        parser2.add_argument('-y', choices='123', help='y help')
1835        parser2.add_argument('z', type=complex, nargs='*', help='z help')
1836
1837        # add third sub-parser
1838        parser3_kwargs = dict(description='3 description')
1839        if subparser_help:
1840            parser3_kwargs['help'] = '3 help'
1841        parser3 = subparsers.add_parser('3', **parser3_kwargs)
1842        parser3.add_argument('t', type=int, help='t help')
1843        parser3.add_argument('u', nargs='...', help='u help')
1844
1845        # return the main parser
1846        return parser
1847
1848    def setUp(self):
1849        super().setUp()
1850        self.parser = self._get_parser()
1851        self.command_help_parser = self._get_parser(subparser_help=True)
1852
1853    def test_parse_args_failures(self):
1854        # check some failure cases:
1855        for args_str in ['', 'a', 'a a', '0.5 a', '0.5 1',
1856                         '0.5 1 -y', '0.5 2 -w']:
1857            args = args_str.split()
1858            self.assertArgumentParserError(self.parser.parse_args, args)
1859
1860    def test_parse_args(self):
1861        # check some non-failure cases:
1862        self.assertEqual(
1863            self.parser.parse_args('0.5 1 b -w 7'.split()),
1864            NS(foo=False, bar=0.5, w=7, x='b'),
1865        )
1866        self.assertEqual(
1867            self.parser.parse_args('0.25 --foo 2 -y 2 3j -- -1j'.split()),
1868            NS(foo=True, bar=0.25, y='2', z=[3j, -1j]),
1869        )
1870        self.assertEqual(
1871            self.parser.parse_args('--foo 0.125 1 c'.split()),
1872            NS(foo=True, bar=0.125, w=None, x='c'),
1873        )
1874        self.assertEqual(
1875            self.parser.parse_args('-1.5 3 11 -- a --foo 7 -- b'.split()),
1876            NS(foo=False, bar=-1.5, t=11, u=['a', '--foo', '7', '--', 'b']),
1877        )
1878
1879    def test_parse_known_args(self):
1880        self.assertEqual(
1881            self.parser.parse_known_args('0.5 1 b -w 7'.split()),
1882            (NS(foo=False, bar=0.5, w=7, x='b'), []),
1883        )
1884        self.assertEqual(
1885            self.parser.parse_known_args('0.5 -p 1 b -w 7'.split()),
1886            (NS(foo=False, bar=0.5, w=7, x='b'), ['-p']),
1887        )
1888        self.assertEqual(
1889            self.parser.parse_known_args('0.5 1 b -w 7 -p'.split()),
1890            (NS(foo=False, bar=0.5, w=7, x='b'), ['-p']),
1891        )
1892        self.assertEqual(
1893            self.parser.parse_known_args('0.5 1 b -q -rs -w 7'.split()),
1894            (NS(foo=False, bar=0.5, w=7, x='b'), ['-q', '-rs']),
1895        )
1896        self.assertEqual(
1897            self.parser.parse_known_args('0.5 -W 1 b -X Y -w 7 Z'.split()),
1898            (NS(foo=False, bar=0.5, w=7, x='b'), ['-W', '-X', 'Y', 'Z']),
1899        )
1900
1901    def test_dest(self):
1902        parser = ErrorRaisingArgumentParser()
1903        parser.add_argument('--foo', action='store_true')
1904        subparsers = parser.add_subparsers(dest='bar')
1905        parser1 = subparsers.add_parser('1')
1906        parser1.add_argument('baz')
1907        self.assertEqual(NS(foo=False, bar='1', baz='2'),
1908                         parser.parse_args('1 2'.split()))
1909
1910    def test_help(self):
1911        self.assertEqual(self.parser.format_usage(),
1912                         'usage: PROG [-h] [--foo] bar {1,2,3} ...\n')
1913        self.assertEqual(self.parser.format_help(), textwrap.dedent('''\
1914            usage: PROG [-h] [--foo] bar {1,2,3} ...
1915
1916            main description
1917
1918            positional arguments:
1919              bar         bar help
1920              {1,2,3}     command help
1921
1922            optional arguments:
1923              -h, --help  show this help message and exit
1924              --foo       foo help
1925            '''))
1926
1927    def test_help_extra_prefix_chars(self):
1928        # Make sure - is still used for help if it is a non-first prefix char
1929        parser = self._get_parser(prefix_chars='+:-')
1930        self.assertEqual(parser.format_usage(),
1931                         'usage: PROG [-h] [++foo] bar {1,2,3} ...\n')
1932        self.assertEqual(parser.format_help(), textwrap.dedent('''\
1933            usage: PROG [-h] [++foo] bar {1,2,3} ...
1934
1935            main description
1936
1937            positional arguments:
1938              bar         bar help
1939              {1,2,3}     command help
1940
1941            optional arguments:
1942              -h, --help  show this help message and exit
1943              ++foo       foo help
1944            '''))
1945
1946    def test_help_non_breaking_spaces(self):
1947        parser = ErrorRaisingArgumentParser(
1948            prog='PROG', description='main description')
1949        parser.add_argument(
1950            "--non-breaking", action='store_false',
1951            help='help message containing non-breaking spaces shall not '
1952            'wrap\N{NO-BREAK SPACE}at non-breaking spaces')
1953        self.assertEqual(parser.format_help(), textwrap.dedent('''\
1954            usage: PROG [-h] [--non-breaking]
1955
1956            main description
1957
1958            optional arguments:
1959              -h, --help      show this help message and exit
1960              --non-breaking  help message containing non-breaking spaces shall not
1961                              wrap\N{NO-BREAK SPACE}at non-breaking spaces
1962        '''))
1963
1964    def test_help_alternate_prefix_chars(self):
1965        parser = self._get_parser(prefix_chars='+:/')
1966        self.assertEqual(parser.format_usage(),
1967                         'usage: PROG [+h] [++foo] bar {1,2,3} ...\n')
1968        self.assertEqual(parser.format_help(), textwrap.dedent('''\
1969            usage: PROG [+h] [++foo] bar {1,2,3} ...
1970
1971            main description
1972
1973            positional arguments:
1974              bar         bar help
1975              {1,2,3}     command help
1976
1977            optional arguments:
1978              +h, ++help  show this help message and exit
1979              ++foo       foo help
1980            '''))
1981
1982    def test_parser_command_help(self):
1983        self.assertEqual(self.command_help_parser.format_usage(),
1984                         'usage: PROG [-h] [--foo] bar {1,2,3} ...\n')
1985        self.assertEqual(self.command_help_parser.format_help(),
1986                         textwrap.dedent('''\
1987            usage: PROG [-h] [--foo] bar {1,2,3} ...
1988
1989            main description
1990
1991            positional arguments:
1992              bar         bar help
1993              {1,2,3}     command help
1994                1         1 help
1995                2         2 help
1996                3         3 help
1997
1998            optional arguments:
1999              -h, --help  show this help message and exit
2000              --foo       foo help
2001            '''))
2002
2003    def test_subparser_title_help(self):
2004        parser = ErrorRaisingArgumentParser(prog='PROG',
2005                                            description='main description')
2006        parser.add_argument('--foo', action='store_true', help='foo help')
2007        parser.add_argument('bar', help='bar help')
2008        subparsers = parser.add_subparsers(title='subcommands',
2009                                           description='command help',
2010                                           help='additional text')
2011        parser1 = subparsers.add_parser('1')
2012        parser2 = subparsers.add_parser('2')
2013        self.assertEqual(parser.format_usage(),
2014                         'usage: PROG [-h] [--foo] bar {1,2} ...\n')
2015        self.assertEqual(parser.format_help(), textwrap.dedent('''\
2016            usage: PROG [-h] [--foo] bar {1,2} ...
2017
2018            main description
2019
2020            positional arguments:
2021              bar         bar help
2022
2023            optional arguments:
2024              -h, --help  show this help message and exit
2025              --foo       foo help
2026
2027            subcommands:
2028              command help
2029
2030              {1,2}       additional text
2031            '''))
2032
2033    def _test_subparser_help(self, args_str, expected_help):
2034        with self.assertRaises(ArgumentParserError) as cm:
2035            self.parser.parse_args(args_str.split())
2036        self.assertEqual(expected_help, cm.exception.stdout)
2037
2038    def test_subparser1_help(self):
2039        self._test_subparser_help('5.0 1 -h', textwrap.dedent('''\
2040            usage: PROG bar 1 [-h] [-w W] {a,b,c}
2041
2042            1 description
2043
2044            positional arguments:
2045              {a,b,c}     x help
2046
2047            optional arguments:
2048              -h, --help  show this help message and exit
2049              -w W        w help
2050            '''))
2051
2052    def test_subparser2_help(self):
2053        self._test_subparser_help('5.0 2 -h', textwrap.dedent('''\
2054            usage: PROG bar 2 [-h] [-y {1,2,3}] [z [z ...]]
2055
2056            2 description
2057
2058            positional arguments:
2059              z           z help
2060
2061            optional arguments:
2062              -h, --help  show this help message and exit
2063              -y {1,2,3}  y help
2064            '''))
2065
2066    def test_alias_invocation(self):
2067        parser = self._get_parser(aliases=True)
2068        self.assertEqual(
2069            parser.parse_known_args('0.5 1alias1 b'.split()),
2070            (NS(foo=False, bar=0.5, w=None, x='b'), []),
2071        )
2072        self.assertEqual(
2073            parser.parse_known_args('0.5 1alias2 b'.split()),
2074            (NS(foo=False, bar=0.5, w=None, x='b'), []),
2075        )
2076
2077    def test_error_alias_invocation(self):
2078        parser = self._get_parser(aliases=True)
2079        self.assertArgumentParserError(parser.parse_args,
2080                                       '0.5 1alias3 b'.split())
2081
2082    def test_alias_help(self):
2083        parser = self._get_parser(aliases=True, subparser_help=True)
2084        self.maxDiff = None
2085        self.assertEqual(parser.format_help(), textwrap.dedent("""\
2086            usage: PROG [-h] [--foo] bar COMMAND ...
2087
2088            main description
2089
2090            positional arguments:
2091              bar                   bar help
2092
2093            optional arguments:
2094              -h, --help            show this help message and exit
2095              --foo                 foo help
2096
2097            commands:
2098              COMMAND
2099                1 (1alias1, 1alias2)
2100                                    1 help
2101                2                   2 help
2102                3                   3 help
2103            """))
2104
2105# ============
2106# Groups tests
2107# ============
2108
2109class TestPositionalsGroups(TestCase):
2110    """Tests that order of group positionals matches construction order"""
2111
2112    def test_nongroup_first(self):
2113        parser = ErrorRaisingArgumentParser()
2114        parser.add_argument('foo')
2115        group = parser.add_argument_group('g')
2116        group.add_argument('bar')
2117        parser.add_argument('baz')
2118        expected = NS(foo='1', bar='2', baz='3')
2119        result = parser.parse_args('1 2 3'.split())
2120        self.assertEqual(expected, result)
2121
2122    def test_group_first(self):
2123        parser = ErrorRaisingArgumentParser()
2124        group = parser.add_argument_group('xxx')
2125        group.add_argument('foo')
2126        parser.add_argument('bar')
2127        parser.add_argument('baz')
2128        expected = NS(foo='1', bar='2', baz='3')
2129        result = parser.parse_args('1 2 3'.split())
2130        self.assertEqual(expected, result)
2131
2132    def test_interleaved_groups(self):
2133        parser = ErrorRaisingArgumentParser()
2134        group = parser.add_argument_group('xxx')
2135        parser.add_argument('foo')
2136        group.add_argument('bar')
2137        parser.add_argument('baz')
2138        group = parser.add_argument_group('yyy')
2139        group.add_argument('frell')
2140        expected = NS(foo='1', bar='2', baz='3', frell='4')
2141        result = parser.parse_args('1 2 3 4'.split())
2142        self.assertEqual(expected, result)
2143
2144# ===================
2145# Parent parser tests
2146# ===================
2147
2148class TestParentParsers(TestCase):
2149    """Tests that parsers can be created with parent parsers"""
2150
2151    def assertArgumentParserError(self, *args, **kwargs):
2152        self.assertRaises(ArgumentParserError, *args, **kwargs)
2153
2154    def setUp(self):
2155        super().setUp()
2156        self.wxyz_parent = ErrorRaisingArgumentParser(add_help=False)
2157        self.wxyz_parent.add_argument('--w')
2158        x_group = self.wxyz_parent.add_argument_group('x')
2159        x_group.add_argument('-y')
2160        self.wxyz_parent.add_argument('z')
2161
2162        self.abcd_parent = ErrorRaisingArgumentParser(add_help=False)
2163        self.abcd_parent.add_argument('a')
2164        self.abcd_parent.add_argument('-b')
2165        c_group = self.abcd_parent.add_argument_group('c')
2166        c_group.add_argument('--d')
2167
2168        self.w_parent = ErrorRaisingArgumentParser(add_help=False)
2169        self.w_parent.add_argument('--w')
2170
2171        self.z_parent = ErrorRaisingArgumentParser(add_help=False)
2172        self.z_parent.add_argument('z')
2173
2174        # parents with mutually exclusive groups
2175        self.ab_mutex_parent = ErrorRaisingArgumentParser(add_help=False)
2176        group = self.ab_mutex_parent.add_mutually_exclusive_group()
2177        group.add_argument('-a', action='store_true')
2178        group.add_argument('-b', action='store_true')
2179
2180        self.main_program = os.path.basename(sys.argv[0])
2181
2182    def test_single_parent(self):
2183        parser = ErrorRaisingArgumentParser(parents=[self.wxyz_parent])
2184        self.assertEqual(parser.parse_args('-y 1 2 --w 3'.split()),
2185                         NS(w='3', y='1', z='2'))
2186
2187    def test_single_parent_mutex(self):
2188        self._test_mutex_ab(self.ab_mutex_parent.parse_args)
2189        parser = ErrorRaisingArgumentParser(parents=[self.ab_mutex_parent])
2190        self._test_mutex_ab(parser.parse_args)
2191
2192    def test_single_granparent_mutex(self):
2193        parents = [self.ab_mutex_parent]
2194        parser = ErrorRaisingArgumentParser(add_help=False, parents=parents)
2195        parser = ErrorRaisingArgumentParser(parents=[parser])
2196        self._test_mutex_ab(parser.parse_args)
2197
2198    def _test_mutex_ab(self, parse_args):
2199        self.assertEqual(parse_args([]), NS(a=False, b=False))
2200        self.assertEqual(parse_args(['-a']), NS(a=True, b=False))
2201        self.assertEqual(parse_args(['-b']), NS(a=False, b=True))
2202        self.assertArgumentParserError(parse_args, ['-a', '-b'])
2203        self.assertArgumentParserError(parse_args, ['-b', '-a'])
2204        self.assertArgumentParserError(parse_args, ['-c'])
2205        self.assertArgumentParserError(parse_args, ['-a', '-c'])
2206        self.assertArgumentParserError(parse_args, ['-b', '-c'])
2207
2208    def test_multiple_parents(self):
2209        parents = [self.abcd_parent, self.wxyz_parent]
2210        parser = ErrorRaisingArgumentParser(parents=parents)
2211        self.assertEqual(parser.parse_args('--d 1 --w 2 3 4'.split()),
2212                         NS(a='3', b=None, d='1', w='2', y=None, z='4'))
2213
2214    def test_multiple_parents_mutex(self):
2215        parents = [self.ab_mutex_parent, self.wxyz_parent]
2216        parser = ErrorRaisingArgumentParser(parents=parents)
2217        self.assertEqual(parser.parse_args('-a --w 2 3'.split()),
2218                         NS(a=True, b=False, w='2', y=None, z='3'))
2219        self.assertArgumentParserError(
2220            parser.parse_args, '-a --w 2 3 -b'.split())
2221        self.assertArgumentParserError(
2222            parser.parse_args, '-a -b --w 2 3'.split())
2223
2224    def test_conflicting_parents(self):
2225        self.assertRaises(
2226            argparse.ArgumentError,
2227            argparse.ArgumentParser,
2228            parents=[self.w_parent, self.wxyz_parent])
2229
2230    def test_conflicting_parents_mutex(self):
2231        self.assertRaises(
2232            argparse.ArgumentError,
2233            argparse.ArgumentParser,
2234            parents=[self.abcd_parent, self.ab_mutex_parent])
2235
2236    def test_same_argument_name_parents(self):
2237        parents = [self.wxyz_parent, self.z_parent]
2238        parser = ErrorRaisingArgumentParser(parents=parents)
2239        self.assertEqual(parser.parse_args('1 2'.split()),
2240                         NS(w=None, y=None, z='2'))
2241
2242    def test_subparser_parents(self):
2243        parser = ErrorRaisingArgumentParser()
2244        subparsers = parser.add_subparsers()
2245        abcde_parser = subparsers.add_parser('bar', parents=[self.abcd_parent])
2246        abcde_parser.add_argument('e')
2247        self.assertEqual(parser.parse_args('bar -b 1 --d 2 3 4'.split()),
2248                         NS(a='3', b='1', d='2', e='4'))
2249
2250    def test_subparser_parents_mutex(self):
2251        parser = ErrorRaisingArgumentParser()
2252        subparsers = parser.add_subparsers()
2253        parents = [self.ab_mutex_parent]
2254        abc_parser = subparsers.add_parser('foo', parents=parents)
2255        c_group = abc_parser.add_argument_group('c_group')
2256        c_group.add_argument('c')
2257        parents = [self.wxyz_parent, self.ab_mutex_parent]
2258        wxyzabe_parser = subparsers.add_parser('bar', parents=parents)
2259        wxyzabe_parser.add_argument('e')
2260        self.assertEqual(parser.parse_args('foo -a 4'.split()),
2261                         NS(a=True, b=False, c='4'))
2262        self.assertEqual(parser.parse_args('bar -b  --w 2 3 4'.split()),
2263                         NS(a=False, b=True, w='2', y=None, z='3', e='4'))
2264        self.assertArgumentParserError(
2265            parser.parse_args, 'foo -a -b 4'.split())
2266        self.assertArgumentParserError(
2267            parser.parse_args, 'bar -b -a 4'.split())
2268
2269    def test_parent_help(self):
2270        parents = [self.abcd_parent, self.wxyz_parent]
2271        parser = ErrorRaisingArgumentParser(parents=parents)
2272        parser_help = parser.format_help()
2273        progname = self.main_program
2274        self.assertEqual(parser_help, textwrap.dedent('''\
2275            usage: {}{}[-h] [-b B] [--d D] [--w W] [-y Y] a z
2276
2277            positional arguments:
2278              a
2279              z
2280
2281            optional arguments:
2282              -h, --help  show this help message and exit
2283              -b B
2284              --w W
2285
2286            c:
2287              --d D
2288
2289            x:
2290              -y Y
2291        '''.format(progname, ' ' if progname else '' )))
2292
2293    def test_groups_parents(self):
2294        parent = ErrorRaisingArgumentParser(add_help=False)
2295        g = parent.add_argument_group(title='g', description='gd')
2296        g.add_argument('-w')
2297        g.add_argument('-x')
2298        m = parent.add_mutually_exclusive_group()
2299        m.add_argument('-y')
2300        m.add_argument('-z')
2301        parser = ErrorRaisingArgumentParser(parents=[parent])
2302
2303        self.assertRaises(ArgumentParserError, parser.parse_args,
2304            ['-y', 'Y', '-z', 'Z'])
2305
2306        parser_help = parser.format_help()
2307        progname = self.main_program
2308        self.assertEqual(parser_help, textwrap.dedent('''\
2309            usage: {}{}[-h] [-w W] [-x X] [-y Y | -z Z]
2310
2311            optional arguments:
2312              -h, --help  show this help message and exit
2313              -y Y
2314              -z Z
2315
2316            g:
2317              gd
2318
2319              -w W
2320              -x X
2321        '''.format(progname, ' ' if progname else '' )))
2322
2323# ==============================
2324# Mutually exclusive group tests
2325# ==============================
2326
2327class TestMutuallyExclusiveGroupErrors(TestCase):
2328
2329    def test_invalid_add_argument_group(self):
2330        parser = ErrorRaisingArgumentParser()
2331        raises = self.assertRaises
2332        raises(TypeError, parser.add_mutually_exclusive_group, title='foo')
2333
2334    def test_invalid_add_argument(self):
2335        parser = ErrorRaisingArgumentParser()
2336        group = parser.add_mutually_exclusive_group()
2337        add_argument = group.add_argument
2338        raises = self.assertRaises
2339        raises(ValueError, add_argument, '--foo', required=True)
2340        raises(ValueError, add_argument, 'bar')
2341        raises(ValueError, add_argument, 'bar', nargs='+')
2342        raises(ValueError, add_argument, 'bar', nargs=1)
2343        raises(ValueError, add_argument, 'bar', nargs=argparse.PARSER)
2344
2345    def test_help(self):
2346        parser = ErrorRaisingArgumentParser(prog='PROG')
2347        group1 = parser.add_mutually_exclusive_group()
2348        group1.add_argument('--foo', action='store_true')
2349        group1.add_argument('--bar', action='store_false')
2350        group2 = parser.add_mutually_exclusive_group()
2351        group2.add_argument('--soup', action='store_true')
2352        group2.add_argument('--nuts', action='store_false')
2353        expected = '''\
2354            usage: PROG [-h] [--foo | --bar] [--soup | --nuts]
2355
2356            optional arguments:
2357              -h, --help  show this help message and exit
2358              --foo
2359              --bar
2360              --soup
2361              --nuts
2362              '''
2363        self.assertEqual(parser.format_help(), textwrap.dedent(expected))
2364
2365class MEMixin(object):
2366
2367    def test_failures_when_not_required(self):
2368        parse_args = self.get_parser(required=False).parse_args
2369        error = ArgumentParserError
2370        for args_string in self.failures:
2371            self.assertRaises(error, parse_args, args_string.split())
2372
2373    def test_failures_when_required(self):
2374        parse_args = self.get_parser(required=True).parse_args
2375        error = ArgumentParserError
2376        for args_string in self.failures + ['']:
2377            self.assertRaises(error, parse_args, args_string.split())
2378
2379    def test_successes_when_not_required(self):
2380        parse_args = self.get_parser(required=False).parse_args
2381        successes = self.successes + self.successes_when_not_required
2382        for args_string, expected_ns in successes:
2383            actual_ns = parse_args(args_string.split())
2384            self.assertEqual(actual_ns, expected_ns)
2385
2386    def test_successes_when_required(self):
2387        parse_args = self.get_parser(required=True).parse_args
2388        for args_string, expected_ns in self.successes:
2389            actual_ns = parse_args(args_string.split())
2390            self.assertEqual(actual_ns, expected_ns)
2391
2392    def test_usage_when_not_required(self):
2393        format_usage = self.get_parser(required=False).format_usage
2394        expected_usage = self.usage_when_not_required
2395        self.assertEqual(format_usage(), textwrap.dedent(expected_usage))
2396
2397    def test_usage_when_required(self):
2398        format_usage = self.get_parser(required=True).format_usage
2399        expected_usage = self.usage_when_required
2400        self.assertEqual(format_usage(), textwrap.dedent(expected_usage))
2401
2402    def test_help_when_not_required(self):
2403        format_help = self.get_parser(required=False).format_help
2404        help = self.usage_when_not_required + self.help
2405        self.assertEqual(format_help(), textwrap.dedent(help))
2406
2407    def test_help_when_required(self):
2408        format_help = self.get_parser(required=True).format_help
2409        help = self.usage_when_required + self.help
2410        self.assertEqual(format_help(), textwrap.dedent(help))
2411
2412
2413class TestMutuallyExclusiveSimple(MEMixin, TestCase):
2414
2415    def get_parser(self, required=None):
2416        parser = ErrorRaisingArgumentParser(prog='PROG')
2417        group = parser.add_mutually_exclusive_group(required=required)
2418        group.add_argument('--bar', help='bar help')
2419        group.add_argument('--baz', nargs='?', const='Z', help='baz help')
2420        return parser
2421
2422    failures = ['--bar X --baz Y', '--bar X --baz']
2423    successes = [
2424        ('--bar X', NS(bar='X', baz=None)),
2425        ('--bar X --bar Z', NS(bar='Z', baz=None)),
2426        ('--baz Y', NS(bar=None, baz='Y')),
2427        ('--baz', NS(bar=None, baz='Z')),
2428    ]
2429    successes_when_not_required = [
2430        ('', NS(bar=None, baz=None)),
2431    ]
2432
2433    usage_when_not_required = '''\
2434        usage: PROG [-h] [--bar BAR | --baz [BAZ]]
2435        '''
2436    usage_when_required = '''\
2437        usage: PROG [-h] (--bar BAR | --baz [BAZ])
2438        '''
2439    help = '''\
2440
2441        optional arguments:
2442          -h, --help   show this help message and exit
2443          --bar BAR    bar help
2444          --baz [BAZ]  baz help
2445        '''
2446
2447
2448class TestMutuallyExclusiveLong(MEMixin, TestCase):
2449
2450    def get_parser(self, required=None):
2451        parser = ErrorRaisingArgumentParser(prog='PROG')
2452        parser.add_argument('--abcde', help='abcde help')
2453        parser.add_argument('--fghij', help='fghij help')
2454        group = parser.add_mutually_exclusive_group(required=required)
2455        group.add_argument('--klmno', help='klmno help')
2456        group.add_argument('--pqrst', help='pqrst help')
2457        return parser
2458
2459    failures = ['--klmno X --pqrst Y']
2460    successes = [
2461        ('--klmno X', NS(abcde=None, fghij=None, klmno='X', pqrst=None)),
2462        ('--abcde Y --klmno X',
2463            NS(abcde='Y', fghij=None, klmno='X', pqrst=None)),
2464        ('--pqrst X', NS(abcde=None, fghij=None, klmno=None, pqrst='X')),
2465        ('--pqrst X --fghij Y',
2466            NS(abcde=None, fghij='Y', klmno=None, pqrst='X')),
2467    ]
2468    successes_when_not_required = [
2469        ('', NS(abcde=None, fghij=None, klmno=None, pqrst=None)),
2470    ]
2471
2472    usage_when_not_required = '''\
2473    usage: PROG [-h] [--abcde ABCDE] [--fghij FGHIJ]
2474                [--klmno KLMNO | --pqrst PQRST]
2475    '''
2476    usage_when_required = '''\
2477    usage: PROG [-h] [--abcde ABCDE] [--fghij FGHIJ]
2478                (--klmno KLMNO | --pqrst PQRST)
2479    '''
2480    help = '''\
2481
2482    optional arguments:
2483      -h, --help     show this help message and exit
2484      --abcde ABCDE  abcde help
2485      --fghij FGHIJ  fghij help
2486      --klmno KLMNO  klmno help
2487      --pqrst PQRST  pqrst help
2488    '''
2489
2490
2491class TestMutuallyExclusiveFirstSuppressed(MEMixin, TestCase):
2492
2493    def get_parser(self, required):
2494        parser = ErrorRaisingArgumentParser(prog='PROG')
2495        group = parser.add_mutually_exclusive_group(required=required)
2496        group.add_argument('-x', help=argparse.SUPPRESS)
2497        group.add_argument('-y', action='store_false', help='y help')
2498        return parser
2499
2500    failures = ['-x X -y']
2501    successes = [
2502        ('-x X', NS(x='X', y=True)),
2503        ('-x X -x Y', NS(x='Y', y=True)),
2504        ('-y', NS(x=None, y=False)),
2505    ]
2506    successes_when_not_required = [
2507        ('', NS(x=None, y=True)),
2508    ]
2509
2510    usage_when_not_required = '''\
2511        usage: PROG [-h] [-y]
2512        '''
2513    usage_when_required = '''\
2514        usage: PROG [-h] -y
2515        '''
2516    help = '''\
2517
2518        optional arguments:
2519          -h, --help  show this help message and exit
2520          -y          y help
2521        '''
2522
2523
2524class TestMutuallyExclusiveManySuppressed(MEMixin, TestCase):
2525
2526    def get_parser(self, required):
2527        parser = ErrorRaisingArgumentParser(prog='PROG')
2528        group = parser.add_mutually_exclusive_group(required=required)
2529        add = group.add_argument
2530        add('--spam', action='store_true', help=argparse.SUPPRESS)
2531        add('--badger', action='store_false', help=argparse.SUPPRESS)
2532        add('--bladder', help=argparse.SUPPRESS)
2533        return parser
2534
2535    failures = [
2536        '--spam --badger',
2537        '--badger --bladder B',
2538        '--bladder B --spam',
2539    ]
2540    successes = [
2541        ('--spam', NS(spam=True, badger=True, bladder=None)),
2542        ('--badger', NS(spam=False, badger=False, bladder=None)),
2543        ('--bladder B', NS(spam=False, badger=True, bladder='B')),
2544        ('--spam --spam', NS(spam=True, badger=True, bladder=None)),
2545    ]
2546    successes_when_not_required = [
2547        ('', NS(spam=False, badger=True, bladder=None)),
2548    ]
2549
2550    usage_when_required = usage_when_not_required = '''\
2551        usage: PROG [-h]
2552        '''
2553    help = '''\
2554
2555        optional arguments:
2556          -h, --help  show this help message and exit
2557        '''
2558
2559
2560class TestMutuallyExclusiveOptionalAndPositional(MEMixin, TestCase):
2561
2562    def get_parser(self, required):
2563        parser = ErrorRaisingArgumentParser(prog='PROG')
2564        group = parser.add_mutually_exclusive_group(required=required)
2565        group.add_argument('--foo', action='store_true', help='FOO')
2566        group.add_argument('--spam', help='SPAM')
2567        group.add_argument('badger', nargs='*', default='X', help='BADGER')
2568        return parser
2569
2570    failures = [
2571        '--foo --spam S',
2572        '--spam S X',
2573        'X --foo',
2574        'X Y Z --spam S',
2575        '--foo X Y',
2576    ]
2577    successes = [
2578        ('--foo', NS(foo=True, spam=None, badger='X')),
2579        ('--spam S', NS(foo=False, spam='S', badger='X')),
2580        ('X', NS(foo=False, spam=None, badger=['X'])),
2581        ('X Y Z', NS(foo=False, spam=None, badger=['X', 'Y', 'Z'])),
2582    ]
2583    successes_when_not_required = [
2584        ('', NS(foo=False, spam=None, badger='X')),
2585    ]
2586
2587    usage_when_not_required = '''\
2588        usage: PROG [-h] [--foo | --spam SPAM | badger [badger ...]]
2589        '''
2590    usage_when_required = '''\
2591        usage: PROG [-h] (--foo | --spam SPAM | badger [badger ...])
2592        '''
2593    help = '''\
2594
2595        positional arguments:
2596          badger       BADGER
2597
2598        optional arguments:
2599          -h, --help   show this help message and exit
2600          --foo        FOO
2601          --spam SPAM  SPAM
2602        '''
2603
2604
2605class TestMutuallyExclusiveOptionalsMixed(MEMixin, TestCase):
2606
2607    def get_parser(self, required):
2608        parser = ErrorRaisingArgumentParser(prog='PROG')
2609        parser.add_argument('-x', action='store_true', help='x help')
2610        group = parser.add_mutually_exclusive_group(required=required)
2611        group.add_argument('-a', action='store_true', help='a help')
2612        group.add_argument('-b', action='store_true', help='b help')
2613        parser.add_argument('-y', action='store_true', help='y help')
2614        group.add_argument('-c', action='store_true', help='c help')
2615        return parser
2616
2617    failures = ['-a -b', '-b -c', '-a -c', '-a -b -c']
2618    successes = [
2619        ('-a', NS(a=True, b=False, c=False, x=False, y=False)),
2620        ('-b', NS(a=False, b=True, c=False, x=False, y=False)),
2621        ('-c', NS(a=False, b=False, c=True, x=False, y=False)),
2622        ('-a -x', NS(a=True, b=False, c=False, x=True, y=False)),
2623        ('-y -b', NS(a=False, b=True, c=False, x=False, y=True)),
2624        ('-x -y -c', NS(a=False, b=False, c=True, x=True, y=True)),
2625    ]
2626    successes_when_not_required = [
2627        ('', NS(a=False, b=False, c=False, x=False, y=False)),
2628        ('-x', NS(a=False, b=False, c=False, x=True, y=False)),
2629        ('-y', NS(a=False, b=False, c=False, x=False, y=True)),
2630    ]
2631
2632    usage_when_required = usage_when_not_required = '''\
2633        usage: PROG [-h] [-x] [-a] [-b] [-y] [-c]
2634        '''
2635    help = '''\
2636
2637        optional arguments:
2638          -h, --help  show this help message and exit
2639          -x          x help
2640          -a          a help
2641          -b          b help
2642          -y          y help
2643          -c          c help
2644        '''
2645
2646
2647class TestMutuallyExclusiveInGroup(MEMixin, TestCase):
2648
2649    def get_parser(self, required=None):
2650        parser = ErrorRaisingArgumentParser(prog='PROG')
2651        titled_group = parser.add_argument_group(
2652            title='Titled group', description='Group description')
2653        mutex_group = \
2654            titled_group.add_mutually_exclusive_group(required=required)
2655        mutex_group.add_argument('--bar', help='bar help')
2656        mutex_group.add_argument('--baz', help='baz help')
2657        return parser
2658
2659    failures = ['--bar X --baz Y', '--baz X --bar Y']
2660    successes = [
2661        ('--bar X', NS(bar='X', baz=None)),
2662        ('--baz Y', NS(bar=None, baz='Y')),
2663    ]
2664    successes_when_not_required = [
2665        ('', NS(bar=None, baz=None)),
2666    ]
2667
2668    usage_when_not_required = '''\
2669        usage: PROG [-h] [--bar BAR | --baz BAZ]
2670        '''
2671    usage_when_required = '''\
2672        usage: PROG [-h] (--bar BAR | --baz BAZ)
2673        '''
2674    help = '''\
2675
2676        optional arguments:
2677          -h, --help  show this help message and exit
2678
2679        Titled group:
2680          Group description
2681
2682          --bar BAR   bar help
2683          --baz BAZ   baz help
2684        '''
2685
2686
2687class TestMutuallyExclusiveOptionalsAndPositionalsMixed(MEMixin, TestCase):
2688
2689    def get_parser(self, required):
2690        parser = ErrorRaisingArgumentParser(prog='PROG')
2691        parser.add_argument('x', help='x help')
2692        parser.add_argument('-y', action='store_true', help='y help')
2693        group = parser.add_mutually_exclusive_group(required=required)
2694        group.add_argument('a', nargs='?', help='a help')
2695        group.add_argument('-b', action='store_true', help='b help')
2696        group.add_argument('-c', action='store_true', help='c help')
2697        return parser
2698
2699    failures = ['X A -b', '-b -c', '-c X A']
2700    successes = [
2701        ('X A', NS(a='A', b=False, c=False, x='X', y=False)),
2702        ('X -b', NS(a=None, b=True, c=False, x='X', y=False)),
2703        ('X -c', NS(a=None, b=False, c=True, x='X', y=False)),
2704        ('X A -y', NS(a='A', b=False, c=False, x='X', y=True)),
2705        ('X -y -b', NS(a=None, b=True, c=False, x='X', y=True)),
2706    ]
2707    successes_when_not_required = [
2708        ('X', NS(a=None, b=False, c=False, x='X', y=False)),
2709        ('X -y', NS(a=None, b=False, c=False, x='X', y=True)),
2710    ]
2711
2712    usage_when_required = usage_when_not_required = '''\
2713        usage: PROG [-h] [-y] [-b] [-c] x [a]
2714        '''
2715    help = '''\
2716
2717        positional arguments:
2718          x           x help
2719          a           a help
2720
2721        optional arguments:
2722          -h, --help  show this help message and exit
2723          -y          y help
2724          -b          b help
2725          -c          c help
2726        '''
2727
2728# =================================================
2729# Mutually exclusive group in parent parser tests
2730# =================================================
2731
2732class MEPBase(object):
2733
2734    def get_parser(self, required=None):
2735        parent = super(MEPBase, self).get_parser(required=required)
2736        parser = ErrorRaisingArgumentParser(
2737            prog=parent.prog, add_help=False, parents=[parent])
2738        return parser
2739
2740
2741class TestMutuallyExclusiveGroupErrorsParent(
2742    MEPBase, TestMutuallyExclusiveGroupErrors):
2743    pass
2744
2745
2746class TestMutuallyExclusiveSimpleParent(
2747    MEPBase, TestMutuallyExclusiveSimple):
2748    pass
2749
2750
2751class TestMutuallyExclusiveLongParent(
2752    MEPBase, TestMutuallyExclusiveLong):
2753    pass
2754
2755
2756class TestMutuallyExclusiveFirstSuppressedParent(
2757    MEPBase, TestMutuallyExclusiveFirstSuppressed):
2758    pass
2759
2760
2761class TestMutuallyExclusiveManySuppressedParent(
2762    MEPBase, TestMutuallyExclusiveManySuppressed):
2763    pass
2764
2765
2766class TestMutuallyExclusiveOptionalAndPositionalParent(
2767    MEPBase, TestMutuallyExclusiveOptionalAndPositional):
2768    pass
2769
2770
2771class TestMutuallyExclusiveOptionalsMixedParent(
2772    MEPBase, TestMutuallyExclusiveOptionalsMixed):
2773    pass
2774
2775
2776class TestMutuallyExclusiveOptionalsAndPositionalsMixedParent(
2777    MEPBase, TestMutuallyExclusiveOptionalsAndPositionalsMixed):
2778    pass
2779
2780# =================
2781# Set default tests
2782# =================
2783
2784class TestSetDefaults(TestCase):
2785
2786    def test_set_defaults_no_args(self):
2787        parser = ErrorRaisingArgumentParser()
2788        parser.set_defaults(x='foo')
2789        parser.set_defaults(y='bar', z=1)
2790        self.assertEqual(NS(x='foo', y='bar', z=1),
2791                         parser.parse_args([]))
2792        self.assertEqual(NS(x='foo', y='bar', z=1),
2793                         parser.parse_args([], NS()))
2794        self.assertEqual(NS(x='baz', y='bar', z=1),
2795                         parser.parse_args([], NS(x='baz')))
2796        self.assertEqual(NS(x='baz', y='bar', z=2),
2797                         parser.parse_args([], NS(x='baz', z=2)))
2798
2799    def test_set_defaults_with_args(self):
2800        parser = ErrorRaisingArgumentParser()
2801        parser.set_defaults(x='foo', y='bar')
2802        parser.add_argument('-x', default='xfoox')
2803        self.assertEqual(NS(x='xfoox', y='bar'),
2804                         parser.parse_args([]))
2805        self.assertEqual(NS(x='xfoox', y='bar'),
2806                         parser.parse_args([], NS()))
2807        self.assertEqual(NS(x='baz', y='bar'),
2808                         parser.parse_args([], NS(x='baz')))
2809        self.assertEqual(NS(x='1', y='bar'),
2810                         parser.parse_args('-x 1'.split()))
2811        self.assertEqual(NS(x='1', y='bar'),
2812                         parser.parse_args('-x 1'.split(), NS()))
2813        self.assertEqual(NS(x='1', y='bar'),
2814                         parser.parse_args('-x 1'.split(), NS(x='baz')))
2815
2816    def test_set_defaults_subparsers(self):
2817        parser = ErrorRaisingArgumentParser()
2818        parser.set_defaults(x='foo')
2819        subparsers = parser.add_subparsers()
2820        parser_a = subparsers.add_parser('a')
2821        parser_a.set_defaults(y='bar')
2822        self.assertEqual(NS(x='foo', y='bar'),
2823                         parser.parse_args('a'.split()))
2824
2825    def test_set_defaults_parents(self):
2826        parent = ErrorRaisingArgumentParser(add_help=False)
2827        parent.set_defaults(x='foo')
2828        parser = ErrorRaisingArgumentParser(parents=[parent])
2829        self.assertEqual(NS(x='foo'), parser.parse_args([]))
2830
2831    def test_set_defaults_on_parent_and_subparser(self):
2832        parser = argparse.ArgumentParser()
2833        xparser = parser.add_subparsers().add_parser('X')
2834        parser.set_defaults(foo=1)
2835        xparser.set_defaults(foo=2)
2836        self.assertEqual(NS(foo=2), parser.parse_args(['X']))
2837
2838    def test_set_defaults_same_as_add_argument(self):
2839        parser = ErrorRaisingArgumentParser()
2840        parser.set_defaults(w='W', x='X', y='Y', z='Z')
2841        parser.add_argument('-w')
2842        parser.add_argument('-x', default='XX')
2843        parser.add_argument('y', nargs='?')
2844        parser.add_argument('z', nargs='?', default='ZZ')
2845
2846        # defaults set previously
2847        self.assertEqual(NS(w='W', x='XX', y='Y', z='ZZ'),
2848                         parser.parse_args([]))
2849
2850        # reset defaults
2851        parser.set_defaults(w='WW', x='X', y='YY', z='Z')
2852        self.assertEqual(NS(w='WW', x='X', y='YY', z='Z'),
2853                         parser.parse_args([]))
2854
2855    def test_set_defaults_same_as_add_argument_group(self):
2856        parser = ErrorRaisingArgumentParser()
2857        parser.set_defaults(w='W', x='X', y='Y', z='Z')
2858        group = parser.add_argument_group('foo')
2859        group.add_argument('-w')
2860        group.add_argument('-x', default='XX')
2861        group.add_argument('y', nargs='?')
2862        group.add_argument('z', nargs='?', default='ZZ')
2863
2864
2865        # defaults set previously
2866        self.assertEqual(NS(w='W', x='XX', y='Y', z='ZZ'),
2867                         parser.parse_args([]))
2868
2869        # reset defaults
2870        parser.set_defaults(w='WW', x='X', y='YY', z='Z')
2871        self.assertEqual(NS(w='WW', x='X', y='YY', z='Z'),
2872                         parser.parse_args([]))
2873
2874# =================
2875# Get default tests
2876# =================
2877
2878class TestGetDefault(TestCase):
2879
2880    def test_get_default(self):
2881        parser = ErrorRaisingArgumentParser()
2882        self.assertIsNone(parser.get_default("foo"))
2883        self.assertIsNone(parser.get_default("bar"))
2884
2885        parser.add_argument("--foo")
2886        self.assertIsNone(parser.get_default("foo"))
2887        self.assertIsNone(parser.get_default("bar"))
2888
2889        parser.add_argument("--bar", type=int, default=42)
2890        self.assertIsNone(parser.get_default("foo"))
2891        self.assertEqual(42, parser.get_default("bar"))
2892
2893        parser.set_defaults(foo="badger")
2894        self.assertEqual("badger", parser.get_default("foo"))
2895        self.assertEqual(42, parser.get_default("bar"))
2896
2897# ==========================
2898# Namespace 'contains' tests
2899# ==========================
2900
2901class TestNamespaceContainsSimple(TestCase):
2902
2903    def test_empty(self):
2904        ns = argparse.Namespace()
2905        self.assertNotIn('', ns)
2906        self.assertNotIn('x', ns)
2907
2908    def test_non_empty(self):
2909        ns = argparse.Namespace(x=1, y=2)
2910        self.assertNotIn('', ns)
2911        self.assertIn('x', ns)
2912        self.assertIn('y', ns)
2913        self.assertNotIn('xx', ns)
2914        self.assertNotIn('z', ns)
2915
2916# =====================
2917# Help formatting tests
2918# =====================
2919
2920class TestHelpFormattingMetaclass(type):
2921
2922    def __init__(cls, name, bases, bodydict):
2923        if name == 'HelpTestCase':
2924            return
2925
2926        class AddTests(object):
2927
2928            def __init__(self, test_class, func_suffix, std_name):
2929                self.func_suffix = func_suffix
2930                self.std_name = std_name
2931
2932                for test_func in [self.test_format,
2933                                  self.test_print,
2934                                  self.test_print_file]:
2935                    test_name = '%s_%s' % (test_func.__name__, func_suffix)
2936
2937                    def test_wrapper(self, test_func=test_func):
2938                        test_func(self)
2939                    try:
2940                        test_wrapper.__name__ = test_name
2941                    except TypeError:
2942                        pass
2943                    setattr(test_class, test_name, test_wrapper)
2944
2945            def _get_parser(self, tester):
2946                parser = argparse.ArgumentParser(
2947                    *tester.parser_signature.args,
2948                    **tester.parser_signature.kwargs)
2949                for argument_sig in getattr(tester, 'argument_signatures', []):
2950                    parser.add_argument(*argument_sig.args,
2951                                        **argument_sig.kwargs)
2952                group_sigs = getattr(tester, 'argument_group_signatures', [])
2953                for group_sig, argument_sigs in group_sigs:
2954                    group = parser.add_argument_group(*group_sig.args,
2955                                                      **group_sig.kwargs)
2956                    for argument_sig in argument_sigs:
2957                        group.add_argument(*argument_sig.args,
2958                                           **argument_sig.kwargs)
2959                subparsers_sigs = getattr(tester, 'subparsers_signatures', [])
2960                if subparsers_sigs:
2961                    subparsers = parser.add_subparsers()
2962                    for subparser_sig in subparsers_sigs:
2963                        subparsers.add_parser(*subparser_sig.args,
2964                                               **subparser_sig.kwargs)
2965                return parser
2966
2967            def _test(self, tester, parser_text):
2968                expected_text = getattr(tester, self.func_suffix)
2969                expected_text = textwrap.dedent(expected_text)
2970                tester.assertEqual(expected_text, parser_text)
2971
2972            def test_format(self, tester):
2973                parser = self._get_parser(tester)
2974                format = getattr(parser, 'format_%s' % self.func_suffix)
2975                self._test(tester, format())
2976
2977            def test_print(self, tester):
2978                parser = self._get_parser(tester)
2979                print_ = getattr(parser, 'print_%s' % self.func_suffix)
2980                old_stream = getattr(sys, self.std_name)
2981                setattr(sys, self.std_name, StdIOBuffer())
2982                try:
2983                    print_()
2984                    parser_text = getattr(sys, self.std_name).getvalue()
2985                finally:
2986                    setattr(sys, self.std_name, old_stream)
2987                self._test(tester, parser_text)
2988
2989            def test_print_file(self, tester):
2990                parser = self._get_parser(tester)
2991                print_ = getattr(parser, 'print_%s' % self.func_suffix)
2992                sfile = StdIOBuffer()
2993                print_(sfile)
2994                parser_text = sfile.getvalue()
2995                self._test(tester, parser_text)
2996
2997        # add tests for {format,print}_{usage,help}
2998        for func_suffix, std_name in [('usage', 'stdout'),
2999                                      ('help', 'stdout')]:
3000            AddTests(cls, func_suffix, std_name)
3001
3002bases = TestCase,
3003HelpTestCase = TestHelpFormattingMetaclass('HelpTestCase', bases, {})
3004
3005
3006class TestHelpBiggerOptionals(HelpTestCase):
3007    """Make sure that argument help aligns when options are longer"""
3008
3009    parser_signature = Sig(prog='PROG', description='DESCRIPTION',
3010                           epilog='EPILOG')
3011    argument_signatures = [
3012        Sig('-v', '--version', action='version', version='0.1'),
3013        Sig('-x', action='store_true', help='X HELP'),
3014        Sig('--y', help='Y HELP'),
3015        Sig('foo', help='FOO HELP'),
3016        Sig('bar', help='BAR HELP'),
3017    ]
3018    argument_group_signatures = []
3019    usage = '''\
3020        usage: PROG [-h] [-v] [-x] [--y Y] foo bar
3021        '''
3022    help = usage + '''\
3023
3024        DESCRIPTION
3025
3026        positional arguments:
3027          foo            FOO HELP
3028          bar            BAR HELP
3029
3030        optional arguments:
3031          -h, --help     show this help message and exit
3032          -v, --version  show program's version number and exit
3033          -x             X HELP
3034          --y Y          Y HELP
3035
3036        EPILOG
3037    '''
3038    version = '''\
3039        0.1
3040        '''
3041
3042class TestShortColumns(HelpTestCase):
3043    '''Test extremely small number of columns.
3044
3045    TestCase prevents "COLUMNS" from being too small in the tests themselves,
3046    but we don't want any exceptions thrown in such cases. Only ugly representation.
3047    '''
3048    def setUp(self):
3049        env = support.EnvironmentVarGuard()
3050        env.set("COLUMNS", '15')
3051        self.addCleanup(env.__exit__)
3052
3053    parser_signature            = TestHelpBiggerOptionals.parser_signature
3054    argument_signatures         = TestHelpBiggerOptionals.argument_signatures
3055    argument_group_signatures   = TestHelpBiggerOptionals.argument_group_signatures
3056    usage = '''\
3057        usage: PROG
3058               [-h]
3059               [-v]
3060               [-x]
3061               [--y Y]
3062               foo
3063               bar
3064        '''
3065    help = usage + '''\
3066
3067        DESCRIPTION
3068
3069        positional arguments:
3070          foo
3071            FOO HELP
3072          bar
3073            BAR HELP
3074
3075        optional arguments:
3076          -h, --help
3077            show this
3078            help
3079            message and
3080            exit
3081          -v, --version
3082            show
3083            program's
3084            version
3085            number and
3086            exit
3087          -x
3088            X HELP
3089          --y Y
3090            Y HELP
3091
3092        EPILOG
3093    '''
3094    version                     = TestHelpBiggerOptionals.version
3095
3096
3097class TestHelpBiggerOptionalGroups(HelpTestCase):
3098    """Make sure that argument help aligns when options are longer"""
3099
3100    parser_signature = Sig(prog='PROG', description='DESCRIPTION',
3101                           epilog='EPILOG')
3102    argument_signatures = [
3103        Sig('-v', '--version', action='version', version='0.1'),
3104        Sig('-x', action='store_true', help='X HELP'),
3105        Sig('--y', help='Y HELP'),
3106        Sig('foo', help='FOO HELP'),
3107        Sig('bar', help='BAR HELP'),
3108    ]
3109    argument_group_signatures = [
3110        (Sig('GROUP TITLE', description='GROUP DESCRIPTION'), [
3111            Sig('baz', help='BAZ HELP'),
3112            Sig('-z', nargs='+', help='Z HELP')]),
3113    ]
3114    usage = '''\
3115        usage: PROG [-h] [-v] [-x] [--y Y] [-z Z [Z ...]] foo bar baz
3116        '''
3117    help = usage + '''\
3118
3119        DESCRIPTION
3120
3121        positional arguments:
3122          foo            FOO HELP
3123          bar            BAR HELP
3124
3125        optional arguments:
3126          -h, --help     show this help message and exit
3127          -v, --version  show program's version number and exit
3128          -x             X HELP
3129          --y Y          Y HELP
3130
3131        GROUP TITLE:
3132          GROUP DESCRIPTION
3133
3134          baz            BAZ HELP
3135          -z Z [Z ...]   Z HELP
3136
3137        EPILOG
3138    '''
3139    version = '''\
3140        0.1
3141        '''
3142
3143
3144class TestHelpBiggerPositionals(HelpTestCase):
3145    """Make sure that help aligns when arguments are longer"""
3146
3147    parser_signature = Sig(usage='USAGE', description='DESCRIPTION')
3148    argument_signatures = [
3149        Sig('-x', action='store_true', help='X HELP'),
3150        Sig('--y', help='Y HELP'),
3151        Sig('ekiekiekifekang', help='EKI HELP'),
3152        Sig('bar', help='BAR HELP'),
3153    ]
3154    argument_group_signatures = []
3155    usage = '''\
3156        usage: USAGE
3157        '''
3158    help = usage + '''\
3159
3160        DESCRIPTION
3161
3162        positional arguments:
3163          ekiekiekifekang  EKI HELP
3164          bar              BAR HELP
3165
3166        optional arguments:
3167          -h, --help       show this help message and exit
3168          -x               X HELP
3169          --y Y            Y HELP
3170        '''
3171
3172    version = ''
3173
3174
3175class TestHelpReformatting(HelpTestCase):
3176    """Make sure that text after short names starts on the first line"""
3177
3178    parser_signature = Sig(
3179        prog='PROG',
3180        description='   oddly    formatted\n'
3181                    'description\n'
3182                    '\n'
3183                    'that is so long that it should go onto multiple '
3184                    'lines when wrapped')
3185    argument_signatures = [
3186        Sig('-x', metavar='XX', help='oddly\n'
3187                                     '    formatted -x help'),
3188        Sig('y', metavar='yyy', help='normal y help'),
3189    ]
3190    argument_group_signatures = [
3191        (Sig('title', description='\n'
3192                                  '    oddly formatted group\n'
3193                                  '\n'
3194                                  'description'),
3195         [Sig('-a', action='store_true',
3196              help=' oddly \n'
3197                   'formatted    -a  help  \n'
3198                   '    again, so long that it should be wrapped over '
3199                   'multiple lines')]),
3200    ]
3201    usage = '''\
3202        usage: PROG [-h] [-x XX] [-a] yyy
3203        '''
3204    help = usage + '''\
3205
3206        oddly formatted description that is so long that it should go onto \
3207multiple
3208        lines when wrapped
3209
3210        positional arguments:
3211          yyy         normal y help
3212
3213        optional arguments:
3214          -h, --help  show this help message and exit
3215          -x XX       oddly formatted -x help
3216
3217        title:
3218          oddly formatted group description
3219
3220          -a          oddly formatted -a help again, so long that it should \
3221be wrapped
3222                      over multiple lines
3223        '''
3224    version = ''
3225
3226
3227class TestHelpWrappingShortNames(HelpTestCase):
3228    """Make sure that text after short names starts on the first line"""
3229
3230    parser_signature = Sig(prog='PROG', description= 'D\nD' * 30)
3231    argument_signatures = [
3232        Sig('-x', metavar='XX', help='XHH HX' * 20),
3233        Sig('y', metavar='yyy', help='YH YH' * 20),
3234    ]
3235    argument_group_signatures = [
3236        (Sig('ALPHAS'), [
3237            Sig('-a', action='store_true', help='AHHH HHA' * 10)]),
3238    ]
3239    usage = '''\
3240        usage: PROG [-h] [-x XX] [-a] yyy
3241        '''
3242    help = usage + '''\
3243
3244        D DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD \
3245DD DD DD
3246        DD DD DD DD D
3247
3248        positional arguments:
3249          yyy         YH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH \
3250YHYH YHYH
3251                      YHYH YHYH YHYH YHYH YHYH YHYH YHYH YH
3252
3253        optional arguments:
3254          -h, --help  show this help message and exit
3255          -x XX       XHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH \
3256HXXHH HXXHH
3257                      HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HX
3258
3259        ALPHAS:
3260          -a          AHHH HHAAHHH HHAAHHH HHAAHHH HHAAHHH HHAAHHH HHAAHHH \
3261HHAAHHH
3262                      HHAAHHH HHAAHHH HHA
3263        '''
3264    version = ''
3265
3266
3267class TestHelpWrappingLongNames(HelpTestCase):
3268    """Make sure that text after long names starts on the next line"""
3269
3270    parser_signature = Sig(usage='USAGE', description= 'D D' * 30)
3271    argument_signatures = [
3272        Sig('-v', '--version', action='version', version='V V' * 30),
3273        Sig('-x', metavar='X' * 25, help='XH XH' * 20),
3274        Sig('y', metavar='y' * 25, help='YH YH' * 20),
3275    ]
3276    argument_group_signatures = [
3277        (Sig('ALPHAS'), [
3278            Sig('-a', metavar='A' * 25, help='AH AH' * 20),
3279            Sig('z', metavar='z' * 25, help='ZH ZH' * 20)]),
3280    ]
3281    usage = '''\
3282        usage: USAGE
3283        '''
3284    help = usage + '''\
3285
3286        D DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD \
3287DD DD DD
3288        DD DD DD DD D
3289
3290        positional arguments:
3291          yyyyyyyyyyyyyyyyyyyyyyyyy
3292                                YH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH \
3293YHYH YHYH
3294                                YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YH
3295
3296        optional arguments:
3297          -h, --help            show this help message and exit
3298          -v, --version         show program's version number and exit
3299          -x XXXXXXXXXXXXXXXXXXXXXXXXX
3300                                XH XHXH XHXH XHXH XHXH XHXH XHXH XHXH XHXH \
3301XHXH XHXH
3302                                XHXH XHXH XHXH XHXH XHXH XHXH XHXH XHXH XHXH XH
3303
3304        ALPHAS:
3305          -a AAAAAAAAAAAAAAAAAAAAAAAAA
3306                                AH AHAH AHAH AHAH AHAH AHAH AHAH AHAH AHAH \
3307AHAH AHAH
3308                                AHAH AHAH AHAH AHAH AHAH AHAH AHAH AHAH AHAH AH
3309          zzzzzzzzzzzzzzzzzzzzzzzzz
3310                                ZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH \
3311ZHZH ZHZH
3312                                ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZH
3313        '''
3314    version = '''\
3315        V VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV \
3316VV VV VV
3317        VV VV VV VV V
3318        '''
3319
3320
3321class TestHelpUsage(HelpTestCase):
3322    """Test basic usage messages"""
3323
3324    parser_signature = Sig(prog='PROG')
3325    argument_signatures = [
3326        Sig('-w', nargs='+', help='w'),
3327        Sig('-x', nargs='*', help='x'),
3328        Sig('a', help='a'),
3329        Sig('b', help='b', nargs=2),
3330        Sig('c', help='c', nargs='?'),
3331    ]
3332    argument_group_signatures = [
3333        (Sig('group'), [
3334            Sig('-y', nargs='?', help='y'),
3335            Sig('-z', nargs=3, help='z'),
3336            Sig('d', help='d', nargs='*'),
3337            Sig('e', help='e', nargs='+'),
3338        ])
3339    ]
3340    usage = '''\
3341        usage: PROG [-h] [-w W [W ...]] [-x [X [X ...]]] [-y [Y]] [-z Z Z Z]
3342                    a b b [c] [d [d ...]] e [e ...]
3343        '''
3344    help = usage + '''\
3345
3346        positional arguments:
3347          a               a
3348          b               b
3349          c               c
3350
3351        optional arguments:
3352          -h, --help      show this help message and exit
3353          -w W [W ...]    w
3354          -x [X [X ...]]  x
3355
3356        group:
3357          -y [Y]          y
3358          -z Z Z Z        z
3359          d               d
3360          e               e
3361        '''
3362    version = ''
3363
3364
3365class TestHelpOnlyUserGroups(HelpTestCase):
3366    """Test basic usage messages"""
3367
3368    parser_signature = Sig(prog='PROG', add_help=False)
3369    argument_signatures = []
3370    argument_group_signatures = [
3371        (Sig('xxxx'), [
3372            Sig('-x', help='x'),
3373            Sig('a', help='a'),
3374        ]),
3375        (Sig('yyyy'), [
3376            Sig('b', help='b'),
3377            Sig('-y', help='y'),
3378        ]),
3379    ]
3380    usage = '''\
3381        usage: PROG [-x X] [-y Y] a b
3382        '''
3383    help = usage + '''\
3384
3385        xxxx:
3386          -x X  x
3387          a     a
3388
3389        yyyy:
3390          b     b
3391          -y Y  y
3392        '''
3393    version = ''
3394
3395
3396class TestHelpUsageLongProg(HelpTestCase):
3397    """Test usage messages where the prog is long"""
3398
3399    parser_signature = Sig(prog='P' * 60)
3400    argument_signatures = [
3401        Sig('-w', metavar='W'),
3402        Sig('-x', metavar='X'),
3403        Sig('a'),
3404        Sig('b'),
3405    ]
3406    argument_group_signatures = []
3407    usage = '''\
3408        usage: PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
3409               [-h] [-w W] [-x X] a b
3410        '''
3411    help = usage + '''\
3412
3413        positional arguments:
3414          a
3415          b
3416
3417        optional arguments:
3418          -h, --help  show this help message and exit
3419          -w W
3420          -x X
3421        '''
3422    version = ''
3423
3424
3425class TestHelpUsageLongProgOptionsWrap(HelpTestCase):
3426    """Test usage messages where the prog is long and the optionals wrap"""
3427
3428    parser_signature = Sig(prog='P' * 60)
3429    argument_signatures = [
3430        Sig('-w', metavar='W' * 25),
3431        Sig('-x', metavar='X' * 25),
3432        Sig('-y', metavar='Y' * 25),
3433        Sig('-z', metavar='Z' * 25),
3434        Sig('a'),
3435        Sig('b'),
3436    ]
3437    argument_group_signatures = []
3438    usage = '''\
3439        usage: PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
3440               [-h] [-w WWWWWWWWWWWWWWWWWWWWWWWWW] \
3441[-x XXXXXXXXXXXXXXXXXXXXXXXXX]
3442               [-y YYYYYYYYYYYYYYYYYYYYYYYYY] [-z ZZZZZZZZZZZZZZZZZZZZZZZZZ]
3443               a b
3444        '''
3445    help = usage + '''\
3446
3447        positional arguments:
3448          a
3449          b
3450
3451        optional arguments:
3452          -h, --help            show this help message and exit
3453          -w WWWWWWWWWWWWWWWWWWWWWWWWW
3454          -x XXXXXXXXXXXXXXXXXXXXXXXXX
3455          -y YYYYYYYYYYYYYYYYYYYYYYYYY
3456          -z ZZZZZZZZZZZZZZZZZZZZZZZZZ
3457        '''
3458    version = ''
3459
3460
3461class TestHelpUsageLongProgPositionalsWrap(HelpTestCase):
3462    """Test usage messages where the prog is long and the positionals wrap"""
3463
3464    parser_signature = Sig(prog='P' * 60, add_help=False)
3465    argument_signatures = [
3466        Sig('a' * 25),
3467        Sig('b' * 25),
3468        Sig('c' * 25),
3469    ]
3470    argument_group_signatures = []
3471    usage = '''\
3472        usage: PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
3473               aaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbb
3474               ccccccccccccccccccccccccc
3475        '''
3476    help = usage + '''\
3477
3478        positional arguments:
3479          aaaaaaaaaaaaaaaaaaaaaaaaa
3480          bbbbbbbbbbbbbbbbbbbbbbbbb
3481          ccccccccccccccccccccccccc
3482        '''
3483    version = ''
3484
3485
3486class TestHelpUsageOptionalsWrap(HelpTestCase):
3487    """Test usage messages where the optionals wrap"""
3488
3489    parser_signature = Sig(prog='PROG')
3490    argument_signatures = [
3491        Sig('-w', metavar='W' * 25),
3492        Sig('-x', metavar='X' * 25),
3493        Sig('-y', metavar='Y' * 25),
3494        Sig('-z', metavar='Z' * 25),
3495        Sig('a'),
3496        Sig('b'),
3497        Sig('c'),
3498    ]
3499    argument_group_signatures = []
3500    usage = '''\
3501        usage: PROG [-h] [-w WWWWWWWWWWWWWWWWWWWWWWWWW] \
3502[-x XXXXXXXXXXXXXXXXXXXXXXXXX]
3503                    [-y YYYYYYYYYYYYYYYYYYYYYYYYY] \
3504[-z ZZZZZZZZZZZZZZZZZZZZZZZZZ]
3505                    a b c
3506        '''
3507    help = usage + '''\
3508
3509        positional arguments:
3510          a
3511          b
3512          c
3513
3514        optional arguments:
3515          -h, --help            show this help message and exit
3516          -w WWWWWWWWWWWWWWWWWWWWWWWWW
3517          -x XXXXXXXXXXXXXXXXXXXXXXXXX
3518          -y YYYYYYYYYYYYYYYYYYYYYYYYY
3519          -z ZZZZZZZZZZZZZZZZZZZZZZZZZ
3520        '''
3521    version = ''
3522
3523
3524class TestHelpUsagePositionalsWrap(HelpTestCase):
3525    """Test usage messages where the positionals wrap"""
3526
3527    parser_signature = Sig(prog='PROG')
3528    argument_signatures = [
3529        Sig('-x'),
3530        Sig('-y'),
3531        Sig('-z'),
3532        Sig('a' * 25),
3533        Sig('b' * 25),
3534        Sig('c' * 25),
3535    ]
3536    argument_group_signatures = []
3537    usage = '''\
3538        usage: PROG [-h] [-x X] [-y Y] [-z Z]
3539                    aaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbb
3540                    ccccccccccccccccccccccccc
3541        '''
3542    help = usage + '''\
3543
3544        positional arguments:
3545          aaaaaaaaaaaaaaaaaaaaaaaaa
3546          bbbbbbbbbbbbbbbbbbbbbbbbb
3547          ccccccccccccccccccccccccc
3548
3549        optional arguments:
3550          -h, --help            show this help message and exit
3551          -x X
3552          -y Y
3553          -z Z
3554        '''
3555    version = ''
3556
3557
3558class TestHelpUsageOptionalsPositionalsWrap(HelpTestCase):
3559    """Test usage messages where the optionals and positionals wrap"""
3560
3561    parser_signature = Sig(prog='PROG')
3562    argument_signatures = [
3563        Sig('-x', metavar='X' * 25),
3564        Sig('-y', metavar='Y' * 25),
3565        Sig('-z', metavar='Z' * 25),
3566        Sig('a' * 25),
3567        Sig('b' * 25),
3568        Sig('c' * 25),
3569    ]
3570    argument_group_signatures = []
3571    usage = '''\
3572        usage: PROG [-h] [-x XXXXXXXXXXXXXXXXXXXXXXXXX] \
3573[-y YYYYYYYYYYYYYYYYYYYYYYYYY]
3574                    [-z ZZZZZZZZZZZZZZZZZZZZZZZZZ]
3575                    aaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbb
3576                    ccccccccccccccccccccccccc
3577        '''
3578    help = usage + '''\
3579
3580        positional arguments:
3581          aaaaaaaaaaaaaaaaaaaaaaaaa
3582          bbbbbbbbbbbbbbbbbbbbbbbbb
3583          ccccccccccccccccccccccccc
3584
3585        optional arguments:
3586          -h, --help            show this help message and exit
3587          -x XXXXXXXXXXXXXXXXXXXXXXXXX
3588          -y YYYYYYYYYYYYYYYYYYYYYYYYY
3589          -z ZZZZZZZZZZZZZZZZZZZZZZZZZ
3590        '''
3591    version = ''
3592
3593
3594class TestHelpUsageOptionalsOnlyWrap(HelpTestCase):
3595    """Test usage messages where there are only optionals and they wrap"""
3596
3597    parser_signature = Sig(prog='PROG')
3598    argument_signatures = [
3599        Sig('-x', metavar='X' * 25),
3600        Sig('-y', metavar='Y' * 25),
3601        Sig('-z', metavar='Z' * 25),
3602    ]
3603    argument_group_signatures = []
3604    usage = '''\
3605        usage: PROG [-h] [-x XXXXXXXXXXXXXXXXXXXXXXXXX] \
3606[-y YYYYYYYYYYYYYYYYYYYYYYYYY]
3607                    [-z ZZZZZZZZZZZZZZZZZZZZZZZZZ]
3608        '''
3609    help = usage + '''\
3610
3611        optional arguments:
3612          -h, --help            show this help message and exit
3613          -x XXXXXXXXXXXXXXXXXXXXXXXXX
3614          -y YYYYYYYYYYYYYYYYYYYYYYYYY
3615          -z ZZZZZZZZZZZZZZZZZZZZZZZZZ
3616        '''
3617    version = ''
3618
3619
3620class TestHelpUsagePositionalsOnlyWrap(HelpTestCase):
3621    """Test usage messages where there are only positionals and they wrap"""
3622
3623    parser_signature = Sig(prog='PROG', add_help=False)
3624    argument_signatures = [
3625        Sig('a' * 25),
3626        Sig('b' * 25),
3627        Sig('c' * 25),
3628    ]
3629    argument_group_signatures = []
3630    usage = '''\
3631        usage: PROG aaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbb
3632                    ccccccccccccccccccccccccc
3633        '''
3634    help = usage + '''\
3635
3636        positional arguments:
3637          aaaaaaaaaaaaaaaaaaaaaaaaa
3638          bbbbbbbbbbbbbbbbbbbbbbbbb
3639          ccccccccccccccccccccccccc
3640        '''
3641    version = ''
3642
3643
3644class TestHelpVariableExpansion(HelpTestCase):
3645    """Test that variables are expanded properly in help messages"""
3646
3647    parser_signature = Sig(prog='PROG')
3648    argument_signatures = [
3649        Sig('-x', type=int,
3650            help='x %(prog)s %(default)s %(type)s %%'),
3651        Sig('-y', action='store_const', default=42, const='XXX',
3652            help='y %(prog)s %(default)s %(const)s'),
3653        Sig('--foo', choices='abc',
3654            help='foo %(prog)s %(default)s %(choices)s'),
3655        Sig('--bar', default='baz', choices=[1, 2], metavar='BBB',
3656            help='bar %(prog)s %(default)s %(dest)s'),
3657        Sig('spam', help='spam %(prog)s %(default)s'),
3658        Sig('badger', default=0.5, help='badger %(prog)s %(default)s'),
3659    ]
3660    argument_group_signatures = [
3661        (Sig('group'), [
3662            Sig('-a', help='a %(prog)s %(default)s'),
3663            Sig('-b', default=-1, help='b %(prog)s %(default)s'),
3664        ])
3665    ]
3666    usage = ('''\
3667        usage: PROG [-h] [-x X] [-y] [--foo {a,b,c}] [--bar BBB] [-a A] [-b B]
3668                    spam badger
3669        ''')
3670    help = usage + '''\
3671
3672        positional arguments:
3673          spam           spam PROG None
3674          badger         badger PROG 0.5
3675
3676        optional arguments:
3677          -h, --help     show this help message and exit
3678          -x X           x PROG None int %
3679          -y             y PROG 42 XXX
3680          --foo {a,b,c}  foo PROG None a, b, c
3681          --bar BBB      bar PROG baz bar
3682
3683        group:
3684          -a A           a PROG None
3685          -b B           b PROG -1
3686        '''
3687    version = ''
3688
3689
3690class TestHelpVariableExpansionUsageSupplied(HelpTestCase):
3691    """Test that variables are expanded properly when usage= is present"""
3692
3693    parser_signature = Sig(prog='PROG', usage='%(prog)s FOO')
3694    argument_signatures = []
3695    argument_group_signatures = []
3696    usage = ('''\
3697        usage: PROG FOO
3698        ''')
3699    help = usage + '''\
3700
3701        optional arguments:
3702          -h, --help  show this help message and exit
3703        '''
3704    version = ''
3705
3706
3707class TestHelpVariableExpansionNoArguments(HelpTestCase):
3708    """Test that variables are expanded properly with no arguments"""
3709
3710    parser_signature = Sig(prog='PROG', add_help=False)
3711    argument_signatures = []
3712    argument_group_signatures = []
3713    usage = ('''\
3714        usage: PROG
3715        ''')
3716    help = usage
3717    version = ''
3718
3719
3720class TestHelpSuppressUsage(HelpTestCase):
3721    """Test that items can be suppressed in usage messages"""
3722
3723    parser_signature = Sig(prog='PROG', usage=argparse.SUPPRESS)
3724    argument_signatures = [
3725        Sig('--foo', help='foo help'),
3726        Sig('spam', help='spam help'),
3727    ]
3728    argument_group_signatures = []
3729    help = '''\
3730        positional arguments:
3731          spam        spam help
3732
3733        optional arguments:
3734          -h, --help  show this help message and exit
3735          --foo FOO   foo help
3736        '''
3737    usage = ''
3738    version = ''
3739
3740
3741class TestHelpSuppressOptional(HelpTestCase):
3742    """Test that optional arguments can be suppressed in help messages"""
3743
3744    parser_signature = Sig(prog='PROG', add_help=False)
3745    argument_signatures = [
3746        Sig('--foo', help=argparse.SUPPRESS),
3747        Sig('spam', help='spam help'),
3748    ]
3749    argument_group_signatures = []
3750    usage = '''\
3751        usage: PROG spam
3752        '''
3753    help = usage + '''\
3754
3755        positional arguments:
3756          spam  spam help
3757        '''
3758    version = ''
3759
3760
3761class TestHelpSuppressOptionalGroup(HelpTestCase):
3762    """Test that optional groups can be suppressed in help messages"""
3763
3764    parser_signature = Sig(prog='PROG')
3765    argument_signatures = [
3766        Sig('--foo', help='foo help'),
3767        Sig('spam', help='spam help'),
3768    ]
3769    argument_group_signatures = [
3770        (Sig('group'), [Sig('--bar', help=argparse.SUPPRESS)]),
3771    ]
3772    usage = '''\
3773        usage: PROG [-h] [--foo FOO] spam
3774        '''
3775    help = usage + '''\
3776
3777        positional arguments:
3778          spam        spam help
3779
3780        optional arguments:
3781          -h, --help  show this help message and exit
3782          --foo FOO   foo help
3783        '''
3784    version = ''
3785
3786
3787class TestHelpSuppressPositional(HelpTestCase):
3788    """Test that positional arguments can be suppressed in help messages"""
3789
3790    parser_signature = Sig(prog='PROG')
3791    argument_signatures = [
3792        Sig('--foo', help='foo help'),
3793        Sig('spam', help=argparse.SUPPRESS),
3794    ]
3795    argument_group_signatures = []
3796    usage = '''\
3797        usage: PROG [-h] [--foo FOO]
3798        '''
3799    help = usage + '''\
3800
3801        optional arguments:
3802          -h, --help  show this help message and exit
3803          --foo FOO   foo help
3804        '''
3805    version = ''
3806
3807
3808class TestHelpRequiredOptional(HelpTestCase):
3809    """Test that required options don't look optional"""
3810
3811    parser_signature = Sig(prog='PROG')
3812    argument_signatures = [
3813        Sig('--foo', required=True, help='foo help'),
3814    ]
3815    argument_group_signatures = []
3816    usage = '''\
3817        usage: PROG [-h] --foo FOO
3818        '''
3819    help = usage + '''\
3820
3821        optional arguments:
3822          -h, --help  show this help message and exit
3823          --foo FOO   foo help
3824        '''
3825    version = ''
3826
3827
3828class TestHelpAlternatePrefixChars(HelpTestCase):
3829    """Test that options display with different prefix characters"""
3830
3831    parser_signature = Sig(prog='PROG', prefix_chars='^;', add_help=False)
3832    argument_signatures = [
3833        Sig('^^foo', action='store_true', help='foo help'),
3834        Sig(';b', ';;bar', help='bar help'),
3835    ]
3836    argument_group_signatures = []
3837    usage = '''\
3838        usage: PROG [^^foo] [;b BAR]
3839        '''
3840    help = usage + '''\
3841
3842        optional arguments:
3843          ^^foo              foo help
3844          ;b BAR, ;;bar BAR  bar help
3845        '''
3846    version = ''
3847
3848
3849class TestHelpNoHelpOptional(HelpTestCase):
3850    """Test that the --help argument can be suppressed help messages"""
3851
3852    parser_signature = Sig(prog='PROG', add_help=False)
3853    argument_signatures = [
3854        Sig('--foo', help='foo help'),
3855        Sig('spam', help='spam help'),
3856    ]
3857    argument_group_signatures = []
3858    usage = '''\
3859        usage: PROG [--foo FOO] spam
3860        '''
3861    help = usage + '''\
3862
3863        positional arguments:
3864          spam       spam help
3865
3866        optional arguments:
3867          --foo FOO  foo help
3868        '''
3869    version = ''
3870
3871
3872class TestHelpNone(HelpTestCase):
3873    """Test that no errors occur if no help is specified"""
3874
3875    parser_signature = Sig(prog='PROG')
3876    argument_signatures = [
3877        Sig('--foo'),
3878        Sig('spam'),
3879    ]
3880    argument_group_signatures = []
3881    usage = '''\
3882        usage: PROG [-h] [--foo FOO] spam
3883        '''
3884    help = usage + '''\
3885
3886        positional arguments:
3887          spam
3888
3889        optional arguments:
3890          -h, --help  show this help message and exit
3891          --foo FOO
3892        '''
3893    version = ''
3894
3895
3896class TestHelpTupleMetavar(HelpTestCase):
3897    """Test specifying metavar as a tuple"""
3898
3899    parser_signature = Sig(prog='PROG')
3900    argument_signatures = [
3901        Sig('-w', help='w', nargs='+', metavar=('W1', 'W2')),
3902        Sig('-x', help='x', nargs='*', metavar=('X1', 'X2')),
3903        Sig('-y', help='y', nargs=3, metavar=('Y1', 'Y2', 'Y3')),
3904        Sig('-z', help='z', nargs='?', metavar=('Z1', )),
3905    ]
3906    argument_group_signatures = []
3907    usage = '''\
3908        usage: PROG [-h] [-w W1 [W2 ...]] [-x [X1 [X2 ...]]] [-y Y1 Y2 Y3] \
3909[-z [Z1]]
3910        '''
3911    help = usage + '''\
3912
3913        optional arguments:
3914          -h, --help        show this help message and exit
3915          -w W1 [W2 ...]    w
3916          -x [X1 [X2 ...]]  x
3917          -y Y1 Y2 Y3       y
3918          -z [Z1]           z
3919        '''
3920    version = ''
3921
3922
3923class TestHelpRawText(HelpTestCase):
3924    """Test the RawTextHelpFormatter"""
3925
3926    parser_signature = Sig(
3927        prog='PROG', formatter_class=argparse.RawTextHelpFormatter,
3928        description='Keep the formatting\n'
3929                    '    exactly as it is written\n'
3930                    '\n'
3931                    'here\n')
3932
3933    argument_signatures = [
3934        Sig('--foo', help='    foo help should also\n'
3935                          'appear as given here'),
3936        Sig('spam', help='spam help'),
3937    ]
3938    argument_group_signatures = [
3939        (Sig('title', description='    This text\n'
3940                                  '  should be indented\n'
3941                                  '    exactly like it is here\n'),
3942         [Sig('--bar', help='bar help')]),
3943    ]
3944    usage = '''\
3945        usage: PROG [-h] [--foo FOO] [--bar BAR] spam
3946        '''
3947    help = usage + '''\
3948
3949        Keep the formatting
3950            exactly as it is written
3951
3952        here
3953
3954        positional arguments:
3955          spam        spam help
3956
3957        optional arguments:
3958          -h, --help  show this help message and exit
3959          --foo FOO       foo help should also
3960                      appear as given here
3961
3962        title:
3963              This text
3964            should be indented
3965              exactly like it is here
3966
3967          --bar BAR   bar help
3968        '''
3969    version = ''
3970
3971
3972class TestHelpRawDescription(HelpTestCase):
3973    """Test the RawTextHelpFormatter"""
3974
3975    parser_signature = Sig(
3976        prog='PROG', formatter_class=argparse.RawDescriptionHelpFormatter,
3977        description='Keep the formatting\n'
3978                    '    exactly as it is written\n'
3979                    '\n'
3980                    'here\n')
3981
3982    argument_signatures = [
3983        Sig('--foo', help='  foo help should not\n'
3984                          '    retain this odd formatting'),
3985        Sig('spam', help='spam help'),
3986    ]
3987    argument_group_signatures = [
3988        (Sig('title', description='    This text\n'
3989                                  '  should be indented\n'
3990                                  '    exactly like it is here\n'),
3991         [Sig('--bar', help='bar help')]),
3992    ]
3993    usage = '''\
3994        usage: PROG [-h] [--foo FOO] [--bar BAR] spam
3995        '''
3996    help = usage + '''\
3997
3998        Keep the formatting
3999            exactly as it is written
4000
4001        here
4002
4003        positional arguments:
4004          spam        spam help
4005
4006        optional arguments:
4007          -h, --help  show this help message and exit
4008          --foo FOO   foo help should not retain this odd formatting
4009
4010        title:
4011              This text
4012            should be indented
4013              exactly like it is here
4014
4015          --bar BAR   bar help
4016        '''
4017    version = ''
4018
4019
4020class TestHelpArgumentDefaults(HelpTestCase):
4021    """Test the ArgumentDefaultsHelpFormatter"""
4022
4023    parser_signature = Sig(
4024        prog='PROG', formatter_class=argparse.ArgumentDefaultsHelpFormatter,
4025        description='description')
4026
4027    argument_signatures = [
4028        Sig('--foo', help='foo help - oh and by the way, %(default)s'),
4029        Sig('--bar', action='store_true', help='bar help'),
4030        Sig('spam', help='spam help'),
4031        Sig('badger', nargs='?', default='wooden', help='badger help'),
4032    ]
4033    argument_group_signatures = [
4034        (Sig('title', description='description'),
4035         [Sig('--baz', type=int, default=42, help='baz help')]),
4036    ]
4037    usage = '''\
4038        usage: PROG [-h] [--foo FOO] [--bar] [--baz BAZ] spam [badger]
4039        '''
4040    help = usage + '''\
4041
4042        description
4043
4044        positional arguments:
4045          spam        spam help
4046          badger      badger help (default: wooden)
4047
4048        optional arguments:
4049          -h, --help  show this help message and exit
4050          --foo FOO   foo help - oh and by the way, None
4051          --bar       bar help (default: False)
4052
4053        title:
4054          description
4055
4056          --baz BAZ   baz help (default: 42)
4057        '''
4058    version = ''
4059
4060class TestHelpVersionAction(HelpTestCase):
4061    """Test the default help for the version action"""
4062
4063    parser_signature = Sig(prog='PROG', description='description')
4064    argument_signatures = [Sig('-V', '--version', action='version', version='3.6')]
4065    argument_group_signatures = []
4066    usage = '''\
4067        usage: PROG [-h] [-V]
4068        '''
4069    help = usage + '''\
4070
4071        description
4072
4073        optional arguments:
4074          -h, --help     show this help message and exit
4075          -V, --version  show program's version number and exit
4076        '''
4077    version = ''
4078
4079
4080class TestHelpVersionActionSuppress(HelpTestCase):
4081    """Test that the --version argument can be suppressed in help messages"""
4082
4083    parser_signature = Sig(prog='PROG')
4084    argument_signatures = [
4085        Sig('-v', '--version', action='version', version='1.0',
4086            help=argparse.SUPPRESS),
4087        Sig('--foo', help='foo help'),
4088        Sig('spam', help='spam help'),
4089    ]
4090    argument_group_signatures = []
4091    usage = '''\
4092        usage: PROG [-h] [--foo FOO] spam
4093        '''
4094    help = usage + '''\
4095
4096        positional arguments:
4097          spam        spam help
4098
4099        optional arguments:
4100          -h, --help  show this help message and exit
4101          --foo FOO   foo help
4102        '''
4103
4104
4105class TestHelpSubparsersOrdering(HelpTestCase):
4106    """Test ordering of subcommands in help matches the code"""
4107    parser_signature = Sig(prog='PROG',
4108                           description='display some subcommands')
4109    argument_signatures = [Sig('-v', '--version', action='version', version='0.1')]
4110
4111    subparsers_signatures = [Sig(name=name)
4112                             for name in ('a', 'b', 'c', 'd', 'e')]
4113
4114    usage = '''\
4115        usage: PROG [-h] [-v] {a,b,c,d,e} ...
4116        '''
4117
4118    help = usage + '''\
4119
4120        display some subcommands
4121
4122        positional arguments:
4123          {a,b,c,d,e}
4124
4125        optional arguments:
4126          -h, --help     show this help message and exit
4127          -v, --version  show program's version number and exit
4128        '''
4129
4130    version = '''\
4131        0.1
4132        '''
4133
4134class TestHelpSubparsersWithHelpOrdering(HelpTestCase):
4135    """Test ordering of subcommands in help matches the code"""
4136    parser_signature = Sig(prog='PROG',
4137                           description='display some subcommands')
4138    argument_signatures = [Sig('-v', '--version', action='version', version='0.1')]
4139
4140    subcommand_data = (('a', 'a subcommand help'),
4141                       ('b', 'b subcommand help'),
4142                       ('c', 'c subcommand help'),
4143                       ('d', 'd subcommand help'),
4144                       ('e', 'e subcommand help'),
4145                       )
4146
4147    subparsers_signatures = [Sig(name=name, help=help)
4148                             for name, help in subcommand_data]
4149
4150    usage = '''\
4151        usage: PROG [-h] [-v] {a,b,c,d,e} ...
4152        '''
4153
4154    help = usage + '''\
4155
4156        display some subcommands
4157
4158        positional arguments:
4159          {a,b,c,d,e}
4160            a            a subcommand help
4161            b            b subcommand help
4162            c            c subcommand help
4163            d            d subcommand help
4164            e            e subcommand help
4165
4166        optional arguments:
4167          -h, --help     show this help message and exit
4168          -v, --version  show program's version number and exit
4169        '''
4170
4171    version = '''\
4172        0.1
4173        '''
4174
4175
4176
4177class TestHelpMetavarTypeFormatter(HelpTestCase):
4178    """"""
4179
4180    def custom_type(string):
4181        return string
4182
4183    parser_signature = Sig(prog='PROG', description='description',
4184                           formatter_class=argparse.MetavarTypeHelpFormatter)
4185    argument_signatures = [Sig('a', type=int),
4186                           Sig('-b', type=custom_type),
4187                           Sig('-c', type=float, metavar='SOME FLOAT')]
4188    argument_group_signatures = []
4189    usage = '''\
4190        usage: PROG [-h] [-b custom_type] [-c SOME FLOAT] int
4191        '''
4192    help = usage + '''\
4193
4194        description
4195
4196        positional arguments:
4197          int
4198
4199        optional arguments:
4200          -h, --help      show this help message and exit
4201          -b custom_type
4202          -c SOME FLOAT
4203        '''
4204    version = ''
4205
4206
4207# =====================================
4208# Optional/Positional constructor tests
4209# =====================================
4210
4211class TestInvalidArgumentConstructors(TestCase):
4212    """Test a bunch of invalid Argument constructors"""
4213
4214    def assertTypeError(self, *args, **kwargs):
4215        parser = argparse.ArgumentParser()
4216        self.assertRaises(TypeError, parser.add_argument,
4217                          *args, **kwargs)
4218
4219    def assertValueError(self, *args, **kwargs):
4220        parser = argparse.ArgumentParser()
4221        self.assertRaises(ValueError, parser.add_argument,
4222                          *args, **kwargs)
4223
4224    def test_invalid_keyword_arguments(self):
4225        self.assertTypeError('-x', bar=None)
4226        self.assertTypeError('-y', callback='foo')
4227        self.assertTypeError('-y', callback_args=())
4228        self.assertTypeError('-y', callback_kwargs={})
4229
4230    def test_missing_destination(self):
4231        self.assertTypeError()
4232        for action in ['append', 'store']:
4233            self.assertTypeError(action=action)
4234
4235    def test_invalid_option_strings(self):
4236        self.assertValueError('--')
4237        self.assertValueError('---')
4238
4239    def test_invalid_type(self):
4240        self.assertValueError('--foo', type='int')
4241        self.assertValueError('--foo', type=(int, float))
4242
4243    def test_invalid_action(self):
4244        self.assertValueError('-x', action='foo')
4245        self.assertValueError('foo', action='baz')
4246        self.assertValueError('--foo', action=('store', 'append'))
4247        parser = argparse.ArgumentParser()
4248        with self.assertRaises(ValueError) as cm:
4249            parser.add_argument("--foo", action="store-true")
4250        self.assertIn('unknown action', str(cm.exception))
4251
4252    def test_multiple_dest(self):
4253        parser = argparse.ArgumentParser()
4254        parser.add_argument(dest='foo')
4255        with self.assertRaises(ValueError) as cm:
4256            parser.add_argument('bar', dest='baz')
4257        self.assertIn('dest supplied twice for positional argument',
4258                      str(cm.exception))
4259
4260    def test_no_argument_actions(self):
4261        for action in ['store_const', 'store_true', 'store_false',
4262                       'append_const', 'count']:
4263            for attrs in [dict(type=int), dict(nargs='+'),
4264                          dict(choices='ab')]:
4265                self.assertTypeError('-x', action=action, **attrs)
4266
4267    def test_no_argument_no_const_actions(self):
4268        # options with zero arguments
4269        for action in ['store_true', 'store_false', 'count']:
4270
4271            # const is always disallowed
4272            self.assertTypeError('-x', const='foo', action=action)
4273
4274            # nargs is always disallowed
4275            self.assertTypeError('-x', nargs='*', action=action)
4276
4277    def test_more_than_one_argument_actions(self):
4278        for action in ['store', 'append']:
4279
4280            # nargs=0 is disallowed
4281            self.assertValueError('-x', nargs=0, action=action)
4282            self.assertValueError('spam', nargs=0, action=action)
4283
4284            # const is disallowed with non-optional arguments
4285            for nargs in [1, '*', '+']:
4286                self.assertValueError('-x', const='foo',
4287                                      nargs=nargs, action=action)
4288                self.assertValueError('spam', const='foo',
4289                                      nargs=nargs, action=action)
4290
4291    def test_required_const_actions(self):
4292        for action in ['store_const', 'append_const']:
4293
4294            # nargs is always disallowed
4295            self.assertTypeError('-x', nargs='+', action=action)
4296
4297    def test_parsers_action_missing_params(self):
4298        self.assertTypeError('command', action='parsers')
4299        self.assertTypeError('command', action='parsers', prog='PROG')
4300        self.assertTypeError('command', action='parsers',
4301                             parser_class=argparse.ArgumentParser)
4302
4303    def test_required_positional(self):
4304        self.assertTypeError('foo', required=True)
4305
4306    def test_user_defined_action(self):
4307
4308        class Success(Exception):
4309            pass
4310
4311        class Action(object):
4312
4313            def __init__(self,
4314                         option_strings,
4315                         dest,
4316                         const,
4317                         default,
4318                         required=False):
4319                if dest == 'spam':
4320                    if const is Success:
4321                        if default is Success:
4322                            raise Success()
4323
4324            def __call__(self, *args, **kwargs):
4325                pass
4326
4327        parser = argparse.ArgumentParser()
4328        self.assertRaises(Success, parser.add_argument, '--spam',
4329                          action=Action, default=Success, const=Success)
4330        self.assertRaises(Success, parser.add_argument, 'spam',
4331                          action=Action, default=Success, const=Success)
4332
4333# ================================
4334# Actions returned by add_argument
4335# ================================
4336
4337class TestActionsReturned(TestCase):
4338
4339    def test_dest(self):
4340        parser = argparse.ArgumentParser()
4341        action = parser.add_argument('--foo')
4342        self.assertEqual(action.dest, 'foo')
4343        action = parser.add_argument('-b', '--bar')
4344        self.assertEqual(action.dest, 'bar')
4345        action = parser.add_argument('-x', '-y')
4346        self.assertEqual(action.dest, 'x')
4347
4348    def test_misc(self):
4349        parser = argparse.ArgumentParser()
4350        action = parser.add_argument('--foo', nargs='?', const=42,
4351                                     default=84, type=int, choices=[1, 2],
4352                                     help='FOO', metavar='BAR', dest='baz')
4353        self.assertEqual(action.nargs, '?')
4354        self.assertEqual(action.const, 42)
4355        self.assertEqual(action.default, 84)
4356        self.assertEqual(action.type, int)
4357        self.assertEqual(action.choices, [1, 2])
4358        self.assertEqual(action.help, 'FOO')
4359        self.assertEqual(action.metavar, 'BAR')
4360        self.assertEqual(action.dest, 'baz')
4361
4362
4363# ================================
4364# Argument conflict handling tests
4365# ================================
4366
4367class TestConflictHandling(TestCase):
4368
4369    def test_bad_type(self):
4370        self.assertRaises(ValueError, argparse.ArgumentParser,
4371                          conflict_handler='foo')
4372
4373    def test_conflict_error(self):
4374        parser = argparse.ArgumentParser()
4375        parser.add_argument('-x')
4376        self.assertRaises(argparse.ArgumentError,
4377                          parser.add_argument, '-x')
4378        parser.add_argument('--spam')
4379        self.assertRaises(argparse.ArgumentError,
4380                          parser.add_argument, '--spam')
4381
4382    def test_resolve_error(self):
4383        get_parser = argparse.ArgumentParser
4384        parser = get_parser(prog='PROG', conflict_handler='resolve')
4385
4386        parser.add_argument('-x', help='OLD X')
4387        parser.add_argument('-x', help='NEW X')
4388        self.assertEqual(parser.format_help(), textwrap.dedent('''\
4389            usage: PROG [-h] [-x X]
4390
4391            optional arguments:
4392              -h, --help  show this help message and exit
4393              -x X        NEW X
4394            '''))
4395
4396        parser.add_argument('--spam', metavar='OLD_SPAM')
4397        parser.add_argument('--spam', metavar='NEW_SPAM')
4398        self.assertEqual(parser.format_help(), textwrap.dedent('''\
4399            usage: PROG [-h] [-x X] [--spam NEW_SPAM]
4400
4401            optional arguments:
4402              -h, --help       show this help message and exit
4403              -x X             NEW X
4404              --spam NEW_SPAM
4405            '''))
4406
4407
4408# =============================
4409# Help and Version option tests
4410# =============================
4411
4412class TestOptionalsHelpVersionActions(TestCase):
4413    """Test the help and version actions"""
4414
4415    def assertPrintHelpExit(self, parser, args_str):
4416        with self.assertRaises(ArgumentParserError) as cm:
4417            parser.parse_args(args_str.split())
4418        self.assertEqual(parser.format_help(), cm.exception.stdout)
4419
4420    def assertArgumentParserError(self, parser, *args):
4421        self.assertRaises(ArgumentParserError, parser.parse_args, args)
4422
4423    def test_version(self):
4424        parser = ErrorRaisingArgumentParser()
4425        parser.add_argument('-v', '--version', action='version', version='1.0')
4426        self.assertPrintHelpExit(parser, '-h')
4427        self.assertPrintHelpExit(parser, '--help')
4428        self.assertRaises(AttributeError, getattr, parser, 'format_version')
4429
4430    def test_version_format(self):
4431        parser = ErrorRaisingArgumentParser(prog='PPP')
4432        parser.add_argument('-v', '--version', action='version', version='%(prog)s 3.5')
4433        with self.assertRaises(ArgumentParserError) as cm:
4434            parser.parse_args(['-v'])
4435        self.assertEqual('PPP 3.5\n', cm.exception.stdout)
4436
4437    def test_version_no_help(self):
4438        parser = ErrorRaisingArgumentParser(add_help=False)
4439        parser.add_argument('-v', '--version', action='version', version='1.0')
4440        self.assertArgumentParserError(parser, '-h')
4441        self.assertArgumentParserError(parser, '--help')
4442        self.assertRaises(AttributeError, getattr, parser, 'format_version')
4443
4444    def test_version_action(self):
4445        parser = ErrorRaisingArgumentParser(prog='XXX')
4446        parser.add_argument('-V', action='version', version='%(prog)s 3.7')
4447        with self.assertRaises(ArgumentParserError) as cm:
4448            parser.parse_args(['-V'])
4449        self.assertEqual('XXX 3.7\n', cm.exception.stdout)
4450
4451    def test_no_help(self):
4452        parser = ErrorRaisingArgumentParser(add_help=False)
4453        self.assertArgumentParserError(parser, '-h')
4454        self.assertArgumentParserError(parser, '--help')
4455        self.assertArgumentParserError(parser, '-v')
4456        self.assertArgumentParserError(parser, '--version')
4457
4458    def test_alternate_help_version(self):
4459        parser = ErrorRaisingArgumentParser()
4460        parser.add_argument('-x', action='help')
4461        parser.add_argument('-y', action='version')
4462        self.assertPrintHelpExit(parser, '-x')
4463        self.assertArgumentParserError(parser, '-v')
4464        self.assertArgumentParserError(parser, '--version')
4465        self.assertRaises(AttributeError, getattr, parser, 'format_version')
4466
4467    def test_help_version_extra_arguments(self):
4468        parser = ErrorRaisingArgumentParser()
4469        parser.add_argument('--version', action='version', version='1.0')
4470        parser.add_argument('-x', action='store_true')
4471        parser.add_argument('y')
4472
4473        # try all combinations of valid prefixes and suffixes
4474        valid_prefixes = ['', '-x', 'foo', '-x bar', 'baz -x']
4475        valid_suffixes = valid_prefixes + ['--bad-option', 'foo bar baz']
4476        for prefix in valid_prefixes:
4477            for suffix in valid_suffixes:
4478                format = '%s %%s %s' % (prefix, suffix)
4479            self.assertPrintHelpExit(parser, format % '-h')
4480            self.assertPrintHelpExit(parser, format % '--help')
4481            self.assertRaises(AttributeError, getattr, parser, 'format_version')
4482
4483
4484# ======================
4485# str() and repr() tests
4486# ======================
4487
4488class TestStrings(TestCase):
4489    """Test str()  and repr() on Optionals and Positionals"""
4490
4491    def assertStringEqual(self, obj, result_string):
4492        for func in [str, repr]:
4493            self.assertEqual(func(obj), result_string)
4494
4495    def test_optional(self):
4496        option = argparse.Action(
4497            option_strings=['--foo', '-a', '-b'],
4498            dest='b',
4499            type='int',
4500            nargs='+',
4501            default=42,
4502            choices=[1, 2, 3],
4503            help='HELP',
4504            metavar='METAVAR')
4505        string = (
4506            "Action(option_strings=['--foo', '-a', '-b'], dest='b', "
4507            "nargs='+', const=None, default=42, type='int', "
4508            "choices=[1, 2, 3], help='HELP', metavar='METAVAR')")
4509        self.assertStringEqual(option, string)
4510
4511    def test_argument(self):
4512        argument = argparse.Action(
4513            option_strings=[],
4514            dest='x',
4515            type=float,
4516            nargs='?',
4517            default=2.5,
4518            choices=[0.5, 1.5, 2.5],
4519            help='H HH H',
4520            metavar='MV MV MV')
4521        string = (
4522            "Action(option_strings=[], dest='x', nargs='?', "
4523            "const=None, default=2.5, type=%r, choices=[0.5, 1.5, 2.5], "
4524            "help='H HH H', metavar='MV MV MV')" % float)
4525        self.assertStringEqual(argument, string)
4526
4527    def test_namespace(self):
4528        ns = argparse.Namespace(foo=42, bar='spam')
4529        string = "Namespace(bar='spam', foo=42)"
4530        self.assertStringEqual(ns, string)
4531
4532    def test_namespace_starkwargs_notidentifier(self):
4533        ns = argparse.Namespace(**{'"': 'quote'})
4534        string = """Namespace(**{'"': 'quote'})"""
4535        self.assertStringEqual(ns, string)
4536
4537    def test_namespace_kwargs_and_starkwargs_notidentifier(self):
4538        ns = argparse.Namespace(a=1, **{'"': 'quote'})
4539        string = """Namespace(a=1, **{'"': 'quote'})"""
4540        self.assertStringEqual(ns, string)
4541
4542    def test_namespace_starkwargs_identifier(self):
4543        ns = argparse.Namespace(**{'valid': True})
4544        string = "Namespace(valid=True)"
4545        self.assertStringEqual(ns, string)
4546
4547    def test_parser(self):
4548        parser = argparse.ArgumentParser(prog='PROG')
4549        string = (
4550            "ArgumentParser(prog='PROG', usage=None, description=None, "
4551            "formatter_class=%r, conflict_handler='error', "
4552            "add_help=True)" % argparse.HelpFormatter)
4553        self.assertStringEqual(parser, string)
4554
4555# ===============
4556# Namespace tests
4557# ===============
4558
4559class TestNamespace(TestCase):
4560
4561    def test_constructor(self):
4562        ns = argparse.Namespace()
4563        self.assertRaises(AttributeError, getattr, ns, 'x')
4564
4565        ns = argparse.Namespace(a=42, b='spam')
4566        self.assertEqual(ns.a, 42)
4567        self.assertEqual(ns.b, 'spam')
4568
4569    def test_equality(self):
4570        ns1 = argparse.Namespace(a=1, b=2)
4571        ns2 = argparse.Namespace(b=2, a=1)
4572        ns3 = argparse.Namespace(a=1)
4573        ns4 = argparse.Namespace(b=2)
4574
4575        self.assertEqual(ns1, ns2)
4576        self.assertNotEqual(ns1, ns3)
4577        self.assertNotEqual(ns1, ns4)
4578        self.assertNotEqual(ns2, ns3)
4579        self.assertNotEqual(ns2, ns4)
4580        self.assertTrue(ns1 != ns3)
4581        self.assertTrue(ns1 != ns4)
4582        self.assertTrue(ns2 != ns3)
4583        self.assertTrue(ns2 != ns4)
4584
4585    def test_equality_returns_notimplemented(self):
4586        # See issue 21481
4587        ns = argparse.Namespace(a=1, b=2)
4588        self.assertIs(ns.__eq__(None), NotImplemented)
4589        self.assertIs(ns.__ne__(None), NotImplemented)
4590
4591
4592# ===================
4593# File encoding tests
4594# ===================
4595
4596class TestEncoding(TestCase):
4597
4598    def _test_module_encoding(self, path):
4599        path, _ = os.path.splitext(path)
4600        path += ".py"
4601        with codecs.open(path, 'r', 'utf-8') as f:
4602            f.read()
4603
4604    def test_argparse_module_encoding(self):
4605        self._test_module_encoding(argparse.__file__)
4606
4607    def test_test_argparse_module_encoding(self):
4608        self._test_module_encoding(__file__)
4609
4610# ===================
4611# ArgumentError tests
4612# ===================
4613
4614class TestArgumentError(TestCase):
4615
4616    def test_argument_error(self):
4617        msg = "my error here"
4618        error = argparse.ArgumentError(None, msg)
4619        self.assertEqual(str(error), msg)
4620
4621# =======================
4622# ArgumentTypeError tests
4623# =======================
4624
4625class TestArgumentTypeError(TestCase):
4626
4627    def test_argument_type_error(self):
4628
4629        def spam(string):
4630            raise argparse.ArgumentTypeError('spam!')
4631
4632        parser = ErrorRaisingArgumentParser(prog='PROG', add_help=False)
4633        parser.add_argument('x', type=spam)
4634        with self.assertRaises(ArgumentParserError) as cm:
4635            parser.parse_args(['XXX'])
4636        self.assertEqual('usage: PROG x\nPROG: error: argument x: spam!\n',
4637                         cm.exception.stderr)
4638
4639# =========================
4640# MessageContentError tests
4641# =========================
4642
4643class TestMessageContentError(TestCase):
4644
4645    def test_missing_argument_name_in_message(self):
4646        parser = ErrorRaisingArgumentParser(prog='PROG', usage='')
4647        parser.add_argument('req_pos', type=str)
4648        parser.add_argument('-req_opt', type=int, required=True)
4649        parser.add_argument('need_one', type=str, nargs='+')
4650
4651        with self.assertRaises(ArgumentParserError) as cm:
4652            parser.parse_args([])
4653        msg = str(cm.exception)
4654        self.assertRegex(msg, 'req_pos')
4655        self.assertRegex(msg, 'req_opt')
4656        self.assertRegex(msg, 'need_one')
4657        with self.assertRaises(ArgumentParserError) as cm:
4658            parser.parse_args(['myXargument'])
4659        msg = str(cm.exception)
4660        self.assertNotIn(msg, 'req_pos')
4661        self.assertRegex(msg, 'req_opt')
4662        self.assertRegex(msg, 'need_one')
4663        with self.assertRaises(ArgumentParserError) as cm:
4664            parser.parse_args(['myXargument', '-req_opt=1'])
4665        msg = str(cm.exception)
4666        self.assertNotIn(msg, 'req_pos')
4667        self.assertNotIn(msg, 'req_opt')
4668        self.assertRegex(msg, 'need_one')
4669
4670    def test_optional_optional_not_in_message(self):
4671        parser = ErrorRaisingArgumentParser(prog='PROG', usage='')
4672        parser.add_argument('req_pos', type=str)
4673        parser.add_argument('--req_opt', type=int, required=True)
4674        parser.add_argument('--opt_opt', type=bool, nargs='?',
4675                            default=True)
4676        with self.assertRaises(ArgumentParserError) as cm:
4677            parser.parse_args([])
4678        msg = str(cm.exception)
4679        self.assertRegex(msg, 'req_pos')
4680        self.assertRegex(msg, 'req_opt')
4681        self.assertNotIn(msg, 'opt_opt')
4682        with self.assertRaises(ArgumentParserError) as cm:
4683            parser.parse_args(['--req_opt=1'])
4684        msg = str(cm.exception)
4685        self.assertRegex(msg, 'req_pos')
4686        self.assertNotIn(msg, 'req_opt')
4687        self.assertNotIn(msg, 'opt_opt')
4688
4689    def test_optional_positional_not_in_message(self):
4690        parser = ErrorRaisingArgumentParser(prog='PROG', usage='')
4691        parser.add_argument('req_pos')
4692        parser.add_argument('optional_positional', nargs='?', default='eggs')
4693        with self.assertRaises(ArgumentParserError) as cm:
4694            parser.parse_args([])
4695        msg = str(cm.exception)
4696        self.assertRegex(msg, 'req_pos')
4697        self.assertNotIn(msg, 'optional_positional')
4698
4699
4700# ================================================
4701# Check that the type function is called only once
4702# ================================================
4703
4704class TestTypeFunctionCallOnlyOnce(TestCase):
4705
4706    def test_type_function_call_only_once(self):
4707        def spam(string_to_convert):
4708            self.assertEqual(string_to_convert, 'spam!')
4709            return 'foo_converted'
4710
4711        parser = argparse.ArgumentParser()
4712        parser.add_argument('--foo', type=spam, default='bar')
4713        args = parser.parse_args('--foo spam!'.split())
4714        self.assertEqual(NS(foo='foo_converted'), args)
4715
4716# ==================================================================
4717# Check semantics regarding the default argument and type conversion
4718# ==================================================================
4719
4720class TestTypeFunctionCalledOnDefault(TestCase):
4721
4722    def test_type_function_call_with_non_string_default(self):
4723        def spam(int_to_convert):
4724            self.assertEqual(int_to_convert, 0)
4725            return 'foo_converted'
4726
4727        parser = argparse.ArgumentParser()
4728        parser.add_argument('--foo', type=spam, default=0)
4729        args = parser.parse_args([])
4730        # foo should *not* be converted because its default is not a string.
4731        self.assertEqual(NS(foo=0), args)
4732
4733    def test_type_function_call_with_string_default(self):
4734        def spam(int_to_convert):
4735            return 'foo_converted'
4736
4737        parser = argparse.ArgumentParser()
4738        parser.add_argument('--foo', type=spam, default='0')
4739        args = parser.parse_args([])
4740        # foo is converted because its default is a string.
4741        self.assertEqual(NS(foo='foo_converted'), args)
4742
4743    def test_no_double_type_conversion_of_default(self):
4744        def extend(str_to_convert):
4745            return str_to_convert + '*'
4746
4747        parser = argparse.ArgumentParser()
4748        parser.add_argument('--test', type=extend, default='*')
4749        args = parser.parse_args([])
4750        # The test argument will be two stars, one coming from the default
4751        # value and one coming from the type conversion being called exactly
4752        # once.
4753        self.assertEqual(NS(test='**'), args)
4754
4755    def test_issue_15906(self):
4756        # Issue #15906: When action='append', type=str, default=[] are
4757        # providing, the dest value was the string representation "[]" when it
4758        # should have been an empty list.
4759        parser = argparse.ArgumentParser()
4760        parser.add_argument('--test', dest='test', type=str,
4761                            default=[], action='append')
4762        args = parser.parse_args([])
4763        self.assertEqual(args.test, [])
4764
4765# ======================
4766# parse_known_args tests
4767# ======================
4768
4769class TestParseKnownArgs(TestCase):
4770
4771    def test_arguments_tuple(self):
4772        parser = argparse.ArgumentParser()
4773        parser.parse_args(())
4774
4775    def test_arguments_list(self):
4776        parser = argparse.ArgumentParser()
4777        parser.parse_args([])
4778
4779    def test_arguments_tuple_positional(self):
4780        parser = argparse.ArgumentParser()
4781        parser.add_argument('x')
4782        parser.parse_args(('x',))
4783
4784    def test_arguments_list_positional(self):
4785        parser = argparse.ArgumentParser()
4786        parser.add_argument('x')
4787        parser.parse_args(['x'])
4788
4789    def test_optionals(self):
4790        parser = argparse.ArgumentParser()
4791        parser.add_argument('--foo')
4792        args, extras = parser.parse_known_args('--foo F --bar --baz'.split())
4793        self.assertEqual(NS(foo='F'), args)
4794        self.assertEqual(['--bar', '--baz'], extras)
4795
4796    def test_mixed(self):
4797        parser = argparse.ArgumentParser()
4798        parser.add_argument('-v', nargs='?', const=1, type=int)
4799        parser.add_argument('--spam', action='store_false')
4800        parser.add_argument('badger')
4801
4802        argv = ["B", "C", "--foo", "-v", "3", "4"]
4803        args, extras = parser.parse_known_args(argv)
4804        self.assertEqual(NS(v=3, spam=True, badger="B"), args)
4805        self.assertEqual(["C", "--foo", "4"], extras)
4806
4807# ==========================
4808# add_argument metavar tests
4809# ==========================
4810
4811class TestAddArgumentMetavar(TestCase):
4812
4813    EXPECTED_MESSAGE = "length of metavar tuple does not match nargs"
4814
4815    def do_test_no_exception(self, nargs, metavar):
4816        parser = argparse.ArgumentParser()
4817        parser.add_argument("--foo", nargs=nargs, metavar=metavar)
4818
4819    def do_test_exception(self, nargs, metavar):
4820        parser = argparse.ArgumentParser()
4821        with self.assertRaises(ValueError) as cm:
4822            parser.add_argument("--foo", nargs=nargs, metavar=metavar)
4823        self.assertEqual(cm.exception.args[0], self.EXPECTED_MESSAGE)
4824
4825    # Unit tests for different values of metavar when nargs=None
4826
4827    def test_nargs_None_metavar_string(self):
4828        self.do_test_no_exception(nargs=None, metavar="1")
4829
4830    def test_nargs_None_metavar_length0(self):
4831        self.do_test_exception(nargs=None, metavar=tuple())
4832
4833    def test_nargs_None_metavar_length1(self):
4834        self.do_test_no_exception(nargs=None, metavar=("1"))
4835
4836    def test_nargs_None_metavar_length2(self):
4837        self.do_test_exception(nargs=None, metavar=("1", "2"))
4838
4839    def test_nargs_None_metavar_length3(self):
4840        self.do_test_exception(nargs=None, metavar=("1", "2", "3"))
4841
4842    # Unit tests for different values of metavar when nargs=?
4843
4844    def test_nargs_optional_metavar_string(self):
4845        self.do_test_no_exception(nargs="?", metavar="1")
4846
4847    def test_nargs_optional_metavar_length0(self):
4848        self.do_test_exception(nargs="?", metavar=tuple())
4849
4850    def test_nargs_optional_metavar_length1(self):
4851        self.do_test_no_exception(nargs="?", metavar=("1"))
4852
4853    def test_nargs_optional_metavar_length2(self):
4854        self.do_test_exception(nargs="?", metavar=("1", "2"))
4855
4856    def test_nargs_optional_metavar_length3(self):
4857        self.do_test_exception(nargs="?", metavar=("1", "2", "3"))
4858
4859    # Unit tests for different values of metavar when nargs=*
4860
4861    def test_nargs_zeroormore_metavar_string(self):
4862        self.do_test_no_exception(nargs="*", metavar="1")
4863
4864    def test_nargs_zeroormore_metavar_length0(self):
4865        self.do_test_exception(nargs="*", metavar=tuple())
4866
4867    def test_nargs_zeroormore_metavar_length1(self):
4868        self.do_test_no_exception(nargs="*", metavar=("1"))
4869
4870    def test_nargs_zeroormore_metavar_length2(self):
4871        self.do_test_no_exception(nargs="*", metavar=("1", "2"))
4872
4873    def test_nargs_zeroormore_metavar_length3(self):
4874        self.do_test_exception(nargs="*", metavar=("1", "2", "3"))
4875
4876    # Unit tests for different values of metavar when nargs=+
4877
4878    def test_nargs_oneormore_metavar_string(self):
4879        self.do_test_no_exception(nargs="+", metavar="1")
4880
4881    def test_nargs_oneormore_metavar_length0(self):
4882        self.do_test_exception(nargs="+", metavar=tuple())
4883
4884    def test_nargs_oneormore_metavar_length1(self):
4885        self.do_test_no_exception(nargs="+", metavar=("1"))
4886
4887    def test_nargs_oneormore_metavar_length2(self):
4888        self.do_test_no_exception(nargs="+", metavar=("1", "2"))
4889
4890    def test_nargs_oneormore_metavar_length3(self):
4891        self.do_test_exception(nargs="+", metavar=("1", "2", "3"))
4892
4893    # Unit tests for different values of metavar when nargs=...
4894
4895    def test_nargs_remainder_metavar_string(self):
4896        self.do_test_no_exception(nargs="...", metavar="1")
4897
4898    def test_nargs_remainder_metavar_length0(self):
4899        self.do_test_no_exception(nargs="...", metavar=tuple())
4900
4901    def test_nargs_remainder_metavar_length1(self):
4902        self.do_test_no_exception(nargs="...", metavar=("1"))
4903
4904    def test_nargs_remainder_metavar_length2(self):
4905        self.do_test_no_exception(nargs="...", metavar=("1", "2"))
4906
4907    def test_nargs_remainder_metavar_length3(self):
4908        self.do_test_no_exception(nargs="...", metavar=("1", "2", "3"))
4909
4910    # Unit tests for different values of metavar when nargs=A...
4911
4912    def test_nargs_parser_metavar_string(self):
4913        self.do_test_no_exception(nargs="A...", metavar="1")
4914
4915    def test_nargs_parser_metavar_length0(self):
4916        self.do_test_exception(nargs="A...", metavar=tuple())
4917
4918    def test_nargs_parser_metavar_length1(self):
4919        self.do_test_no_exception(nargs="A...", metavar=("1"))
4920
4921    def test_nargs_parser_metavar_length2(self):
4922        self.do_test_exception(nargs="A...", metavar=("1", "2"))
4923
4924    def test_nargs_parser_metavar_length3(self):
4925        self.do_test_exception(nargs="A...", metavar=("1", "2", "3"))
4926
4927    # Unit tests for different values of metavar when nargs=1
4928
4929    def test_nargs_1_metavar_string(self):
4930        self.do_test_no_exception(nargs=1, metavar="1")
4931
4932    def test_nargs_1_metavar_length0(self):
4933        self.do_test_exception(nargs=1, metavar=tuple())
4934
4935    def test_nargs_1_metavar_length1(self):
4936        self.do_test_no_exception(nargs=1, metavar=("1"))
4937
4938    def test_nargs_1_metavar_length2(self):
4939        self.do_test_exception(nargs=1, metavar=("1", "2"))
4940
4941    def test_nargs_1_metavar_length3(self):
4942        self.do_test_exception(nargs=1, metavar=("1", "2", "3"))
4943
4944    # Unit tests for different values of metavar when nargs=2
4945
4946    def test_nargs_2_metavar_string(self):
4947        self.do_test_no_exception(nargs=2, metavar="1")
4948
4949    def test_nargs_2_metavar_length0(self):
4950        self.do_test_exception(nargs=2, metavar=tuple())
4951
4952    def test_nargs_2_metavar_length1(self):
4953        self.do_test_no_exception(nargs=2, metavar=("1"))
4954
4955    def test_nargs_2_metavar_length2(self):
4956        self.do_test_no_exception(nargs=2, metavar=("1", "2"))
4957
4958    def test_nargs_2_metavar_length3(self):
4959        self.do_test_exception(nargs=2, metavar=("1", "2", "3"))
4960
4961    # Unit tests for different values of metavar when nargs=3
4962
4963    def test_nargs_3_metavar_string(self):
4964        self.do_test_no_exception(nargs=3, metavar="1")
4965
4966    def test_nargs_3_metavar_length0(self):
4967        self.do_test_exception(nargs=3, metavar=tuple())
4968
4969    def test_nargs_3_metavar_length1(self):
4970        self.do_test_no_exception(nargs=3, metavar=("1"))
4971
4972    def test_nargs_3_metavar_length2(self):
4973        self.do_test_exception(nargs=3, metavar=("1", "2"))
4974
4975    def test_nargs_3_metavar_length3(self):
4976        self.do_test_no_exception(nargs=3, metavar=("1", "2", "3"))
4977
4978# ============================
4979# from argparse import * tests
4980# ============================
4981
4982class TestImportStar(TestCase):
4983
4984    def test(self):
4985        for name in argparse.__all__:
4986            self.assertTrue(hasattr(argparse, name))
4987
4988    def test_all_exports_everything_but_modules(self):
4989        items = [
4990            name
4991            for name, value in vars(argparse).items()
4992            if not (name.startswith("_") or name == 'ngettext')
4993            if not inspect.ismodule(value)
4994        ]
4995        self.assertEqual(sorted(items), sorted(argparse.__all__))
4996
4997def test_main():
4998    support.run_unittest(__name__)
4999    # Remove global references to avoid looking like we have refleaks.
5000    RFile.seen = {}
5001    WFile.seen = set()
5002
5003
5004
5005if __name__ == '__main__':
5006    test_main()
5007