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