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