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