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