blob: d6d16090eb0260c487754e1f7fc7f5358a68b634 [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
2816# =================================================
2817# Mutually exclusive group in parent parser tests
2818# =================================================
2819
2820class MEPBase(object):
2821
2822 def get_parser(self, required=None):
2823 parent = super(MEPBase, self).get_parser(required=required)
2824 parser = ErrorRaisingArgumentParser(
2825 prog=parent.prog, add_help=False, parents=[parent])
2826 return parser
2827
2828
2829class TestMutuallyExclusiveGroupErrorsParent(
2830 MEPBase, TestMutuallyExclusiveGroupErrors):
2831 pass
2832
2833
2834class TestMutuallyExclusiveSimpleParent(
2835 MEPBase, TestMutuallyExclusiveSimple):
2836 pass
2837
2838
2839class TestMutuallyExclusiveLongParent(
2840 MEPBase, TestMutuallyExclusiveLong):
2841 pass
2842
2843
2844class TestMutuallyExclusiveFirstSuppressedParent(
2845 MEPBase, TestMutuallyExclusiveFirstSuppressed):
2846 pass
2847
2848
2849class TestMutuallyExclusiveManySuppressedParent(
2850 MEPBase, TestMutuallyExclusiveManySuppressed):
2851 pass
2852
2853
2854class TestMutuallyExclusiveOptionalAndPositionalParent(
2855 MEPBase, TestMutuallyExclusiveOptionalAndPositional):
2856 pass
2857
2858
2859class TestMutuallyExclusiveOptionalsMixedParent(
2860 MEPBase, TestMutuallyExclusiveOptionalsMixed):
2861 pass
2862
2863
2864class TestMutuallyExclusiveOptionalsAndPositionalsMixedParent(
2865 MEPBase, TestMutuallyExclusiveOptionalsAndPositionalsMixed):
2866 pass
2867
2868# =================
2869# Set default tests
2870# =================
2871
2872class TestSetDefaults(TestCase):
2873
2874 def test_set_defaults_no_args(self):
2875 parser = ErrorRaisingArgumentParser()
2876 parser.set_defaults(x='foo')
2877 parser.set_defaults(y='bar', z=1)
2878 self.assertEqual(NS(x='foo', y='bar', z=1),
2879 parser.parse_args([]))
2880 self.assertEqual(NS(x='foo', y='bar', z=1),
2881 parser.parse_args([], NS()))
2882 self.assertEqual(NS(x='baz', y='bar', z=1),
2883 parser.parse_args([], NS(x='baz')))
2884 self.assertEqual(NS(x='baz', y='bar', z=2),
2885 parser.parse_args([], NS(x='baz', z=2)))
2886
2887 def test_set_defaults_with_args(self):
2888 parser = ErrorRaisingArgumentParser()
2889 parser.set_defaults(x='foo', y='bar')
2890 parser.add_argument('-x', default='xfoox')
2891 self.assertEqual(NS(x='xfoox', y='bar'),
2892 parser.parse_args([]))
2893 self.assertEqual(NS(x='xfoox', y='bar'),
2894 parser.parse_args([], NS()))
2895 self.assertEqual(NS(x='baz', y='bar'),
2896 parser.parse_args([], NS(x='baz')))
2897 self.assertEqual(NS(x='1', y='bar'),
2898 parser.parse_args('-x 1'.split()))
2899 self.assertEqual(NS(x='1', y='bar'),
2900 parser.parse_args('-x 1'.split(), NS()))
2901 self.assertEqual(NS(x='1', y='bar'),
2902 parser.parse_args('-x 1'.split(), NS(x='baz')))
2903
2904 def test_set_defaults_subparsers(self):
2905 parser = ErrorRaisingArgumentParser()
2906 parser.set_defaults(x='foo')
2907 subparsers = parser.add_subparsers()
2908 parser_a = subparsers.add_parser('a')
2909 parser_a.set_defaults(y='bar')
2910 self.assertEqual(NS(x='foo', y='bar'),
2911 parser.parse_args('a'.split()))
2912
2913 def test_set_defaults_parents(self):
2914 parent = ErrorRaisingArgumentParser(add_help=False)
2915 parent.set_defaults(x='foo')
2916 parser = ErrorRaisingArgumentParser(parents=[parent])
2917 self.assertEqual(NS(x='foo'), parser.parse_args([]))
2918
R David Murray7570cbd2014-10-17 19:55:11 -04002919 def test_set_defaults_on_parent_and_subparser(self):
2920 parser = argparse.ArgumentParser()
2921 xparser = parser.add_subparsers().add_parser('X')
2922 parser.set_defaults(foo=1)
2923 xparser.set_defaults(foo=2)
2924 self.assertEqual(NS(foo=2), parser.parse_args(['X']))
2925
Benjamin Peterson698a18a2010-03-02 22:34:37 +00002926 def test_set_defaults_same_as_add_argument(self):
2927 parser = ErrorRaisingArgumentParser()
2928 parser.set_defaults(w='W', x='X', y='Y', z='Z')
2929 parser.add_argument('-w')
2930 parser.add_argument('-x', default='XX')
2931 parser.add_argument('y', nargs='?')
2932 parser.add_argument('z', nargs='?', default='ZZ')
2933
2934 # defaults set previously
2935 self.assertEqual(NS(w='W', x='XX', y='Y', z='ZZ'),
2936 parser.parse_args([]))
2937
2938 # reset defaults
2939 parser.set_defaults(w='WW', x='X', y='YY', z='Z')
2940 self.assertEqual(NS(w='WW', x='X', y='YY', z='Z'),
2941 parser.parse_args([]))
2942
2943 def test_set_defaults_same_as_add_argument_group(self):
2944 parser = ErrorRaisingArgumentParser()
2945 parser.set_defaults(w='W', x='X', y='Y', z='Z')
2946 group = parser.add_argument_group('foo')
2947 group.add_argument('-w')
2948 group.add_argument('-x', default='XX')
2949 group.add_argument('y', nargs='?')
2950 group.add_argument('z', nargs='?', default='ZZ')
2951
2952
2953 # defaults set previously
2954 self.assertEqual(NS(w='W', x='XX', y='Y', z='ZZ'),
2955 parser.parse_args([]))
2956
2957 # reset defaults
2958 parser.set_defaults(w='WW', x='X', y='YY', z='Z')
2959 self.assertEqual(NS(w='WW', x='X', y='YY', z='Z'),
2960 parser.parse_args([]))
2961
2962# =================
2963# Get default tests
2964# =================
2965
2966class TestGetDefault(TestCase):
2967
2968 def test_get_default(self):
2969 parser = ErrorRaisingArgumentParser()
Berker Peksag1c5f56a2014-07-06 09:33:20 +03002970 self.assertIsNone(parser.get_default("foo"))
2971 self.assertIsNone(parser.get_default("bar"))
Benjamin Peterson698a18a2010-03-02 22:34:37 +00002972
2973 parser.add_argument("--foo")
Berker Peksag1c5f56a2014-07-06 09:33:20 +03002974 self.assertIsNone(parser.get_default("foo"))
2975 self.assertIsNone(parser.get_default("bar"))
Benjamin Peterson698a18a2010-03-02 22:34:37 +00002976
2977 parser.add_argument("--bar", type=int, default=42)
Berker Peksag1c5f56a2014-07-06 09:33:20 +03002978 self.assertIsNone(parser.get_default("foo"))
Benjamin Peterson698a18a2010-03-02 22:34:37 +00002979 self.assertEqual(42, parser.get_default("bar"))
2980
2981 parser.set_defaults(foo="badger")
2982 self.assertEqual("badger", parser.get_default("foo"))
2983 self.assertEqual(42, parser.get_default("bar"))
2984
2985# ==========================
2986# Namespace 'contains' tests
2987# ==========================
2988
2989class TestNamespaceContainsSimple(TestCase):
2990
2991 def test_empty(self):
2992 ns = argparse.Namespace()
Berker Peksag1c5f56a2014-07-06 09:33:20 +03002993 self.assertNotIn('', ns)
2994 self.assertNotIn('x', ns)
Benjamin Peterson698a18a2010-03-02 22:34:37 +00002995
2996 def test_non_empty(self):
2997 ns = argparse.Namespace(x=1, y=2)
Berker Peksag1c5f56a2014-07-06 09:33:20 +03002998 self.assertNotIn('', ns)
2999 self.assertIn('x', ns)
3000 self.assertIn('y', ns)
3001 self.assertNotIn('xx', ns)
3002 self.assertNotIn('z', ns)
Benjamin Peterson698a18a2010-03-02 22:34:37 +00003003
3004# =====================
3005# Help formatting tests
3006# =====================
3007
3008class TestHelpFormattingMetaclass(type):
3009
3010 def __init__(cls, name, bases, bodydict):
3011 if name == 'HelpTestCase':
3012 return
3013
3014 class AddTests(object):
3015
3016 def __init__(self, test_class, func_suffix, std_name):
3017 self.func_suffix = func_suffix
3018 self.std_name = std_name
3019
3020 for test_func in [self.test_format,
3021 self.test_print,
3022 self.test_print_file]:
3023 test_name = '%s_%s' % (test_func.__name__, func_suffix)
3024
3025 def test_wrapper(self, test_func=test_func):
3026 test_func(self)
3027 try:
3028 test_wrapper.__name__ = test_name
3029 except TypeError:
3030 pass
3031 setattr(test_class, test_name, test_wrapper)
3032
3033 def _get_parser(self, tester):
3034 parser = argparse.ArgumentParser(
3035 *tester.parser_signature.args,
3036 **tester.parser_signature.kwargs)
Steven Bethard8a6a1982011-03-27 13:53:53 +02003037 for argument_sig in getattr(tester, 'argument_signatures', []):
Benjamin Peterson698a18a2010-03-02 22:34:37 +00003038 parser.add_argument(*argument_sig.args,
3039 **argument_sig.kwargs)
Steven Bethard8a6a1982011-03-27 13:53:53 +02003040 group_sigs = getattr(tester, 'argument_group_signatures', [])
3041 for group_sig, argument_sigs in group_sigs:
Benjamin Peterson698a18a2010-03-02 22:34:37 +00003042 group = parser.add_argument_group(*group_sig.args,
3043 **group_sig.kwargs)
3044 for argument_sig in argument_sigs:
3045 group.add_argument(*argument_sig.args,
3046 **argument_sig.kwargs)
Steven Bethard8a6a1982011-03-27 13:53:53 +02003047 subparsers_sigs = getattr(tester, 'subparsers_signatures', [])
3048 if subparsers_sigs:
3049 subparsers = parser.add_subparsers()
3050 for subparser_sig in subparsers_sigs:
3051 subparsers.add_parser(*subparser_sig.args,
3052 **subparser_sig.kwargs)
Benjamin Peterson698a18a2010-03-02 22:34:37 +00003053 return parser
3054
3055 def _test(self, tester, parser_text):
3056 expected_text = getattr(tester, self.func_suffix)
3057 expected_text = textwrap.dedent(expected_text)
Benjamin Peterson698a18a2010-03-02 22:34:37 +00003058 tester.assertEqual(expected_text, parser_text)
3059
3060 def test_format(self, tester):
3061 parser = self._get_parser(tester)
3062 format = getattr(parser, 'format_%s' % self.func_suffix)
3063 self._test(tester, format())
3064
3065 def test_print(self, tester):
3066 parser = self._get_parser(tester)
3067 print_ = getattr(parser, 'print_%s' % self.func_suffix)
3068 old_stream = getattr(sys, self.std_name)
Benjamin Petersonb48af542010-04-11 20:43:16 +00003069 setattr(sys, self.std_name, StdIOBuffer())
Benjamin Peterson698a18a2010-03-02 22:34:37 +00003070 try:
3071 print_()
3072 parser_text = getattr(sys, self.std_name).getvalue()
3073 finally:
3074 setattr(sys, self.std_name, old_stream)
3075 self._test(tester, parser_text)
3076
3077 def test_print_file(self, tester):
3078 parser = self._get_parser(tester)
3079 print_ = getattr(parser, 'print_%s' % self.func_suffix)
Benjamin Petersonb48af542010-04-11 20:43:16 +00003080 sfile = StdIOBuffer()
Benjamin Peterson698a18a2010-03-02 22:34:37 +00003081 print_(sfile)
3082 parser_text = sfile.getvalue()
3083 self._test(tester, parser_text)
3084
Florent Xiclunaaf1adbe2012-07-07 17:02:22 +02003085 # add tests for {format,print}_{usage,help}
Benjamin Peterson698a18a2010-03-02 22:34:37 +00003086 for func_suffix, std_name in [('usage', 'stdout'),
Florent Xiclunaaf1adbe2012-07-07 17:02:22 +02003087 ('help', 'stdout')]:
Benjamin Peterson698a18a2010-03-02 22:34:37 +00003088 AddTests(cls, func_suffix, std_name)
3089
3090bases = TestCase,
3091HelpTestCase = TestHelpFormattingMetaclass('HelpTestCase', bases, {})
3092
3093
3094class TestHelpBiggerOptionals(HelpTestCase):
3095 """Make sure that argument help aligns when options are longer"""
3096
3097 parser_signature = Sig(prog='PROG', description='DESCRIPTION',
Florent Xiclunaaf1adbe2012-07-07 17:02:22 +02003098 epilog='EPILOG')
Benjamin Peterson698a18a2010-03-02 22:34:37 +00003099 argument_signatures = [
Florent Xiclunaaf1adbe2012-07-07 17:02:22 +02003100 Sig('-v', '--version', action='version', version='0.1'),
Benjamin Peterson698a18a2010-03-02 22:34:37 +00003101 Sig('-x', action='store_true', help='X HELP'),
3102 Sig('--y', help='Y HELP'),
3103 Sig('foo', help='FOO HELP'),
3104 Sig('bar', help='BAR HELP'),
3105 ]
3106 argument_group_signatures = []
3107 usage = '''\
3108 usage: PROG [-h] [-v] [-x] [--y Y] foo bar
3109 '''
3110 help = usage + '''\
3111
3112 DESCRIPTION
3113
3114 positional arguments:
3115 foo FOO HELP
3116 bar BAR HELP
3117
3118 optional arguments:
3119 -h, --help show this help message and exit
3120 -v, --version show program's version number and exit
3121 -x X HELP
3122 --y Y Y HELP
3123
3124 EPILOG
3125 '''
3126 version = '''\
3127 0.1
3128 '''
3129
Serhiy Storchakaf4511122014-01-09 23:14:27 +02003130class TestShortColumns(HelpTestCase):
3131 '''Test extremely small number of columns.
3132
3133 TestCase prevents "COLUMNS" from being too small in the tests themselves,
Martin Panter2e4571a2015-11-14 01:07:43 +00003134 but we don't want any exceptions thrown in such cases. Only ugly representation.
Serhiy Storchakaf4511122014-01-09 23:14:27 +02003135 '''
3136 def setUp(self):
3137 env = support.EnvironmentVarGuard()
3138 env.set("COLUMNS", '15')
3139 self.addCleanup(env.__exit__)
3140
3141 parser_signature = TestHelpBiggerOptionals.parser_signature
3142 argument_signatures = TestHelpBiggerOptionals.argument_signatures
3143 argument_group_signatures = TestHelpBiggerOptionals.argument_group_signatures
3144 usage = '''\
3145 usage: PROG
3146 [-h]
3147 [-v]
3148 [-x]
3149 [--y Y]
3150 foo
3151 bar
3152 '''
3153 help = usage + '''\
3154
3155 DESCRIPTION
3156
3157 positional arguments:
3158 foo
3159 FOO HELP
3160 bar
3161 BAR HELP
3162
3163 optional arguments:
3164 -h, --help
3165 show this
3166 help
3167 message and
3168 exit
3169 -v, --version
3170 show
3171 program's
3172 version
3173 number and
3174 exit
3175 -x
3176 X HELP
3177 --y Y
3178 Y HELP
3179
3180 EPILOG
3181 '''
3182 version = TestHelpBiggerOptionals.version
3183
Benjamin Peterson698a18a2010-03-02 22:34:37 +00003184
3185class TestHelpBiggerOptionalGroups(HelpTestCase):
3186 """Make sure that argument help aligns when options are longer"""
3187
3188 parser_signature = Sig(prog='PROG', description='DESCRIPTION',
Florent Xiclunaaf1adbe2012-07-07 17:02:22 +02003189 epilog='EPILOG')
Benjamin Peterson698a18a2010-03-02 22:34:37 +00003190 argument_signatures = [
Florent Xiclunaaf1adbe2012-07-07 17:02:22 +02003191 Sig('-v', '--version', action='version', version='0.1'),
Benjamin Peterson698a18a2010-03-02 22:34:37 +00003192 Sig('-x', action='store_true', help='X HELP'),
3193 Sig('--y', help='Y HELP'),
3194 Sig('foo', help='FOO HELP'),
3195 Sig('bar', help='BAR HELP'),
3196 ]
3197 argument_group_signatures = [
3198 (Sig('GROUP TITLE', description='GROUP DESCRIPTION'), [
3199 Sig('baz', help='BAZ HELP'),
3200 Sig('-z', nargs='+', help='Z HELP')]),
3201 ]
3202 usage = '''\
3203 usage: PROG [-h] [-v] [-x] [--y Y] [-z Z [Z ...]] foo bar baz
3204 '''
3205 help = usage + '''\
3206
3207 DESCRIPTION
3208
3209 positional arguments:
3210 foo FOO HELP
3211 bar BAR HELP
3212
3213 optional arguments:
3214 -h, --help show this help message and exit
3215 -v, --version show program's version number and exit
3216 -x X HELP
3217 --y Y Y HELP
3218
3219 GROUP TITLE:
3220 GROUP DESCRIPTION
3221
3222 baz BAZ HELP
3223 -z Z [Z ...] Z HELP
3224
3225 EPILOG
3226 '''
3227 version = '''\
3228 0.1
3229 '''
3230
3231
3232class TestHelpBiggerPositionals(HelpTestCase):
3233 """Make sure that help aligns when arguments are longer"""
3234
3235 parser_signature = Sig(usage='USAGE', description='DESCRIPTION')
3236 argument_signatures = [
3237 Sig('-x', action='store_true', help='X HELP'),
3238 Sig('--y', help='Y HELP'),
3239 Sig('ekiekiekifekang', help='EKI HELP'),
3240 Sig('bar', help='BAR HELP'),
3241 ]
3242 argument_group_signatures = []
3243 usage = '''\
3244 usage: USAGE
3245 '''
3246 help = usage + '''\
3247
3248 DESCRIPTION
3249
3250 positional arguments:
3251 ekiekiekifekang EKI HELP
3252 bar BAR HELP
3253
3254 optional arguments:
3255 -h, --help show this help message and exit
3256 -x X HELP
3257 --y Y Y HELP
3258 '''
3259
3260 version = ''
3261
3262
3263class TestHelpReformatting(HelpTestCase):
3264 """Make sure that text after short names starts on the first line"""
3265
3266 parser_signature = Sig(
3267 prog='PROG',
3268 description=' oddly formatted\n'
3269 'description\n'
3270 '\n'
3271 'that is so long that it should go onto multiple '
3272 'lines when wrapped')
3273 argument_signatures = [
3274 Sig('-x', metavar='XX', help='oddly\n'
3275 ' formatted -x help'),
3276 Sig('y', metavar='yyy', help='normal y help'),
3277 ]
3278 argument_group_signatures = [
3279 (Sig('title', description='\n'
3280 ' oddly formatted group\n'
3281 '\n'
3282 'description'),
3283 [Sig('-a', action='store_true',
3284 help=' oddly \n'
3285 'formatted -a help \n'
3286 ' again, so long that it should be wrapped over '
3287 'multiple lines')]),
3288 ]
3289 usage = '''\
3290 usage: PROG [-h] [-x XX] [-a] yyy
3291 '''
3292 help = usage + '''\
3293
3294 oddly formatted description that is so long that it should go onto \
3295multiple
3296 lines when wrapped
3297
3298 positional arguments:
3299 yyy normal y help
3300
3301 optional arguments:
3302 -h, --help show this help message and exit
3303 -x XX oddly formatted -x help
3304
3305 title:
3306 oddly formatted group description
3307
3308 -a oddly formatted -a help again, so long that it should \
3309be wrapped
3310 over multiple lines
3311 '''
3312 version = ''
3313
3314
3315class TestHelpWrappingShortNames(HelpTestCase):
3316 """Make sure that text after short names starts on the first line"""
3317
3318 parser_signature = Sig(prog='PROG', description= 'D\nD' * 30)
3319 argument_signatures = [
3320 Sig('-x', metavar='XX', help='XHH HX' * 20),
3321 Sig('y', metavar='yyy', help='YH YH' * 20),
3322 ]
3323 argument_group_signatures = [
3324 (Sig('ALPHAS'), [
3325 Sig('-a', action='store_true', help='AHHH HHA' * 10)]),
3326 ]
3327 usage = '''\
3328 usage: PROG [-h] [-x XX] [-a] yyy
3329 '''
3330 help = usage + '''\
3331
3332 D DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD \
3333DD DD DD
3334 DD DD DD DD D
3335
3336 positional arguments:
3337 yyy YH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH \
3338YHYH YHYH
3339 YHYH YHYH YHYH YHYH YHYH YHYH YHYH YH
3340
3341 optional arguments:
3342 -h, --help show this help message and exit
3343 -x XX XHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH \
3344HXXHH HXXHH
3345 HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HX
3346
3347 ALPHAS:
3348 -a AHHH HHAAHHH HHAAHHH HHAAHHH HHAAHHH HHAAHHH HHAAHHH \
3349HHAAHHH
3350 HHAAHHH HHAAHHH HHA
3351 '''
3352 version = ''
3353
3354
3355class TestHelpWrappingLongNames(HelpTestCase):
3356 """Make sure that text after long names starts on the next line"""
3357
Florent Xiclunaaf1adbe2012-07-07 17:02:22 +02003358 parser_signature = Sig(usage='USAGE', description= 'D D' * 30)
Benjamin Peterson698a18a2010-03-02 22:34:37 +00003359 argument_signatures = [
Florent Xiclunaaf1adbe2012-07-07 17:02:22 +02003360 Sig('-v', '--version', action='version', version='V V' * 30),
Benjamin Peterson698a18a2010-03-02 22:34:37 +00003361 Sig('-x', metavar='X' * 25, help='XH XH' * 20),
3362 Sig('y', metavar='y' * 25, help='YH YH' * 20),
3363 ]
3364 argument_group_signatures = [
3365 (Sig('ALPHAS'), [
3366 Sig('-a', metavar='A' * 25, help='AH AH' * 20),
3367 Sig('z', metavar='z' * 25, help='ZH ZH' * 20)]),
3368 ]
3369 usage = '''\
3370 usage: USAGE
3371 '''
3372 help = usage + '''\
3373
3374 D DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD \
3375DD DD DD
3376 DD DD DD DD D
3377
3378 positional arguments:
3379 yyyyyyyyyyyyyyyyyyyyyyyyy
3380 YH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH \
3381YHYH YHYH
3382 YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YH
3383
3384 optional arguments:
3385 -h, --help show this help message and exit
3386 -v, --version show program's version number and exit
3387 -x XXXXXXXXXXXXXXXXXXXXXXXXX
3388 XH XHXH XHXH XHXH XHXH XHXH XHXH XHXH XHXH \
3389XHXH XHXH
3390 XHXH XHXH XHXH XHXH XHXH XHXH XHXH XHXH XHXH XH
3391
3392 ALPHAS:
3393 -a AAAAAAAAAAAAAAAAAAAAAAAAA
3394 AH AHAH AHAH AHAH AHAH AHAH AHAH AHAH AHAH \
3395AHAH AHAH
3396 AHAH AHAH AHAH AHAH AHAH AHAH AHAH AHAH AHAH AH
3397 zzzzzzzzzzzzzzzzzzzzzzzzz
3398 ZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH \
3399ZHZH ZHZH
3400 ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZH
3401 '''
3402 version = '''\
3403 V VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV \
3404VV VV VV
3405 VV VV VV VV V
3406 '''
3407
3408
3409class TestHelpUsage(HelpTestCase):
3410 """Test basic usage messages"""
3411
3412 parser_signature = Sig(prog='PROG')
3413 argument_signatures = [
3414 Sig('-w', nargs='+', help='w'),
3415 Sig('-x', nargs='*', help='x'),
3416 Sig('a', help='a'),
3417 Sig('b', help='b', nargs=2),
3418 Sig('c', help='c', nargs='?'),
3419 ]
3420 argument_group_signatures = [
3421 (Sig('group'), [
3422 Sig('-y', nargs='?', help='y'),
3423 Sig('-z', nargs=3, help='z'),
3424 Sig('d', help='d', nargs='*'),
3425 Sig('e', help='e', nargs='+'),
3426 ])
3427 ]
3428 usage = '''\
3429 usage: PROG [-h] [-w W [W ...]] [-x [X [X ...]]] [-y [Y]] [-z Z Z Z]
3430 a b b [c] [d [d ...]] e [e ...]
3431 '''
3432 help = usage + '''\
3433
3434 positional arguments:
3435 a a
3436 b b
3437 c c
3438
3439 optional arguments:
3440 -h, --help show this help message and exit
3441 -w W [W ...] w
3442 -x [X [X ...]] x
3443
3444 group:
3445 -y [Y] y
3446 -z Z Z Z z
3447 d d
3448 e e
3449 '''
3450 version = ''
3451
3452
3453class TestHelpOnlyUserGroups(HelpTestCase):
3454 """Test basic usage messages"""
3455
3456 parser_signature = Sig(prog='PROG', add_help=False)
3457 argument_signatures = []
3458 argument_group_signatures = [
3459 (Sig('xxxx'), [
3460 Sig('-x', help='x'),
3461 Sig('a', help='a'),
3462 ]),
3463 (Sig('yyyy'), [
3464 Sig('b', help='b'),
3465 Sig('-y', help='y'),
3466 ]),
3467 ]
3468 usage = '''\
3469 usage: PROG [-x X] [-y Y] a b
3470 '''
3471 help = usage + '''\
3472
3473 xxxx:
3474 -x X x
3475 a a
3476
3477 yyyy:
3478 b b
3479 -y Y y
3480 '''
3481 version = ''
3482
3483
3484class TestHelpUsageLongProg(HelpTestCase):
3485 """Test usage messages where the prog is long"""
3486
3487 parser_signature = Sig(prog='P' * 60)
3488 argument_signatures = [
3489 Sig('-w', metavar='W'),
3490 Sig('-x', metavar='X'),
3491 Sig('a'),
3492 Sig('b'),
3493 ]
3494 argument_group_signatures = []
3495 usage = '''\
3496 usage: PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
3497 [-h] [-w W] [-x X] a b
3498 '''
3499 help = usage + '''\
3500
3501 positional arguments:
3502 a
3503 b
3504
3505 optional arguments:
3506 -h, --help show this help message and exit
3507 -w W
3508 -x X
3509 '''
3510 version = ''
3511
3512
3513class TestHelpUsageLongProgOptionsWrap(HelpTestCase):
3514 """Test usage messages where the prog is long and the optionals wrap"""
3515
3516 parser_signature = Sig(prog='P' * 60)
3517 argument_signatures = [
3518 Sig('-w', metavar='W' * 25),
3519 Sig('-x', metavar='X' * 25),
3520 Sig('-y', metavar='Y' * 25),
3521 Sig('-z', metavar='Z' * 25),
3522 Sig('a'),
3523 Sig('b'),
3524 ]
3525 argument_group_signatures = []
3526 usage = '''\
3527 usage: PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
3528 [-h] [-w WWWWWWWWWWWWWWWWWWWWWWWWW] \
3529[-x XXXXXXXXXXXXXXXXXXXXXXXXX]
3530 [-y YYYYYYYYYYYYYYYYYYYYYYYYY] [-z ZZZZZZZZZZZZZZZZZZZZZZZZZ]
3531 a b
3532 '''
3533 help = usage + '''\
3534
3535 positional arguments:
3536 a
3537 b
3538
3539 optional arguments:
3540 -h, --help show this help message and exit
3541 -w WWWWWWWWWWWWWWWWWWWWWWWWW
3542 -x XXXXXXXXXXXXXXXXXXXXXXXXX
3543 -y YYYYYYYYYYYYYYYYYYYYYYYYY
3544 -z ZZZZZZZZZZZZZZZZZZZZZZZZZ
3545 '''
3546 version = ''
3547
3548
3549class TestHelpUsageLongProgPositionalsWrap(HelpTestCase):
3550 """Test usage messages where the prog is long and the positionals wrap"""
3551
3552 parser_signature = Sig(prog='P' * 60, add_help=False)
3553 argument_signatures = [
3554 Sig('a' * 25),
3555 Sig('b' * 25),
3556 Sig('c' * 25),
3557 ]
3558 argument_group_signatures = []
3559 usage = '''\
3560 usage: PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
3561 aaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbb
3562 ccccccccccccccccccccccccc
3563 '''
3564 help = usage + '''\
3565
3566 positional arguments:
3567 aaaaaaaaaaaaaaaaaaaaaaaaa
3568 bbbbbbbbbbbbbbbbbbbbbbbbb
3569 ccccccccccccccccccccccccc
3570 '''
3571 version = ''
3572
3573
3574class TestHelpUsageOptionalsWrap(HelpTestCase):
3575 """Test usage messages where the optionals wrap"""
3576
3577 parser_signature = Sig(prog='PROG')
3578 argument_signatures = [
3579 Sig('-w', metavar='W' * 25),
3580 Sig('-x', metavar='X' * 25),
3581 Sig('-y', metavar='Y' * 25),
3582 Sig('-z', metavar='Z' * 25),
3583 Sig('a'),
3584 Sig('b'),
3585 Sig('c'),
3586 ]
3587 argument_group_signatures = []
3588 usage = '''\
3589 usage: PROG [-h] [-w WWWWWWWWWWWWWWWWWWWWWWWWW] \
3590[-x XXXXXXXXXXXXXXXXXXXXXXXXX]
3591 [-y YYYYYYYYYYYYYYYYYYYYYYYYY] \
3592[-z ZZZZZZZZZZZZZZZZZZZZZZZZZ]
3593 a b c
3594 '''
3595 help = usage + '''\
3596
3597 positional arguments:
3598 a
3599 b
3600 c
3601
3602 optional arguments:
3603 -h, --help show this help message and exit
3604 -w WWWWWWWWWWWWWWWWWWWWWWWWW
3605 -x XXXXXXXXXXXXXXXXXXXXXXXXX
3606 -y YYYYYYYYYYYYYYYYYYYYYYYYY
3607 -z ZZZZZZZZZZZZZZZZZZZZZZZZZ
3608 '''
3609 version = ''
3610
3611
3612class TestHelpUsagePositionalsWrap(HelpTestCase):
3613 """Test usage messages where the positionals wrap"""
3614
3615 parser_signature = Sig(prog='PROG')
3616 argument_signatures = [
3617 Sig('-x'),
3618 Sig('-y'),
3619 Sig('-z'),
3620 Sig('a' * 25),
3621 Sig('b' * 25),
3622 Sig('c' * 25),
3623 ]
3624 argument_group_signatures = []
3625 usage = '''\
3626 usage: PROG [-h] [-x X] [-y Y] [-z Z]
3627 aaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbb
3628 ccccccccccccccccccccccccc
3629 '''
3630 help = usage + '''\
3631
3632 positional arguments:
3633 aaaaaaaaaaaaaaaaaaaaaaaaa
3634 bbbbbbbbbbbbbbbbbbbbbbbbb
3635 ccccccccccccccccccccccccc
3636
3637 optional arguments:
3638 -h, --help show this help message and exit
3639 -x X
3640 -y Y
3641 -z Z
3642 '''
3643 version = ''
3644
3645
3646class TestHelpUsageOptionalsPositionalsWrap(HelpTestCase):
3647 """Test usage messages where the optionals and positionals wrap"""
3648
3649 parser_signature = Sig(prog='PROG')
3650 argument_signatures = [
3651 Sig('-x', metavar='X' * 25),
3652 Sig('-y', metavar='Y' * 25),
3653 Sig('-z', metavar='Z' * 25),
3654 Sig('a' * 25),
3655 Sig('b' * 25),
3656 Sig('c' * 25),
3657 ]
3658 argument_group_signatures = []
3659 usage = '''\
3660 usage: PROG [-h] [-x XXXXXXXXXXXXXXXXXXXXXXXXX] \
3661[-y YYYYYYYYYYYYYYYYYYYYYYYYY]
3662 [-z ZZZZZZZZZZZZZZZZZZZZZZZZZ]
3663 aaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbb
3664 ccccccccccccccccccccccccc
3665 '''
3666 help = usage + '''\
3667
3668 positional arguments:
3669 aaaaaaaaaaaaaaaaaaaaaaaaa
3670 bbbbbbbbbbbbbbbbbbbbbbbbb
3671 ccccccccccccccccccccccccc
3672
3673 optional arguments:
3674 -h, --help show this help message and exit
3675 -x XXXXXXXXXXXXXXXXXXXXXXXXX
3676 -y YYYYYYYYYYYYYYYYYYYYYYYYY
3677 -z ZZZZZZZZZZZZZZZZZZZZZZZZZ
3678 '''
3679 version = ''
3680
3681
3682class TestHelpUsageOptionalsOnlyWrap(HelpTestCase):
3683 """Test usage messages where there are only optionals and they wrap"""
3684
3685 parser_signature = Sig(prog='PROG')
3686 argument_signatures = [
3687 Sig('-x', metavar='X' * 25),
3688 Sig('-y', metavar='Y' * 25),
3689 Sig('-z', metavar='Z' * 25),
3690 ]
3691 argument_group_signatures = []
3692 usage = '''\
3693 usage: PROG [-h] [-x XXXXXXXXXXXXXXXXXXXXXXXXX] \
3694[-y YYYYYYYYYYYYYYYYYYYYYYYYY]
3695 [-z ZZZZZZZZZZZZZZZZZZZZZZZZZ]
3696 '''
3697 help = usage + '''\
3698
3699 optional arguments:
3700 -h, --help show this help message and exit
3701 -x XXXXXXXXXXXXXXXXXXXXXXXXX
3702 -y YYYYYYYYYYYYYYYYYYYYYYYYY
3703 -z ZZZZZZZZZZZZZZZZZZZZZZZZZ
3704 '''
3705 version = ''
3706
3707
3708class TestHelpUsagePositionalsOnlyWrap(HelpTestCase):
3709 """Test usage messages where there are only positionals and they wrap"""
3710
3711 parser_signature = Sig(prog='PROG', add_help=False)
3712 argument_signatures = [
3713 Sig('a' * 25),
3714 Sig('b' * 25),
3715 Sig('c' * 25),
3716 ]
3717 argument_group_signatures = []
3718 usage = '''\
3719 usage: PROG aaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbb
3720 ccccccccccccccccccccccccc
3721 '''
3722 help = usage + '''\
3723
3724 positional arguments:
3725 aaaaaaaaaaaaaaaaaaaaaaaaa
3726 bbbbbbbbbbbbbbbbbbbbbbbbb
3727 ccccccccccccccccccccccccc
3728 '''
3729 version = ''
3730
3731
3732class TestHelpVariableExpansion(HelpTestCase):
3733 """Test that variables are expanded properly in help messages"""
3734
3735 parser_signature = Sig(prog='PROG')
3736 argument_signatures = [
3737 Sig('-x', type=int,
3738 help='x %(prog)s %(default)s %(type)s %%'),
3739 Sig('-y', action='store_const', default=42, const='XXX',
3740 help='y %(prog)s %(default)s %(const)s'),
3741 Sig('--foo', choices='abc',
3742 help='foo %(prog)s %(default)s %(choices)s'),
3743 Sig('--bar', default='baz', choices=[1, 2], metavar='BBB',
3744 help='bar %(prog)s %(default)s %(dest)s'),
3745 Sig('spam', help='spam %(prog)s %(default)s'),
3746 Sig('badger', default=0.5, help='badger %(prog)s %(default)s'),
3747 ]
3748 argument_group_signatures = [
3749 (Sig('group'), [
3750 Sig('-a', help='a %(prog)s %(default)s'),
3751 Sig('-b', default=-1, help='b %(prog)s %(default)s'),
3752 ])
3753 ]
3754 usage = ('''\
3755 usage: PROG [-h] [-x X] [-y] [--foo {a,b,c}] [--bar BBB] [-a A] [-b B]
3756 spam badger
3757 ''')
3758 help = usage + '''\
3759
3760 positional arguments:
3761 spam spam PROG None
3762 badger badger PROG 0.5
3763
3764 optional arguments:
3765 -h, --help show this help message and exit
3766 -x X x PROG None int %
3767 -y y PROG 42 XXX
3768 --foo {a,b,c} foo PROG None a, b, c
3769 --bar BBB bar PROG baz bar
3770
3771 group:
3772 -a A a PROG None
3773 -b B b PROG -1
3774 '''
3775 version = ''
3776
3777
3778class TestHelpVariableExpansionUsageSupplied(HelpTestCase):
3779 """Test that variables are expanded properly when usage= is present"""
3780
3781 parser_signature = Sig(prog='PROG', usage='%(prog)s FOO')
3782 argument_signatures = []
3783 argument_group_signatures = []
3784 usage = ('''\
3785 usage: PROG FOO
3786 ''')
3787 help = usage + '''\
3788
3789 optional arguments:
3790 -h, --help show this help message and exit
3791 '''
3792 version = ''
3793
3794
3795class TestHelpVariableExpansionNoArguments(HelpTestCase):
3796 """Test that variables are expanded properly with no arguments"""
3797
3798 parser_signature = Sig(prog='PROG', add_help=False)
3799 argument_signatures = []
3800 argument_group_signatures = []
3801 usage = ('''\
3802 usage: PROG
3803 ''')
3804 help = usage
3805 version = ''
3806
3807
3808class TestHelpSuppressUsage(HelpTestCase):
3809 """Test that items can be suppressed in usage messages"""
3810
3811 parser_signature = Sig(prog='PROG', usage=argparse.SUPPRESS)
3812 argument_signatures = [
3813 Sig('--foo', help='foo help'),
3814 Sig('spam', help='spam help'),
3815 ]
3816 argument_group_signatures = []
3817 help = '''\
3818 positional arguments:
3819 spam spam help
3820
3821 optional arguments:
3822 -h, --help show this help message and exit
3823 --foo FOO foo help
3824 '''
3825 usage = ''
3826 version = ''
3827
3828
3829class TestHelpSuppressOptional(HelpTestCase):
3830 """Test that optional arguments can be suppressed in help messages"""
3831
3832 parser_signature = Sig(prog='PROG', add_help=False)
3833 argument_signatures = [
3834 Sig('--foo', help=argparse.SUPPRESS),
3835 Sig('spam', help='spam help'),
3836 ]
3837 argument_group_signatures = []
3838 usage = '''\
3839 usage: PROG spam
3840 '''
3841 help = usage + '''\
3842
3843 positional arguments:
3844 spam spam help
3845 '''
3846 version = ''
3847
3848
3849class TestHelpSuppressOptionalGroup(HelpTestCase):
3850 """Test that optional groups can be suppressed in help messages"""
3851
3852 parser_signature = Sig(prog='PROG')
3853 argument_signatures = [
3854 Sig('--foo', help='foo help'),
3855 Sig('spam', help='spam help'),
3856 ]
3857 argument_group_signatures = [
3858 (Sig('group'), [Sig('--bar', help=argparse.SUPPRESS)]),
3859 ]
3860 usage = '''\
3861 usage: PROG [-h] [--foo FOO] spam
3862 '''
3863 help = usage + '''\
3864
3865 positional arguments:
3866 spam spam help
3867
3868 optional arguments:
3869 -h, --help show this help message and exit
3870 --foo FOO foo help
3871 '''
3872 version = ''
3873
3874
3875class TestHelpSuppressPositional(HelpTestCase):
3876 """Test that positional arguments can be suppressed in help messages"""
3877
3878 parser_signature = Sig(prog='PROG')
3879 argument_signatures = [
3880 Sig('--foo', help='foo help'),
3881 Sig('spam', help=argparse.SUPPRESS),
3882 ]
3883 argument_group_signatures = []
3884 usage = '''\
3885 usage: PROG [-h] [--foo FOO]
3886 '''
3887 help = usage + '''\
3888
3889 optional arguments:
3890 -h, --help show this help message and exit
3891 --foo FOO foo help
3892 '''
3893 version = ''
3894
3895
3896class TestHelpRequiredOptional(HelpTestCase):
3897 """Test that required options don't look optional"""
3898
3899 parser_signature = Sig(prog='PROG')
3900 argument_signatures = [
3901 Sig('--foo', required=True, help='foo help'),
3902 ]
3903 argument_group_signatures = []
3904 usage = '''\
3905 usage: PROG [-h] --foo FOO
3906 '''
3907 help = usage + '''\
3908
3909 optional arguments:
3910 -h, --help show this help message and exit
3911 --foo FOO foo help
3912 '''
3913 version = ''
3914
3915
3916class TestHelpAlternatePrefixChars(HelpTestCase):
3917 """Test that options display with different prefix characters"""
3918
3919 parser_signature = Sig(prog='PROG', prefix_chars='^;', add_help=False)
3920 argument_signatures = [
3921 Sig('^^foo', action='store_true', help='foo help'),
3922 Sig(';b', ';;bar', help='bar help'),
3923 ]
3924 argument_group_signatures = []
3925 usage = '''\
3926 usage: PROG [^^foo] [;b BAR]
3927 '''
3928 help = usage + '''\
3929
3930 optional arguments:
3931 ^^foo foo help
3932 ;b BAR, ;;bar BAR bar help
3933 '''
3934 version = ''
3935
3936
3937class TestHelpNoHelpOptional(HelpTestCase):
3938 """Test that the --help argument can be suppressed help messages"""
3939
3940 parser_signature = Sig(prog='PROG', add_help=False)
3941 argument_signatures = [
3942 Sig('--foo', help='foo help'),
3943 Sig('spam', help='spam help'),
3944 ]
3945 argument_group_signatures = []
3946 usage = '''\
3947 usage: PROG [--foo FOO] spam
3948 '''
3949 help = usage + '''\
3950
3951 positional arguments:
3952 spam spam help
3953
3954 optional arguments:
3955 --foo FOO foo help
3956 '''
3957 version = ''
3958
3959
Benjamin Peterson698a18a2010-03-02 22:34:37 +00003960class TestHelpNone(HelpTestCase):
3961 """Test that no errors occur if no help is specified"""
3962
3963 parser_signature = Sig(prog='PROG')
3964 argument_signatures = [
3965 Sig('--foo'),
3966 Sig('spam'),
3967 ]
3968 argument_group_signatures = []
3969 usage = '''\
3970 usage: PROG [-h] [--foo FOO] spam
3971 '''
3972 help = usage + '''\
3973
3974 positional arguments:
3975 spam
3976
3977 optional arguments:
3978 -h, --help show this help message and exit
3979 --foo FOO
3980 '''
3981 version = ''
3982
3983
3984class TestHelpTupleMetavar(HelpTestCase):
3985 """Test specifying metavar as a tuple"""
3986
3987 parser_signature = Sig(prog='PROG')
3988 argument_signatures = [
3989 Sig('-w', help='w', nargs='+', metavar=('W1', 'W2')),
3990 Sig('-x', help='x', nargs='*', metavar=('X1', 'X2')),
3991 Sig('-y', help='y', nargs=3, metavar=('Y1', 'Y2', 'Y3')),
3992 Sig('-z', help='z', nargs='?', metavar=('Z1', )),
3993 ]
3994 argument_group_signatures = []
3995 usage = '''\
3996 usage: PROG [-h] [-w W1 [W2 ...]] [-x [X1 [X2 ...]]] [-y Y1 Y2 Y3] \
3997[-z [Z1]]
3998 '''
3999 help = usage + '''\
4000
4001 optional arguments:
4002 -h, --help show this help message and exit
4003 -w W1 [W2 ...] w
4004 -x [X1 [X2 ...]] x
4005 -y Y1 Y2 Y3 y
4006 -z [Z1] z
4007 '''
4008 version = ''
4009
4010
4011class TestHelpRawText(HelpTestCase):
4012 """Test the RawTextHelpFormatter"""
4013
4014 parser_signature = Sig(
4015 prog='PROG', formatter_class=argparse.RawTextHelpFormatter,
4016 description='Keep the formatting\n'
4017 ' exactly as it is written\n'
4018 '\n'
4019 'here\n')
4020
4021 argument_signatures = [
4022 Sig('--foo', help=' foo help should also\n'
4023 'appear as given here'),
4024 Sig('spam', help='spam help'),
4025 ]
4026 argument_group_signatures = [
4027 (Sig('title', description=' This text\n'
4028 ' should be indented\n'
4029 ' exactly like it is here\n'),
4030 [Sig('--bar', help='bar help')]),
4031 ]
4032 usage = '''\
4033 usage: PROG [-h] [--foo FOO] [--bar BAR] spam
4034 '''
4035 help = usage + '''\
4036
4037 Keep the formatting
4038 exactly as it is written
4039
4040 here
4041
4042 positional arguments:
4043 spam spam help
4044
4045 optional arguments:
4046 -h, --help show this help message and exit
4047 --foo FOO foo help should also
4048 appear as given here
4049
4050 title:
4051 This text
4052 should be indented
4053 exactly like it is here
4054
4055 --bar BAR bar help
4056 '''
4057 version = ''
4058
4059
4060class TestHelpRawDescription(HelpTestCase):
4061 """Test the RawTextHelpFormatter"""
4062
4063 parser_signature = Sig(
4064 prog='PROG', formatter_class=argparse.RawDescriptionHelpFormatter,
4065 description='Keep the formatting\n'
4066 ' exactly as it is written\n'
4067 '\n'
4068 'here\n')
4069
4070 argument_signatures = [
4071 Sig('--foo', help=' foo help should not\n'
4072 ' retain this odd formatting'),
4073 Sig('spam', help='spam help'),
4074 ]
4075 argument_group_signatures = [
4076 (Sig('title', description=' This text\n'
4077 ' should be indented\n'
4078 ' exactly like it is here\n'),
4079 [Sig('--bar', help='bar help')]),
4080 ]
4081 usage = '''\
4082 usage: PROG [-h] [--foo FOO] [--bar BAR] spam
4083 '''
4084 help = usage + '''\
4085
4086 Keep the formatting
4087 exactly as it is written
4088
4089 here
4090
4091 positional arguments:
4092 spam spam help
4093
4094 optional arguments:
4095 -h, --help show this help message and exit
4096 --foo FOO foo help should not retain this odd formatting
4097
4098 title:
4099 This text
4100 should be indented
4101 exactly like it is here
4102
4103 --bar BAR bar help
4104 '''
4105 version = ''
4106
4107
4108class TestHelpArgumentDefaults(HelpTestCase):
4109 """Test the ArgumentDefaultsHelpFormatter"""
4110
4111 parser_signature = Sig(
4112 prog='PROG', formatter_class=argparse.ArgumentDefaultsHelpFormatter,
4113 description='description')
4114
4115 argument_signatures = [
4116 Sig('--foo', help='foo help - oh and by the way, %(default)s'),
4117 Sig('--bar', action='store_true', help='bar help'),
4118 Sig('spam', help='spam help'),
4119 Sig('badger', nargs='?', default='wooden', help='badger help'),
4120 ]
4121 argument_group_signatures = [
4122 (Sig('title', description='description'),
4123 [Sig('--baz', type=int, default=42, help='baz help')]),
4124 ]
4125 usage = '''\
4126 usage: PROG [-h] [--foo FOO] [--bar] [--baz BAZ] spam [badger]
4127 '''
4128 help = usage + '''\
4129
4130 description
4131
4132 positional arguments:
4133 spam spam help
4134 badger badger help (default: wooden)
4135
4136 optional arguments:
4137 -h, --help show this help message and exit
4138 --foo FOO foo help - oh and by the way, None
4139 --bar bar help (default: False)
4140
4141 title:
4142 description
4143
4144 --baz BAZ baz help (default: 42)
4145 '''
4146 version = ''
4147
Steven Bethard50fe5932010-05-24 03:47:38 +00004148class TestHelpVersionAction(HelpTestCase):
4149 """Test the default help for the version action"""
4150
4151 parser_signature = Sig(prog='PROG', description='description')
4152 argument_signatures = [Sig('-V', '--version', action='version', version='3.6')]
4153 argument_group_signatures = []
4154 usage = '''\
4155 usage: PROG [-h] [-V]
4156 '''
4157 help = usage + '''\
4158
4159 description
4160
4161 optional arguments:
4162 -h, --help show this help message and exit
4163 -V, --version show program's version number and exit
4164 '''
4165 version = ''
4166
Berker Peksagecb75e22015-04-10 16:11:12 +03004167
4168class TestHelpVersionActionSuppress(HelpTestCase):
4169 """Test that the --version argument can be suppressed in help messages"""
4170
4171 parser_signature = Sig(prog='PROG')
4172 argument_signatures = [
4173 Sig('-v', '--version', action='version', version='1.0',
4174 help=argparse.SUPPRESS),
4175 Sig('--foo', help='foo help'),
4176 Sig('spam', help='spam help'),
4177 ]
4178 argument_group_signatures = []
4179 usage = '''\
4180 usage: PROG [-h] [--foo FOO] spam
4181 '''
4182 help = usage + '''\
4183
4184 positional arguments:
4185 spam spam help
4186
4187 optional arguments:
4188 -h, --help show this help message and exit
4189 --foo FOO foo help
4190 '''
4191
4192
Steven Bethard8a6a1982011-03-27 13:53:53 +02004193class TestHelpSubparsersOrdering(HelpTestCase):
4194 """Test ordering of subcommands in help matches the code"""
4195 parser_signature = Sig(prog='PROG',
Florent Xiclunaaf1adbe2012-07-07 17:02:22 +02004196 description='display some subcommands')
4197 argument_signatures = [Sig('-v', '--version', action='version', version='0.1')]
Steven Bethard8a6a1982011-03-27 13:53:53 +02004198
4199 subparsers_signatures = [Sig(name=name)
4200 for name in ('a', 'b', 'c', 'd', 'e')]
4201
4202 usage = '''\
4203 usage: PROG [-h] [-v] {a,b,c,d,e} ...
4204 '''
4205
4206 help = usage + '''\
4207
4208 display some subcommands
4209
4210 positional arguments:
4211 {a,b,c,d,e}
4212
4213 optional arguments:
4214 -h, --help show this help message and exit
4215 -v, --version show program's version number and exit
4216 '''
4217
4218 version = '''\
4219 0.1
4220 '''
4221
4222class TestHelpSubparsersWithHelpOrdering(HelpTestCase):
4223 """Test ordering of subcommands in help matches the code"""
4224 parser_signature = Sig(prog='PROG',
Florent Xiclunaaf1adbe2012-07-07 17:02:22 +02004225 description='display some subcommands')
4226 argument_signatures = [Sig('-v', '--version', action='version', version='0.1')]
Steven Bethard8a6a1982011-03-27 13:53:53 +02004227
4228 subcommand_data = (('a', 'a subcommand help'),
4229 ('b', 'b subcommand help'),
4230 ('c', 'c subcommand help'),
4231 ('d', 'd subcommand help'),
4232 ('e', 'e subcommand help'),
4233 )
4234
4235 subparsers_signatures = [Sig(name=name, help=help)
4236 for name, help in subcommand_data]
4237
4238 usage = '''\
4239 usage: PROG [-h] [-v] {a,b,c,d,e} ...
4240 '''
4241
4242 help = usage + '''\
4243
4244 display some subcommands
4245
4246 positional arguments:
4247 {a,b,c,d,e}
4248 a a subcommand help
4249 b b subcommand help
4250 c c subcommand help
4251 d d subcommand help
4252 e e subcommand help
4253
4254 optional arguments:
4255 -h, --help show this help message and exit
4256 -v, --version show program's version number and exit
4257 '''
4258
4259 version = '''\
4260 0.1
4261 '''
4262
4263
Steven Bethard0331e902011-03-26 14:48:04 +01004264
4265class TestHelpMetavarTypeFormatter(HelpTestCase):
Steven Bethard0331e902011-03-26 14:48:04 +01004266
4267 def custom_type(string):
4268 return string
4269
4270 parser_signature = Sig(prog='PROG', description='description',
4271 formatter_class=argparse.MetavarTypeHelpFormatter)
4272 argument_signatures = [Sig('a', type=int),
4273 Sig('-b', type=custom_type),
4274 Sig('-c', type=float, metavar='SOME FLOAT')]
4275 argument_group_signatures = []
4276 usage = '''\
4277 usage: PROG [-h] [-b custom_type] [-c SOME FLOAT] int
4278 '''
4279 help = usage + '''\
4280
4281 description
4282
4283 positional arguments:
4284 int
4285
4286 optional arguments:
4287 -h, --help show this help message and exit
4288 -b custom_type
4289 -c SOME FLOAT
4290 '''
4291 version = ''
4292
4293
Benjamin Peterson698a18a2010-03-02 22:34:37 +00004294# =====================================
4295# Optional/Positional constructor tests
4296# =====================================
4297
4298class TestInvalidArgumentConstructors(TestCase):
4299 """Test a bunch of invalid Argument constructors"""
4300
4301 def assertTypeError(self, *args, **kwargs):
4302 parser = argparse.ArgumentParser()
4303 self.assertRaises(TypeError, parser.add_argument,
4304 *args, **kwargs)
4305
4306 def assertValueError(self, *args, **kwargs):
4307 parser = argparse.ArgumentParser()
4308 self.assertRaises(ValueError, parser.add_argument,
4309 *args, **kwargs)
4310
4311 def test_invalid_keyword_arguments(self):
4312 self.assertTypeError('-x', bar=None)
4313 self.assertTypeError('-y', callback='foo')
4314 self.assertTypeError('-y', callback_args=())
4315 self.assertTypeError('-y', callback_kwargs={})
4316
4317 def test_missing_destination(self):
4318 self.assertTypeError()
4319 for action in ['append', 'store']:
4320 self.assertTypeError(action=action)
4321
4322 def test_invalid_option_strings(self):
4323 self.assertValueError('--')
4324 self.assertValueError('---')
4325
4326 def test_invalid_type(self):
4327 self.assertValueError('--foo', type='int')
Steven Bethard7cb20a82011-04-04 01:53:02 +02004328 self.assertValueError('--foo', type=(int, float))
Benjamin Peterson698a18a2010-03-02 22:34:37 +00004329
4330 def test_invalid_action(self):
4331 self.assertValueError('-x', action='foo')
4332 self.assertValueError('foo', action='baz')
Steven Bethard7cb20a82011-04-04 01:53:02 +02004333 self.assertValueError('--foo', action=('store', 'append'))
Benjamin Peterson698a18a2010-03-02 22:34:37 +00004334 parser = argparse.ArgumentParser()
Berker Peksag1c5f56a2014-07-06 09:33:20 +03004335 with self.assertRaises(ValueError) as cm:
Benjamin Peterson698a18a2010-03-02 22:34:37 +00004336 parser.add_argument("--foo", action="store-true")
Berker Peksag1c5f56a2014-07-06 09:33:20 +03004337 self.assertIn('unknown action', str(cm.exception))
Benjamin Peterson698a18a2010-03-02 22:34:37 +00004338
4339 def test_multiple_dest(self):
4340 parser = argparse.ArgumentParser()
4341 parser.add_argument(dest='foo')
Berker Peksag1c5f56a2014-07-06 09:33:20 +03004342 with self.assertRaises(ValueError) as cm:
Benjamin Peterson698a18a2010-03-02 22:34:37 +00004343 parser.add_argument('bar', dest='baz')
Berker Peksag1c5f56a2014-07-06 09:33:20 +03004344 self.assertIn('dest supplied twice for positional argument',
4345 str(cm.exception))
Benjamin Peterson698a18a2010-03-02 22:34:37 +00004346
4347 def test_no_argument_actions(self):
4348 for action in ['store_const', 'store_true', 'store_false',
4349 'append_const', 'count']:
4350 for attrs in [dict(type=int), dict(nargs='+'),
4351 dict(choices='ab')]:
4352 self.assertTypeError('-x', action=action, **attrs)
4353
4354 def test_no_argument_no_const_actions(self):
4355 # options with zero arguments
4356 for action in ['store_true', 'store_false', 'count']:
4357
4358 # const is always disallowed
4359 self.assertTypeError('-x', const='foo', action=action)
4360
4361 # nargs is always disallowed
4362 self.assertTypeError('-x', nargs='*', action=action)
4363
4364 def test_more_than_one_argument_actions(self):
4365 for action in ['store', 'append']:
4366
4367 # nargs=0 is disallowed
4368 self.assertValueError('-x', nargs=0, action=action)
4369 self.assertValueError('spam', nargs=0, action=action)
4370
4371 # const is disallowed with non-optional arguments
4372 for nargs in [1, '*', '+']:
4373 self.assertValueError('-x', const='foo',
4374 nargs=nargs, action=action)
4375 self.assertValueError('spam', const='foo',
4376 nargs=nargs, action=action)
4377
4378 def test_required_const_actions(self):
4379 for action in ['store_const', 'append_const']:
4380
4381 # nargs is always disallowed
4382 self.assertTypeError('-x', nargs='+', action=action)
4383
4384 def test_parsers_action_missing_params(self):
4385 self.assertTypeError('command', action='parsers')
4386 self.assertTypeError('command', action='parsers', prog='PROG')
4387 self.assertTypeError('command', action='parsers',
4388 parser_class=argparse.ArgumentParser)
4389
4390 def test_required_positional(self):
4391 self.assertTypeError('foo', required=True)
4392
4393 def test_user_defined_action(self):
4394
4395 class Success(Exception):
4396 pass
4397
4398 class Action(object):
4399
4400 def __init__(self,
4401 option_strings,
4402 dest,
4403 const,
4404 default,
4405 required=False):
4406 if dest == 'spam':
4407 if const is Success:
4408 if default is Success:
4409 raise Success()
4410
4411 def __call__(self, *args, **kwargs):
4412 pass
4413
4414 parser = argparse.ArgumentParser()
4415 self.assertRaises(Success, parser.add_argument, '--spam',
4416 action=Action, default=Success, const=Success)
4417 self.assertRaises(Success, parser.add_argument, 'spam',
4418 action=Action, default=Success, const=Success)
4419
4420# ================================
4421# Actions returned by add_argument
4422# ================================
4423
4424class TestActionsReturned(TestCase):
4425
4426 def test_dest(self):
4427 parser = argparse.ArgumentParser()
4428 action = parser.add_argument('--foo')
4429 self.assertEqual(action.dest, 'foo')
4430 action = parser.add_argument('-b', '--bar')
4431 self.assertEqual(action.dest, 'bar')
4432 action = parser.add_argument('-x', '-y')
4433 self.assertEqual(action.dest, 'x')
4434
4435 def test_misc(self):
4436 parser = argparse.ArgumentParser()
4437 action = parser.add_argument('--foo', nargs='?', const=42,
4438 default=84, type=int, choices=[1, 2],
4439 help='FOO', metavar='BAR', dest='baz')
4440 self.assertEqual(action.nargs, '?')
4441 self.assertEqual(action.const, 42)
4442 self.assertEqual(action.default, 84)
4443 self.assertEqual(action.type, int)
4444 self.assertEqual(action.choices, [1, 2])
4445 self.assertEqual(action.help, 'FOO')
4446 self.assertEqual(action.metavar, 'BAR')
4447 self.assertEqual(action.dest, 'baz')
4448
4449
4450# ================================
4451# Argument conflict handling tests
4452# ================================
4453
4454class TestConflictHandling(TestCase):
4455
4456 def test_bad_type(self):
4457 self.assertRaises(ValueError, argparse.ArgumentParser,
4458 conflict_handler='foo')
4459
4460 def test_conflict_error(self):
4461 parser = argparse.ArgumentParser()
4462 parser.add_argument('-x')
4463 self.assertRaises(argparse.ArgumentError,
4464 parser.add_argument, '-x')
4465 parser.add_argument('--spam')
4466 self.assertRaises(argparse.ArgumentError,
4467 parser.add_argument, '--spam')
4468
4469 def test_resolve_error(self):
4470 get_parser = argparse.ArgumentParser
4471 parser = get_parser(prog='PROG', conflict_handler='resolve')
4472
4473 parser.add_argument('-x', help='OLD X')
4474 parser.add_argument('-x', help='NEW X')
4475 self.assertEqual(parser.format_help(), textwrap.dedent('''\
4476 usage: PROG [-h] [-x X]
4477
4478 optional arguments:
4479 -h, --help show this help message and exit
4480 -x X NEW X
4481 '''))
4482
4483 parser.add_argument('--spam', metavar='OLD_SPAM')
4484 parser.add_argument('--spam', metavar='NEW_SPAM')
4485 self.assertEqual(parser.format_help(), textwrap.dedent('''\
4486 usage: PROG [-h] [-x X] [--spam NEW_SPAM]
4487
4488 optional arguments:
4489 -h, --help show this help message and exit
4490 -x X NEW X
4491 --spam NEW_SPAM
4492 '''))
4493
4494
4495# =============================
4496# Help and Version option tests
4497# =============================
4498
4499class TestOptionalsHelpVersionActions(TestCase):
4500 """Test the help and version actions"""
4501
Benjamin Peterson698a18a2010-03-02 22:34:37 +00004502 def assertPrintHelpExit(self, parser, args_str):
Berker Peksag1c5f56a2014-07-06 09:33:20 +03004503 with self.assertRaises(ArgumentParserError) as cm:
4504 parser.parse_args(args_str.split())
4505 self.assertEqual(parser.format_help(), cm.exception.stdout)
Benjamin Peterson698a18a2010-03-02 22:34:37 +00004506
Benjamin Peterson698a18a2010-03-02 22:34:37 +00004507 def assertArgumentParserError(self, parser, *args):
4508 self.assertRaises(ArgumentParserError, parser.parse_args, args)
4509
4510 def test_version(self):
Florent Xiclunaaf1adbe2012-07-07 17:02:22 +02004511 parser = ErrorRaisingArgumentParser()
4512 parser.add_argument('-v', '--version', action='version', version='1.0')
Benjamin Peterson698a18a2010-03-02 22:34:37 +00004513 self.assertPrintHelpExit(parser, '-h')
4514 self.assertPrintHelpExit(parser, '--help')
Florent Xiclunaaf1adbe2012-07-07 17:02:22 +02004515 self.assertRaises(AttributeError, getattr, parser, 'format_version')
Benjamin Peterson698a18a2010-03-02 22:34:37 +00004516
4517 def test_version_format(self):
Florent Xiclunaaf1adbe2012-07-07 17:02:22 +02004518 parser = ErrorRaisingArgumentParser(prog='PPP')
4519 parser.add_argument('-v', '--version', action='version', version='%(prog)s 3.5')
Berker Peksag1c5f56a2014-07-06 09:33:20 +03004520 with self.assertRaises(ArgumentParserError) as cm:
4521 parser.parse_args(['-v'])
4522 self.assertEqual('PPP 3.5\n', cm.exception.stdout)
Benjamin Peterson698a18a2010-03-02 22:34:37 +00004523
4524 def test_version_no_help(self):
Florent Xiclunaaf1adbe2012-07-07 17:02:22 +02004525 parser = ErrorRaisingArgumentParser(add_help=False)
4526 parser.add_argument('-v', '--version', action='version', version='1.0')
Benjamin Peterson698a18a2010-03-02 22:34:37 +00004527 self.assertArgumentParserError(parser, '-h')
4528 self.assertArgumentParserError(parser, '--help')
Florent Xiclunaaf1adbe2012-07-07 17:02:22 +02004529 self.assertRaises(AttributeError, getattr, parser, 'format_version')
Benjamin Peterson698a18a2010-03-02 22:34:37 +00004530
4531 def test_version_action(self):
4532 parser = ErrorRaisingArgumentParser(prog='XXX')
4533 parser.add_argument('-V', action='version', version='%(prog)s 3.7')
Berker Peksag1c5f56a2014-07-06 09:33:20 +03004534 with self.assertRaises(ArgumentParserError) as cm:
4535 parser.parse_args(['-V'])
4536 self.assertEqual('XXX 3.7\n', cm.exception.stdout)
Benjamin Peterson698a18a2010-03-02 22:34:37 +00004537
4538 def test_no_help(self):
4539 parser = ErrorRaisingArgumentParser(add_help=False)
4540 self.assertArgumentParserError(parser, '-h')
4541 self.assertArgumentParserError(parser, '--help')
4542 self.assertArgumentParserError(parser, '-v')
4543 self.assertArgumentParserError(parser, '--version')
4544
4545 def test_alternate_help_version(self):
4546 parser = ErrorRaisingArgumentParser()
4547 parser.add_argument('-x', action='help')
4548 parser.add_argument('-y', action='version')
4549 self.assertPrintHelpExit(parser, '-x')
Benjamin Peterson698a18a2010-03-02 22:34:37 +00004550 self.assertArgumentParserError(parser, '-v')
4551 self.assertArgumentParserError(parser, '--version')
Florent Xiclunaaf1adbe2012-07-07 17:02:22 +02004552 self.assertRaises(AttributeError, getattr, parser, 'format_version')
Benjamin Peterson698a18a2010-03-02 22:34:37 +00004553
4554 def test_help_version_extra_arguments(self):
Florent Xiclunaaf1adbe2012-07-07 17:02:22 +02004555 parser = ErrorRaisingArgumentParser()
4556 parser.add_argument('--version', action='version', version='1.0')
Benjamin Peterson698a18a2010-03-02 22:34:37 +00004557 parser.add_argument('-x', action='store_true')
4558 parser.add_argument('y')
4559
4560 # try all combinations of valid prefixes and suffixes
4561 valid_prefixes = ['', '-x', 'foo', '-x bar', 'baz -x']
4562 valid_suffixes = valid_prefixes + ['--bad-option', 'foo bar baz']
4563 for prefix in valid_prefixes:
4564 for suffix in valid_suffixes:
4565 format = '%s %%s %s' % (prefix, suffix)
4566 self.assertPrintHelpExit(parser, format % '-h')
4567 self.assertPrintHelpExit(parser, format % '--help')
Florent Xiclunaaf1adbe2012-07-07 17:02:22 +02004568 self.assertRaises(AttributeError, getattr, parser, 'format_version')
Benjamin Peterson698a18a2010-03-02 22:34:37 +00004569
4570
4571# ======================
4572# str() and repr() tests
4573# ======================
4574
4575class TestStrings(TestCase):
4576 """Test str() and repr() on Optionals and Positionals"""
4577
4578 def assertStringEqual(self, obj, result_string):
4579 for func in [str, repr]:
4580 self.assertEqual(func(obj), result_string)
4581
4582 def test_optional(self):
4583 option = argparse.Action(
4584 option_strings=['--foo', '-a', '-b'],
4585 dest='b',
4586 type='int',
4587 nargs='+',
4588 default=42,
4589 choices=[1, 2, 3],
4590 help='HELP',
4591 metavar='METAVAR')
4592 string = (
4593 "Action(option_strings=['--foo', '-a', '-b'], dest='b', "
4594 "nargs='+', const=None, default=42, type='int', "
4595 "choices=[1, 2, 3], help='HELP', metavar='METAVAR')")
4596 self.assertStringEqual(option, string)
4597
4598 def test_argument(self):
4599 argument = argparse.Action(
4600 option_strings=[],
4601 dest='x',
4602 type=float,
4603 nargs='?',
4604 default=2.5,
4605 choices=[0.5, 1.5, 2.5],
4606 help='H HH H',
4607 metavar='MV MV MV')
4608 string = (
4609 "Action(option_strings=[], dest='x', nargs='?', "
4610 "const=None, default=2.5, type=%r, choices=[0.5, 1.5, 2.5], "
4611 "help='H HH H', metavar='MV MV MV')" % float)
4612 self.assertStringEqual(argument, string)
4613
4614 def test_namespace(self):
4615 ns = argparse.Namespace(foo=42, bar='spam')
4616 string = "Namespace(bar='spam', foo=42)"
4617 self.assertStringEqual(ns, string)
4618
Berker Peksag76b17142015-07-29 23:51:47 +03004619 def test_namespace_starkwargs_notidentifier(self):
4620 ns = argparse.Namespace(**{'"': 'quote'})
4621 string = """Namespace(**{'"': 'quote'})"""
4622 self.assertStringEqual(ns, string)
4623
4624 def test_namespace_kwargs_and_starkwargs_notidentifier(self):
4625 ns = argparse.Namespace(a=1, **{'"': 'quote'})
4626 string = """Namespace(a=1, **{'"': 'quote'})"""
4627 self.assertStringEqual(ns, string)
4628
4629 def test_namespace_starkwargs_identifier(self):
4630 ns = argparse.Namespace(**{'valid': True})
4631 string = "Namespace(valid=True)"
4632 self.assertStringEqual(ns, string)
4633
Benjamin Peterson698a18a2010-03-02 22:34:37 +00004634 def test_parser(self):
4635 parser = argparse.ArgumentParser(prog='PROG')
4636 string = (
4637 "ArgumentParser(prog='PROG', usage=None, description=None, "
Florent Xiclunaaf1adbe2012-07-07 17:02:22 +02004638 "formatter_class=%r, conflict_handler='error', "
Benjamin Peterson698a18a2010-03-02 22:34:37 +00004639 "add_help=True)" % argparse.HelpFormatter)
4640 self.assertStringEqual(parser, string)
4641
4642# ===============
4643# Namespace tests
4644# ===============
4645
4646class TestNamespace(TestCase):
4647
4648 def test_constructor(self):
4649 ns = argparse.Namespace()
4650 self.assertRaises(AttributeError, getattr, ns, 'x')
4651
4652 ns = argparse.Namespace(a=42, b='spam')
4653 self.assertEqual(ns.a, 42)
4654 self.assertEqual(ns.b, 'spam')
4655
4656 def test_equality(self):
4657 ns1 = argparse.Namespace(a=1, b=2)
4658 ns2 = argparse.Namespace(b=2, a=1)
4659 ns3 = argparse.Namespace(a=1)
4660 ns4 = argparse.Namespace(b=2)
4661
4662 self.assertEqual(ns1, ns2)
4663 self.assertNotEqual(ns1, ns3)
4664 self.assertNotEqual(ns1, ns4)
4665 self.assertNotEqual(ns2, ns3)
4666 self.assertNotEqual(ns2, ns4)
4667 self.assertTrue(ns1 != ns3)
4668 self.assertTrue(ns1 != ns4)
4669 self.assertTrue(ns2 != ns3)
4670 self.assertTrue(ns2 != ns4)
4671
Berker Peksagc16387b2016-09-28 17:21:52 +03004672 def test_equality_returns_notimplemented(self):
Raymond Hettingerdea46ec2014-05-26 00:43:27 -07004673 # See issue 21481
4674 ns = argparse.Namespace(a=1, b=2)
4675 self.assertIs(ns.__eq__(None), NotImplemented)
4676 self.assertIs(ns.__ne__(None), NotImplemented)
4677
Benjamin Peterson698a18a2010-03-02 22:34:37 +00004678
4679# ===================
4680# File encoding tests
4681# ===================
4682
4683class TestEncoding(TestCase):
4684
4685 def _test_module_encoding(self, path):
4686 path, _ = os.path.splitext(path)
4687 path += ".py"
Victor Stinner272d8882017-06-16 08:59:01 +02004688 with open(path, 'r', encoding='utf-8') as f:
Antoine Pitroub86680e2010-10-14 21:15:17 +00004689 f.read()
Benjamin Peterson698a18a2010-03-02 22:34:37 +00004690
4691 def test_argparse_module_encoding(self):
4692 self._test_module_encoding(argparse.__file__)
4693
4694 def test_test_argparse_module_encoding(self):
4695 self._test_module_encoding(__file__)
4696
4697# ===================
4698# ArgumentError tests
4699# ===================
4700
4701class TestArgumentError(TestCase):
4702
4703 def test_argument_error(self):
4704 msg = "my error here"
4705 error = argparse.ArgumentError(None, msg)
4706 self.assertEqual(str(error), msg)
4707
4708# =======================
4709# ArgumentTypeError tests
4710# =======================
4711
R. David Murray722b5fd2010-11-20 03:48:58 +00004712class TestArgumentTypeError(TestCase):
Benjamin Peterson698a18a2010-03-02 22:34:37 +00004713
4714 def test_argument_type_error(self):
4715
4716 def spam(string):
4717 raise argparse.ArgumentTypeError('spam!')
4718
4719 parser = ErrorRaisingArgumentParser(prog='PROG', add_help=False)
4720 parser.add_argument('x', type=spam)
Berker Peksag1c5f56a2014-07-06 09:33:20 +03004721 with self.assertRaises(ArgumentParserError) as cm:
Benjamin Peterson698a18a2010-03-02 22:34:37 +00004722 parser.parse_args(['XXX'])
Berker Peksag1c5f56a2014-07-06 09:33:20 +03004723 self.assertEqual('usage: PROG x\nPROG: error: argument x: spam!\n',
4724 cm.exception.stderr)
Benjamin Peterson698a18a2010-03-02 22:34:37 +00004725
R David Murrayf97c59a2011-06-09 12:34:07 -04004726# =========================
4727# MessageContentError tests
4728# =========================
4729
4730class TestMessageContentError(TestCase):
4731
4732 def test_missing_argument_name_in_message(self):
4733 parser = ErrorRaisingArgumentParser(prog='PROG', usage='')
4734 parser.add_argument('req_pos', type=str)
4735 parser.add_argument('-req_opt', type=int, required=True)
4736 parser.add_argument('need_one', type=str, nargs='+')
4737
4738 with self.assertRaises(ArgumentParserError) as cm:
4739 parser.parse_args([])
4740 msg = str(cm.exception)
4741 self.assertRegex(msg, 'req_pos')
4742 self.assertRegex(msg, 'req_opt')
4743 self.assertRegex(msg, 'need_one')
4744 with self.assertRaises(ArgumentParserError) as cm:
4745 parser.parse_args(['myXargument'])
4746 msg = str(cm.exception)
4747 self.assertNotIn(msg, 'req_pos')
4748 self.assertRegex(msg, 'req_opt')
4749 self.assertRegex(msg, 'need_one')
4750 with self.assertRaises(ArgumentParserError) as cm:
4751 parser.parse_args(['myXargument', '-req_opt=1'])
4752 msg = str(cm.exception)
4753 self.assertNotIn(msg, 'req_pos')
4754 self.assertNotIn(msg, 'req_opt')
4755 self.assertRegex(msg, 'need_one')
4756
4757 def test_optional_optional_not_in_message(self):
4758 parser = ErrorRaisingArgumentParser(prog='PROG', usage='')
4759 parser.add_argument('req_pos', type=str)
4760 parser.add_argument('--req_opt', type=int, required=True)
4761 parser.add_argument('--opt_opt', type=bool, nargs='?',
4762 default=True)
4763 with self.assertRaises(ArgumentParserError) as cm:
4764 parser.parse_args([])
4765 msg = str(cm.exception)
4766 self.assertRegex(msg, 'req_pos')
4767 self.assertRegex(msg, 'req_opt')
4768 self.assertNotIn(msg, 'opt_opt')
4769 with self.assertRaises(ArgumentParserError) as cm:
4770 parser.parse_args(['--req_opt=1'])
4771 msg = str(cm.exception)
4772 self.assertRegex(msg, 'req_pos')
4773 self.assertNotIn(msg, 'req_opt')
4774 self.assertNotIn(msg, 'opt_opt')
4775
4776 def test_optional_positional_not_in_message(self):
4777 parser = ErrorRaisingArgumentParser(prog='PROG', usage='')
4778 parser.add_argument('req_pos')
4779 parser.add_argument('optional_positional', nargs='?', default='eggs')
4780 with self.assertRaises(ArgumentParserError) as cm:
4781 parser.parse_args([])
4782 msg = str(cm.exception)
4783 self.assertRegex(msg, 'req_pos')
4784 self.assertNotIn(msg, 'optional_positional')
4785
4786
R David Murray6fb8fb12012-08-31 22:45:20 -04004787# ================================================
4788# Check that the type function is called only once
4789# ================================================
4790
4791class TestTypeFunctionCallOnlyOnce(TestCase):
4792
4793 def test_type_function_call_only_once(self):
4794 def spam(string_to_convert):
4795 self.assertEqual(string_to_convert, 'spam!')
4796 return 'foo_converted'
4797
4798 parser = argparse.ArgumentParser()
4799 parser.add_argument('--foo', type=spam, default='bar')
4800 args = parser.parse_args('--foo spam!'.split())
4801 self.assertEqual(NS(foo='foo_converted'), args)
4802
Barry Warsaweaae1b72012-09-12 14:34:50 -04004803# ==================================================================
4804# Check semantics regarding the default argument and type conversion
4805# ==================================================================
R David Murray6fb8fb12012-08-31 22:45:20 -04004806
Barry Warsaweaae1b72012-09-12 14:34:50 -04004807class TestTypeFunctionCalledOnDefault(TestCase):
R David Murray6fb8fb12012-08-31 22:45:20 -04004808
4809 def test_type_function_call_with_non_string_default(self):
4810 def spam(int_to_convert):
4811 self.assertEqual(int_to_convert, 0)
4812 return 'foo_converted'
4813
4814 parser = argparse.ArgumentParser()
4815 parser.add_argument('--foo', type=spam, default=0)
4816 args = parser.parse_args([])
Barry Warsaweaae1b72012-09-12 14:34:50 -04004817 # foo should *not* be converted because its default is not a string.
4818 self.assertEqual(NS(foo=0), args)
4819
4820 def test_type_function_call_with_string_default(self):
4821 def spam(int_to_convert):
4822 return 'foo_converted'
4823
4824 parser = argparse.ArgumentParser()
4825 parser.add_argument('--foo', type=spam, default='0')
4826 args = parser.parse_args([])
4827 # foo is converted because its default is a string.
R David Murray6fb8fb12012-08-31 22:45:20 -04004828 self.assertEqual(NS(foo='foo_converted'), args)
4829
Barry Warsaweaae1b72012-09-12 14:34:50 -04004830 def test_no_double_type_conversion_of_default(self):
4831 def extend(str_to_convert):
4832 return str_to_convert + '*'
4833
4834 parser = argparse.ArgumentParser()
4835 parser.add_argument('--test', type=extend, default='*')
4836 args = parser.parse_args([])
4837 # The test argument will be two stars, one coming from the default
4838 # value and one coming from the type conversion being called exactly
4839 # once.
4840 self.assertEqual(NS(test='**'), args)
4841
Barry Warsaw4b2f9e92012-09-11 22:38:47 -04004842 def test_issue_15906(self):
4843 # Issue #15906: When action='append', type=str, default=[] are
4844 # providing, the dest value was the string representation "[]" when it
4845 # should have been an empty list.
4846 parser = argparse.ArgumentParser()
4847 parser.add_argument('--test', dest='test', type=str,
4848 default=[], action='append')
4849 args = parser.parse_args([])
4850 self.assertEqual(args.test, [])
4851
Benjamin Peterson698a18a2010-03-02 22:34:37 +00004852# ======================
4853# parse_known_args tests
4854# ======================
4855
4856class TestParseKnownArgs(TestCase):
4857
R David Murrayb5228282012-09-08 12:08:01 -04004858 def test_arguments_tuple(self):
4859 parser = argparse.ArgumentParser()
4860 parser.parse_args(())
4861
4862 def test_arguments_list(self):
4863 parser = argparse.ArgumentParser()
4864 parser.parse_args([])
4865
4866 def test_arguments_tuple_positional(self):
4867 parser = argparse.ArgumentParser()
4868 parser.add_argument('x')
4869 parser.parse_args(('x',))
4870
4871 def test_arguments_list_positional(self):
4872 parser = argparse.ArgumentParser()
4873 parser.add_argument('x')
4874 parser.parse_args(['x'])
4875
Benjamin Peterson698a18a2010-03-02 22:34:37 +00004876 def test_optionals(self):
4877 parser = argparse.ArgumentParser()
4878 parser.add_argument('--foo')
4879 args, extras = parser.parse_known_args('--foo F --bar --baz'.split())
4880 self.assertEqual(NS(foo='F'), args)
4881 self.assertEqual(['--bar', '--baz'], extras)
4882
4883 def test_mixed(self):
4884 parser = argparse.ArgumentParser()
4885 parser.add_argument('-v', nargs='?', const=1, type=int)
4886 parser.add_argument('--spam', action='store_false')
4887 parser.add_argument('badger')
4888
4889 argv = ["B", "C", "--foo", "-v", "3", "4"]
4890 args, extras = parser.parse_known_args(argv)
4891 self.assertEqual(NS(v=3, spam=True, badger="B"), args)
4892 self.assertEqual(["C", "--foo", "4"], extras)
4893
R. David Murray0f6b9d22017-09-06 20:25:40 -04004894# ===========================
4895# parse_intermixed_args tests
4896# ===========================
4897
4898class TestIntermixedArgs(TestCase):
4899 def test_basic(self):
4900 # test parsing intermixed optionals and positionals
4901 parser = argparse.ArgumentParser(prog='PROG')
4902 parser.add_argument('--foo', dest='foo')
4903 bar = parser.add_argument('--bar', dest='bar', required=True)
4904 parser.add_argument('cmd')
4905 parser.add_argument('rest', nargs='*', type=int)
4906 argv = 'cmd --foo x 1 --bar y 2 3'.split()
4907 args = parser.parse_intermixed_args(argv)
4908 # rest gets [1,2,3] despite the foo and bar strings
4909 self.assertEqual(NS(bar='y', cmd='cmd', foo='x', rest=[1, 2, 3]), args)
4910
4911 args, extras = parser.parse_known_args(argv)
4912 # cannot parse the '1,2,3'
4913 self.assertEqual(NS(bar='y', cmd='cmd', foo='x', rest=[]), args)
4914 self.assertEqual(["1", "2", "3"], extras)
4915
4916 argv = 'cmd --foo x 1 --error 2 --bar y 3'.split()
4917 args, extras = parser.parse_known_intermixed_args(argv)
4918 # unknown optionals go into extras
4919 self.assertEqual(NS(bar='y', cmd='cmd', foo='x', rest=[1]), args)
4920 self.assertEqual(['--error', '2', '3'], extras)
4921
4922 # restores attributes that were temporarily changed
4923 self.assertIsNone(parser.usage)
4924 self.assertEqual(bar.required, True)
4925
4926 def test_remainder(self):
4927 # Intermixed and remainder are incompatible
4928 parser = ErrorRaisingArgumentParser(prog='PROG')
4929 parser.add_argument('-z')
4930 parser.add_argument('x')
4931 parser.add_argument('y', nargs='...')
4932 argv = 'X A B -z Z'.split()
4933 # intermixed fails with '...' (also 'A...')
4934 # self.assertRaises(TypeError, parser.parse_intermixed_args, argv)
4935 with self.assertRaises(TypeError) as cm:
4936 parser.parse_intermixed_args(argv)
4937 self.assertRegex(str(cm.exception), r'\.\.\.')
4938
4939 def test_exclusive(self):
4940 # mutually exclusive group; intermixed works fine
4941 parser = ErrorRaisingArgumentParser(prog='PROG')
4942 group = parser.add_mutually_exclusive_group(required=True)
4943 group.add_argument('--foo', action='store_true', help='FOO')
4944 group.add_argument('--spam', help='SPAM')
4945 parser.add_argument('badger', nargs='*', default='X', help='BADGER')
4946 args = parser.parse_intermixed_args('1 --foo 2'.split())
4947 self.assertEqual(NS(badger=['1', '2'], foo=True, spam=None), args)
4948 self.assertRaises(ArgumentParserError, parser.parse_intermixed_args, '1 2'.split())
4949 self.assertEqual(group.required, True)
4950
4951 def test_exclusive_incompatible(self):
4952 # mutually exclusive group including positional - fail
4953 parser = ErrorRaisingArgumentParser(prog='PROG')
4954 group = parser.add_mutually_exclusive_group(required=True)
4955 group.add_argument('--foo', action='store_true', help='FOO')
4956 group.add_argument('--spam', help='SPAM')
4957 group.add_argument('badger', nargs='*', default='X', help='BADGER')
4958 self.assertRaises(TypeError, parser.parse_intermixed_args, [])
4959 self.assertEqual(group.required, True)
4960
4961class TestIntermixedMessageContentError(TestCase):
4962 # case where Intermixed gives different error message
4963 # error is raised by 1st parsing step
4964 def test_missing_argument_name_in_message(self):
4965 parser = ErrorRaisingArgumentParser(prog='PROG', usage='')
4966 parser.add_argument('req_pos', type=str)
4967 parser.add_argument('-req_opt', type=int, required=True)
4968
4969 with self.assertRaises(ArgumentParserError) as cm:
4970 parser.parse_args([])
4971 msg = str(cm.exception)
4972 self.assertRegex(msg, 'req_pos')
4973 self.assertRegex(msg, 'req_opt')
4974
4975 with self.assertRaises(ArgumentParserError) as cm:
4976 parser.parse_intermixed_args([])
4977 msg = str(cm.exception)
4978 self.assertNotRegex(msg, 'req_pos')
4979 self.assertRegex(msg, 'req_opt')
4980
Steven Bethard8d9a4622011-03-26 17:33:56 +01004981# ==========================
4982# add_argument metavar tests
4983# ==========================
4984
4985class TestAddArgumentMetavar(TestCase):
4986
4987 EXPECTED_MESSAGE = "length of metavar tuple does not match nargs"
4988
4989 def do_test_no_exception(self, nargs, metavar):
4990 parser = argparse.ArgumentParser()
4991 parser.add_argument("--foo", nargs=nargs, metavar=metavar)
4992
4993 def do_test_exception(self, nargs, metavar):
4994 parser = argparse.ArgumentParser()
4995 with self.assertRaises(ValueError) as cm:
4996 parser.add_argument("--foo", nargs=nargs, metavar=metavar)
4997 self.assertEqual(cm.exception.args[0], self.EXPECTED_MESSAGE)
4998
4999 # Unit tests for different values of metavar when nargs=None
5000
5001 def test_nargs_None_metavar_string(self):
5002 self.do_test_no_exception(nargs=None, metavar="1")
5003
5004 def test_nargs_None_metavar_length0(self):
5005 self.do_test_exception(nargs=None, metavar=tuple())
5006
5007 def test_nargs_None_metavar_length1(self):
wim glenn66f02aa2018-06-08 05:12:49 -05005008 self.do_test_no_exception(nargs=None, metavar=("1",))
Steven Bethard8d9a4622011-03-26 17:33:56 +01005009
5010 def test_nargs_None_metavar_length2(self):
5011 self.do_test_exception(nargs=None, metavar=("1", "2"))
5012
5013 def test_nargs_None_metavar_length3(self):
5014 self.do_test_exception(nargs=None, metavar=("1", "2", "3"))
5015
5016 # Unit tests for different values of metavar when nargs=?
5017
5018 def test_nargs_optional_metavar_string(self):
5019 self.do_test_no_exception(nargs="?", metavar="1")
5020
5021 def test_nargs_optional_metavar_length0(self):
5022 self.do_test_exception(nargs="?", metavar=tuple())
5023
5024 def test_nargs_optional_metavar_length1(self):
wim glenn66f02aa2018-06-08 05:12:49 -05005025 self.do_test_no_exception(nargs="?", metavar=("1",))
Steven Bethard8d9a4622011-03-26 17:33:56 +01005026
5027 def test_nargs_optional_metavar_length2(self):
5028 self.do_test_exception(nargs="?", metavar=("1", "2"))
5029
5030 def test_nargs_optional_metavar_length3(self):
5031 self.do_test_exception(nargs="?", metavar=("1", "2", "3"))
5032
5033 # Unit tests for different values of metavar when nargs=*
5034
5035 def test_nargs_zeroormore_metavar_string(self):
5036 self.do_test_no_exception(nargs="*", metavar="1")
5037
5038 def test_nargs_zeroormore_metavar_length0(self):
5039 self.do_test_exception(nargs="*", metavar=tuple())
5040
5041 def test_nargs_zeroormore_metavar_length1(self):
wim glenn66f02aa2018-06-08 05:12:49 -05005042 self.do_test_exception(nargs="*", metavar=("1",))
Steven Bethard8d9a4622011-03-26 17:33:56 +01005043
5044 def test_nargs_zeroormore_metavar_length2(self):
5045 self.do_test_no_exception(nargs="*", metavar=("1", "2"))
5046
5047 def test_nargs_zeroormore_metavar_length3(self):
5048 self.do_test_exception(nargs="*", metavar=("1", "2", "3"))
5049
5050 # Unit tests for different values of metavar when nargs=+
5051
5052 def test_nargs_oneormore_metavar_string(self):
5053 self.do_test_no_exception(nargs="+", metavar="1")
5054
5055 def test_nargs_oneormore_metavar_length0(self):
5056 self.do_test_exception(nargs="+", metavar=tuple())
5057
5058 def test_nargs_oneormore_metavar_length1(self):
wim glenn66f02aa2018-06-08 05:12:49 -05005059 self.do_test_exception(nargs="+", metavar=("1",))
Steven Bethard8d9a4622011-03-26 17:33:56 +01005060
5061 def test_nargs_oneormore_metavar_length2(self):
5062 self.do_test_no_exception(nargs="+", metavar=("1", "2"))
5063
5064 def test_nargs_oneormore_metavar_length3(self):
5065 self.do_test_exception(nargs="+", metavar=("1", "2", "3"))
5066
5067 # Unit tests for different values of metavar when nargs=...
5068
5069 def test_nargs_remainder_metavar_string(self):
5070 self.do_test_no_exception(nargs="...", metavar="1")
5071
5072 def test_nargs_remainder_metavar_length0(self):
5073 self.do_test_no_exception(nargs="...", metavar=tuple())
5074
5075 def test_nargs_remainder_metavar_length1(self):
wim glenn66f02aa2018-06-08 05:12:49 -05005076 self.do_test_no_exception(nargs="...", metavar=("1",))
Steven Bethard8d9a4622011-03-26 17:33:56 +01005077
5078 def test_nargs_remainder_metavar_length2(self):
5079 self.do_test_no_exception(nargs="...", metavar=("1", "2"))
5080
5081 def test_nargs_remainder_metavar_length3(self):
5082 self.do_test_no_exception(nargs="...", metavar=("1", "2", "3"))
5083
5084 # Unit tests for different values of metavar when nargs=A...
5085
5086 def test_nargs_parser_metavar_string(self):
5087 self.do_test_no_exception(nargs="A...", metavar="1")
5088
5089 def test_nargs_parser_metavar_length0(self):
5090 self.do_test_exception(nargs="A...", metavar=tuple())
5091
5092 def test_nargs_parser_metavar_length1(self):
wim glenn66f02aa2018-06-08 05:12:49 -05005093 self.do_test_no_exception(nargs="A...", metavar=("1",))
Steven Bethard8d9a4622011-03-26 17:33:56 +01005094
5095 def test_nargs_parser_metavar_length2(self):
5096 self.do_test_exception(nargs="A...", metavar=("1", "2"))
5097
5098 def test_nargs_parser_metavar_length3(self):
5099 self.do_test_exception(nargs="A...", metavar=("1", "2", "3"))
5100
5101 # Unit tests for different values of metavar when nargs=1
5102
5103 def test_nargs_1_metavar_string(self):
5104 self.do_test_no_exception(nargs=1, metavar="1")
5105
5106 def test_nargs_1_metavar_length0(self):
5107 self.do_test_exception(nargs=1, metavar=tuple())
5108
5109 def test_nargs_1_metavar_length1(self):
wim glenn66f02aa2018-06-08 05:12:49 -05005110 self.do_test_no_exception(nargs=1, metavar=("1",))
Steven Bethard8d9a4622011-03-26 17:33:56 +01005111
5112 def test_nargs_1_metavar_length2(self):
5113 self.do_test_exception(nargs=1, metavar=("1", "2"))
5114
5115 def test_nargs_1_metavar_length3(self):
5116 self.do_test_exception(nargs=1, metavar=("1", "2", "3"))
5117
5118 # Unit tests for different values of metavar when nargs=2
5119
5120 def test_nargs_2_metavar_string(self):
5121 self.do_test_no_exception(nargs=2, metavar="1")
5122
5123 def test_nargs_2_metavar_length0(self):
5124 self.do_test_exception(nargs=2, metavar=tuple())
5125
5126 def test_nargs_2_metavar_length1(self):
wim glenn66f02aa2018-06-08 05:12:49 -05005127 self.do_test_exception(nargs=2, metavar=("1",))
Steven Bethard8d9a4622011-03-26 17:33:56 +01005128
5129 def test_nargs_2_metavar_length2(self):
5130 self.do_test_no_exception(nargs=2, metavar=("1", "2"))
5131
5132 def test_nargs_2_metavar_length3(self):
5133 self.do_test_exception(nargs=2, metavar=("1", "2", "3"))
5134
5135 # Unit tests for different values of metavar when nargs=3
5136
5137 def test_nargs_3_metavar_string(self):
5138 self.do_test_no_exception(nargs=3, metavar="1")
5139
5140 def test_nargs_3_metavar_length0(self):
5141 self.do_test_exception(nargs=3, metavar=tuple())
5142
5143 def test_nargs_3_metavar_length1(self):
wim glenn66f02aa2018-06-08 05:12:49 -05005144 self.do_test_exception(nargs=3, metavar=("1",))
Steven Bethard8d9a4622011-03-26 17:33:56 +01005145
5146 def test_nargs_3_metavar_length2(self):
5147 self.do_test_exception(nargs=3, metavar=("1", "2"))
5148
5149 def test_nargs_3_metavar_length3(self):
5150 self.do_test_no_exception(nargs=3, metavar=("1", "2", "3"))
5151
tmblweed4b3e9752019-08-01 21:57:13 -07005152
5153class TestInvalidNargs(TestCase):
5154
5155 EXPECTED_INVALID_MESSAGE = "invalid nargs value"
5156 EXPECTED_RANGE_MESSAGE = ("nargs for store actions must be != 0; if you "
5157 "have nothing to store, actions such as store "
5158 "true or store const may be more appropriate")
5159
5160 def do_test_range_exception(self, nargs):
5161 parser = argparse.ArgumentParser()
5162 with self.assertRaises(ValueError) as cm:
5163 parser.add_argument("--foo", nargs=nargs)
5164 self.assertEqual(cm.exception.args[0], self.EXPECTED_RANGE_MESSAGE)
5165
5166 def do_test_invalid_exception(self, nargs):
5167 parser = argparse.ArgumentParser()
5168 with self.assertRaises(ValueError) as cm:
5169 parser.add_argument("--foo", nargs=nargs)
5170 self.assertEqual(cm.exception.args[0], self.EXPECTED_INVALID_MESSAGE)
5171
5172 # Unit tests for different values of nargs
5173
5174 def test_nargs_alphabetic(self):
5175 self.do_test_invalid_exception(nargs='a')
5176 self.do_test_invalid_exception(nargs="abcd")
5177
5178 def test_nargs_zero(self):
5179 self.do_test_range_exception(nargs=0)
5180
Benjamin Peterson698a18a2010-03-02 22:34:37 +00005181# ============================
5182# from argparse import * tests
5183# ============================
5184
5185class TestImportStar(TestCase):
5186
5187 def test(self):
5188 for name in argparse.__all__:
5189 self.assertTrue(hasattr(argparse, name))
5190
Steven Bethard72c55382010-11-01 15:23:12 +00005191 def test_all_exports_everything_but_modules(self):
5192 items = [
5193 name
5194 for name, value in vars(argparse).items()
Éric Araujo12159152010-12-04 17:31:49 +00005195 if not (name.startswith("_") or name == 'ngettext')
Steven Bethard72c55382010-11-01 15:23:12 +00005196 if not inspect.ismodule(value)
5197 ]
5198 self.assertEqual(sorted(items), sorted(argparse.__all__))
5199
wim glenn66f02aa2018-06-08 05:12:49 -05005200
5201class TestWrappingMetavar(TestCase):
5202
5203 def setUp(self):
Berker Peksag74102c92018-07-25 18:23:44 +03005204 super().setUp()
wim glenn66f02aa2018-06-08 05:12:49 -05005205 self.parser = ErrorRaisingArgumentParser(
5206 'this_is_spammy_prog_with_a_long_name_sorry_about_the_name'
5207 )
5208 # this metavar was triggering library assertion errors due to usage
5209 # message formatting incorrectly splitting on the ] chars within
5210 metavar = '<http[s]://example:1234>'
5211 self.parser.add_argument('--proxy', metavar=metavar)
5212
5213 def test_help_with_metavar(self):
5214 help_text = self.parser.format_help()
5215 self.assertEqual(help_text, textwrap.dedent('''\
5216 usage: this_is_spammy_prog_with_a_long_name_sorry_about_the_name
5217 [-h] [--proxy <http[s]://example:1234>]
5218
5219 optional arguments:
5220 -h, --help show this help message and exit
5221 --proxy <http[s]://example:1234>
5222 '''))
5223
5224
Benjamin Peterson698a18a2010-03-02 22:34:37 +00005225def test_main():
Florent Xiclunaaf1adbe2012-07-07 17:02:22 +02005226 support.run_unittest(__name__)
Benjamin Peterson4fd181c2010-03-02 23:46:42 +00005227 # Remove global references to avoid looking like we have refleaks.
5228 RFile.seen = {}
5229 WFile.seen = set()
5230
Benjamin Peterson698a18a2010-03-02 22:34:37 +00005231
5232
5233if __name__ == '__main__':
5234 test_main()