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