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