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