blob: 03c95fade4125e27d49f68e232f878295a292b2b [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
3import codecs
Steven Bethard72c55382010-11-01 15:23:12 +00004import inspect
Benjamin Peterson698a18a2010-03-02 22:34:37 +00005import os
6import shutil
Steven Bethardb0270112011-01-24 21:02:50 +00007import stat
Benjamin Peterson698a18a2010-03-02 22:34:37 +00008import sys
9import textwrap
10import tempfile
11import unittest
Benjamin Peterson698a18a2010-03-02 22:34:37 +000012import argparse
13
Benjamin Peterson16f2fd02010-03-02 23:09:38 +000014from io import StringIO
15
Benjamin Peterson698a18a2010-03-02 22:34:37 +000016from test import support
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
22 def assertEqual(self, obj1, obj2):
23 if obj1 != obj2:
24 print('')
25 print(repr(obj1))
26 print(repr(obj2))
27 print(obj1)
28 print(obj2)
29 super(TestCase, self).assertEqual(obj1, obj2)
30
Steven Bethard1f1c2472010-11-01 13:56:09 +000031 def setUp(self):
32 # The tests assume that line wrapping occurs at 80 columns, but this
33 # behaviour can be overridden by setting the COLUMNS environment
34 # variable. To ensure that this assumption is true, unset COLUMNS.
35 env = support.EnvironmentVarGuard()
36 env.unset("COLUMNS")
37 self.addCleanup(env.__exit__)
Benjamin Peterson698a18a2010-03-02 22:34:37 +000038
Benjamin Petersonb48af542010-04-11 20:43:16 +000039
Benjamin Peterson698a18a2010-03-02 22:34:37 +000040class TempDirMixin(object):
41
42 def setUp(self):
43 self.temp_dir = tempfile.mkdtemp()
44 self.old_dir = os.getcwd()
45 os.chdir(self.temp_dir)
46
47 def tearDown(self):
48 os.chdir(self.old_dir)
Steven Bethardb0270112011-01-24 21:02:50 +000049 shutil.rmtree(self.temp_dir, True)
Benjamin Peterson698a18a2010-03-02 22:34:37 +000050
Steven Bethardb0270112011-01-24 21:02:50 +000051 def create_readonly_file(self, filename):
52 file_path = os.path.join(self.temp_dir, filename)
53 with open(file_path, 'w') as file:
54 file.write(filename)
55 os.chmod(file_path, stat.S_IREAD)
Benjamin Peterson698a18a2010-03-02 22:34:37 +000056
57class Sig(object):
58
59 def __init__(self, *args, **kwargs):
60 self.args = args
61 self.kwargs = kwargs
62
63
64class NS(object):
65
66 def __init__(self, **kwargs):
67 self.__dict__.update(kwargs)
68
69 def __repr__(self):
70 sorted_items = sorted(self.__dict__.items())
71 kwarg_str = ', '.join(['%s=%r' % tup for tup in sorted_items])
72 return '%s(%s)' % (type(self).__name__, kwarg_str)
73
74 def __eq__(self, other):
75 return vars(self) == vars(other)
76
77 def __ne__(self, other):
78 return not (self == other)
79
80
81class ArgumentParserError(Exception):
82
83 def __init__(self, message, stdout=None, stderr=None, error_code=None):
84 Exception.__init__(self, message, stdout, stderr)
85 self.message = message
86 self.stdout = stdout
87 self.stderr = stderr
88 self.error_code = error_code
89
90
91def stderr_to_parser_error(parse_args, *args, **kwargs):
92 # if this is being called recursively and stderr or stdout is already being
93 # redirected, simply call the function and let the enclosing function
94 # catch the exception
Benjamin Petersonb48af542010-04-11 20:43:16 +000095 if isinstance(sys.stderr, StdIOBuffer) or isinstance(sys.stdout, StdIOBuffer):
Benjamin Peterson698a18a2010-03-02 22:34:37 +000096 return parse_args(*args, **kwargs)
97
98 # if this is not being called recursively, redirect stderr and
99 # use it as the ArgumentParserError message
100 old_stdout = sys.stdout
101 old_stderr = sys.stderr
Benjamin Petersonb48af542010-04-11 20:43:16 +0000102 sys.stdout = StdIOBuffer()
103 sys.stderr = StdIOBuffer()
Benjamin Peterson698a18a2010-03-02 22:34:37 +0000104 try:
105 try:
106 result = parse_args(*args, **kwargs)
107 for key in list(vars(result)):
108 if getattr(result, key) is sys.stdout:
109 setattr(result, key, old_stdout)
110 if getattr(result, key) is sys.stderr:
111 setattr(result, key, old_stderr)
112 return result
113 except SystemExit:
114 code = sys.exc_info()[1].code
115 stdout = sys.stdout.getvalue()
116 stderr = sys.stderr.getvalue()
117 raise ArgumentParserError("SystemExit", stdout, stderr, code)
118 finally:
119 sys.stdout = old_stdout
120 sys.stderr = old_stderr
121
122
123class ErrorRaisingArgumentParser(argparse.ArgumentParser):
124
125 def parse_args(self, *args, **kwargs):
126 parse_args = super(ErrorRaisingArgumentParser, self).parse_args
127 return stderr_to_parser_error(parse_args, *args, **kwargs)
128
129 def exit(self, *args, **kwargs):
130 exit = super(ErrorRaisingArgumentParser, self).exit
131 return stderr_to_parser_error(exit, *args, **kwargs)
132
133 def error(self, *args, **kwargs):
134 error = super(ErrorRaisingArgumentParser, self).error
135 return stderr_to_parser_error(error, *args, **kwargs)
136
137
138class ParserTesterMetaclass(type):
139 """Adds parser tests using the class attributes.
140
141 Classes of this type should specify the following attributes:
142
143 argument_signatures -- a list of Sig objects which specify
144 the signatures of Argument objects to be created
145 failures -- a list of args lists that should cause the parser
146 to fail
147 successes -- a list of (initial_args, options, remaining_args) tuples
148 where initial_args specifies the string args to be parsed,
149 options is a dict that should match the vars() of the options
150 parsed out of initial_args, and remaining_args should be any
151 remaining unparsed arguments
152 """
153
154 def __init__(cls, name, bases, bodydict):
155 if name == 'ParserTestCase':
156 return
157
158 # default parser signature is empty
159 if not hasattr(cls, 'parser_signature'):
160 cls.parser_signature = Sig()
161 if not hasattr(cls, 'parser_class'):
162 cls.parser_class = ErrorRaisingArgumentParser
163
164 # ---------------------------------------
165 # functions for adding optional arguments
166 # ---------------------------------------
167 def no_groups(parser, argument_signatures):
168 """Add all arguments directly to the parser"""
169 for sig in argument_signatures:
170 parser.add_argument(*sig.args, **sig.kwargs)
171
172 def one_group(parser, argument_signatures):
173 """Add all arguments under a single group in the parser"""
174 group = parser.add_argument_group('foo')
175 for sig in argument_signatures:
176 group.add_argument(*sig.args, **sig.kwargs)
177
178 def many_groups(parser, argument_signatures):
179 """Add each argument in its own group to the parser"""
180 for i, sig in enumerate(argument_signatures):
181 group = parser.add_argument_group('foo:%i' % i)
182 group.add_argument(*sig.args, **sig.kwargs)
183
184 # --------------------------
185 # functions for parsing args
186 # --------------------------
187 def listargs(parser, args):
188 """Parse the args by passing in a list"""
189 return parser.parse_args(args)
190
191 def sysargs(parser, args):
192 """Parse the args by defaulting to sys.argv"""
193 old_sys_argv = sys.argv
194 sys.argv = [old_sys_argv[0]] + args
195 try:
196 return parser.parse_args()
197 finally:
198 sys.argv = old_sys_argv
199
200 # class that holds the combination of one optional argument
201 # addition method and one arg parsing method
202 class AddTests(object):
203
204 def __init__(self, tester_cls, add_arguments, parse_args):
205 self._add_arguments = add_arguments
206 self._parse_args = parse_args
207
208 add_arguments_name = self._add_arguments.__name__
209 parse_args_name = self._parse_args.__name__
210 for test_func in [self.test_failures, self.test_successes]:
211 func_name = test_func.__name__
212 names = func_name, add_arguments_name, parse_args_name
213 test_name = '_'.join(names)
214
215 def wrapper(self, test_func=test_func):
216 test_func(self)
217 try:
218 wrapper.__name__ = test_name
219 except TypeError:
220 pass
221 setattr(tester_cls, test_name, wrapper)
222
223 def _get_parser(self, tester):
224 args = tester.parser_signature.args
225 kwargs = tester.parser_signature.kwargs
226 parser = tester.parser_class(*args, **kwargs)
227 self._add_arguments(parser, tester.argument_signatures)
228 return parser
229
230 def test_failures(self, tester):
231 parser = self._get_parser(tester)
232 for args_str in tester.failures:
233 args = args_str.split()
234 raises = tester.assertRaises
235 raises(ArgumentParserError, parser.parse_args, args)
236
237 def test_successes(self, tester):
238 parser = self._get_parser(tester)
239 for args, expected_ns in tester.successes:
240 if isinstance(args, str):
241 args = args.split()
242 result_ns = self._parse_args(parser, args)
243 tester.assertEqual(expected_ns, result_ns)
244
245 # add tests for each combination of an optionals adding method
246 # and an arg parsing method
247 for add_arguments in [no_groups, one_group, many_groups]:
248 for parse_args in [listargs, sysargs]:
249 AddTests(cls, add_arguments, parse_args)
250
251bases = TestCase,
252ParserTestCase = ParserTesterMetaclass('ParserTestCase', bases, {})
253
254# ===============
255# Optionals tests
256# ===============
257
258class TestOptionalsSingleDash(ParserTestCase):
259 """Test an Optional with a single-dash option string"""
260
261 argument_signatures = [Sig('-x')]
262 failures = ['-x', 'a', '--foo', '-x --foo', '-x -y']
263 successes = [
264 ('', NS(x=None)),
265 ('-x a', NS(x='a')),
266 ('-xa', NS(x='a')),
267 ('-x -1', NS(x='-1')),
268 ('-x-1', NS(x='-1')),
269 ]
270
271
272class TestOptionalsSingleDashCombined(ParserTestCase):
273 """Test an Optional with a single-dash option string"""
274
275 argument_signatures = [
276 Sig('-x', action='store_true'),
277 Sig('-yyy', action='store_const', const=42),
278 Sig('-z'),
279 ]
280 failures = ['a', '--foo', '-xa', '-x --foo', '-x -z', '-z -x',
281 '-yx', '-yz a', '-yyyx', '-yyyza', '-xyza']
282 successes = [
283 ('', NS(x=False, yyy=None, z=None)),
284 ('-x', NS(x=True, yyy=None, z=None)),
285 ('-za', NS(x=False, yyy=None, z='a')),
286 ('-z a', NS(x=False, yyy=None, z='a')),
287 ('-xza', NS(x=True, yyy=None, z='a')),
288 ('-xz a', NS(x=True, yyy=None, z='a')),
289 ('-x -za', NS(x=True, yyy=None, z='a')),
290 ('-x -z a', NS(x=True, yyy=None, z='a')),
291 ('-y', NS(x=False, yyy=42, z=None)),
292 ('-yyy', NS(x=False, yyy=42, z=None)),
293 ('-x -yyy -za', NS(x=True, yyy=42, z='a')),
294 ('-x -yyy -z a', NS(x=True, yyy=42, z='a')),
295 ]
296
297
298class TestOptionalsSingleDashLong(ParserTestCase):
299 """Test an Optional with a multi-character single-dash option string"""
300
301 argument_signatures = [Sig('-foo')]
302 failures = ['-foo', 'a', '--foo', '-foo --foo', '-foo -y', '-fooa']
303 successes = [
304 ('', NS(foo=None)),
305 ('-foo a', NS(foo='a')),
306 ('-foo -1', NS(foo='-1')),
307 ('-fo a', NS(foo='a')),
308 ('-f a', NS(foo='a')),
309 ]
310
311
312class TestOptionalsSingleDashSubsetAmbiguous(ParserTestCase):
313 """Test Optionals where option strings are subsets of each other"""
314
315 argument_signatures = [Sig('-f'), Sig('-foobar'), Sig('-foorab')]
316 failures = ['-f', '-foo', '-fo', '-foo b', '-foob', '-fooba', '-foora']
317 successes = [
318 ('', NS(f=None, foobar=None, foorab=None)),
319 ('-f a', NS(f='a', foobar=None, foorab=None)),
320 ('-fa', NS(f='a', foobar=None, foorab=None)),
321 ('-foa', NS(f='oa', foobar=None, foorab=None)),
322 ('-fooa', NS(f='ooa', foobar=None, foorab=None)),
323 ('-foobar a', NS(f=None, foobar='a', foorab=None)),
324 ('-foorab a', NS(f=None, foobar=None, foorab='a')),
325 ]
326
327
328class TestOptionalsSingleDashAmbiguous(ParserTestCase):
329 """Test Optionals that partially match but are not subsets"""
330
331 argument_signatures = [Sig('-foobar'), Sig('-foorab')]
332 failures = ['-f', '-f a', '-fa', '-foa', '-foo', '-fo', '-foo b']
333 successes = [
334 ('', NS(foobar=None, foorab=None)),
335 ('-foob a', NS(foobar='a', foorab=None)),
336 ('-foor a', NS(foobar=None, foorab='a')),
337 ('-fooba a', NS(foobar='a', foorab=None)),
338 ('-foora a', NS(foobar=None, foorab='a')),
339 ('-foobar a', NS(foobar='a', foorab=None)),
340 ('-foorab a', NS(foobar=None, foorab='a')),
341 ]
342
343
344class TestOptionalsNumeric(ParserTestCase):
345 """Test an Optional with a short opt string"""
346
347 argument_signatures = [Sig('-1', dest='one')]
348 failures = ['-1', 'a', '-1 --foo', '-1 -y', '-1 -1', '-1 -2']
349 successes = [
350 ('', NS(one=None)),
351 ('-1 a', NS(one='a')),
352 ('-1a', NS(one='a')),
353 ('-1-2', NS(one='-2')),
354 ]
355
356
357class TestOptionalsDoubleDash(ParserTestCase):
358 """Test an Optional with a double-dash option string"""
359
360 argument_signatures = [Sig('--foo')]
361 failures = ['--foo', '-f', '-f a', 'a', '--foo -x', '--foo --bar']
362 successes = [
363 ('', NS(foo=None)),
364 ('--foo a', NS(foo='a')),
365 ('--foo=a', NS(foo='a')),
366 ('--foo -2.5', NS(foo='-2.5')),
367 ('--foo=-2.5', NS(foo='-2.5')),
368 ]
369
370
371class TestOptionalsDoubleDashPartialMatch(ParserTestCase):
372 """Tests partial matching with a double-dash option string"""
373
374 argument_signatures = [
375 Sig('--badger', action='store_true'),
376 Sig('--bat'),
377 ]
378 failures = ['--bar', '--b', '--ba', '--b=2', '--ba=4', '--badge 5']
379 successes = [
380 ('', NS(badger=False, bat=None)),
381 ('--bat X', NS(badger=False, bat='X')),
382 ('--bad', NS(badger=True, bat=None)),
383 ('--badg', NS(badger=True, bat=None)),
384 ('--badge', NS(badger=True, bat=None)),
385 ('--badger', NS(badger=True, bat=None)),
386 ]
387
388
389class TestOptionalsDoubleDashPrefixMatch(ParserTestCase):
390 """Tests when one double-dash option string is a prefix of another"""
391
392 argument_signatures = [
393 Sig('--badger', action='store_true'),
394 Sig('--ba'),
395 ]
396 failures = ['--bar', '--b', '--ba', '--b=2', '--badge 5']
397 successes = [
398 ('', NS(badger=False, ba=None)),
399 ('--ba X', NS(badger=False, ba='X')),
400 ('--ba=X', NS(badger=False, ba='X')),
401 ('--bad', NS(badger=True, ba=None)),
402 ('--badg', NS(badger=True, ba=None)),
403 ('--badge', NS(badger=True, ba=None)),
404 ('--badger', NS(badger=True, ba=None)),
405 ]
406
407
408class TestOptionalsSingleDoubleDash(ParserTestCase):
409 """Test an Optional with single- and double-dash option strings"""
410
411 argument_signatures = [
412 Sig('-f', action='store_true'),
413 Sig('--bar'),
414 Sig('-baz', action='store_const', const=42),
415 ]
416 failures = ['--bar', '-fbar', '-fbaz', '-bazf', '-b B', 'B']
417 successes = [
418 ('', NS(f=False, bar=None, baz=None)),
419 ('-f', NS(f=True, bar=None, baz=None)),
420 ('--ba B', NS(f=False, bar='B', baz=None)),
421 ('-f --bar B', NS(f=True, bar='B', baz=None)),
422 ('-f -b', NS(f=True, bar=None, baz=42)),
423 ('-ba -f', NS(f=True, bar=None, baz=42)),
424 ]
425
426
427class TestOptionalsAlternatePrefixChars(ParserTestCase):
R. David Murray88c49fe2010-08-03 17:56:09 +0000428 """Test an Optional with option strings with custom prefixes"""
Benjamin Peterson698a18a2010-03-02 22:34:37 +0000429
430 parser_signature = Sig(prefix_chars='+:/', add_help=False)
431 argument_signatures = [
432 Sig('+f', action='store_true'),
433 Sig('::bar'),
434 Sig('/baz', action='store_const', const=42),
435 ]
R. David Murray88c49fe2010-08-03 17:56:09 +0000436 failures = ['--bar', '-fbar', '-b B', 'B', '-f', '--bar B', '-baz', '-h', '--help', '+h', '::help', '/help']
437 successes = [
438 ('', NS(f=False, bar=None, baz=None)),
439 ('+f', NS(f=True, bar=None, baz=None)),
440 ('::ba B', NS(f=False, bar='B', baz=None)),
441 ('+f ::bar B', NS(f=True, bar='B', baz=None)),
442 ('+f /b', NS(f=True, bar=None, baz=42)),
443 ('/ba +f', NS(f=True, bar=None, baz=42)),
444 ]
445
446
447class TestOptionalsAlternatePrefixCharsAddedHelp(ParserTestCase):
448 """When ``-`` not in prefix_chars, default operators created for help
449 should use the prefix_chars in use rather than - or --
450 http://bugs.python.org/issue9444"""
451
452 parser_signature = Sig(prefix_chars='+:/', add_help=True)
453 argument_signatures = [
454 Sig('+f', action='store_true'),
455 Sig('::bar'),
456 Sig('/baz', action='store_const', const=42),
457 ]
Benjamin Peterson698a18a2010-03-02 22:34:37 +0000458 failures = ['--bar', '-fbar', '-b B', 'B', '-f', '--bar B', '-baz']
459 successes = [
460 ('', NS(f=False, bar=None, baz=None)),
461 ('+f', NS(f=True, bar=None, baz=None)),
462 ('::ba B', NS(f=False, bar='B', baz=None)),
463 ('+f ::bar B', NS(f=True, bar='B', baz=None)),
464 ('+f /b', NS(f=True, bar=None, baz=42)),
R. David Murray88c49fe2010-08-03 17:56:09 +0000465 ('/ba +f', NS(f=True, bar=None, baz=42))
Benjamin Peterson698a18a2010-03-02 22:34:37 +0000466 ]
467
Steven Bethard1ca45a52010-11-01 15:57:36 +0000468
469class TestOptionalsAlternatePrefixCharsMultipleShortArgs(ParserTestCase):
470 """Verify that Optionals must be called with their defined prefixes"""
471
472 parser_signature = Sig(prefix_chars='+-', add_help=False)
473 argument_signatures = [
474 Sig('-x', action='store_true'),
475 Sig('+y', action='store_true'),
476 Sig('+z', action='store_true'),
477 ]
478 failures = ['-w',
479 '-xyz',
480 '+x',
481 '-y',
482 '+xyz',
483 ]
484 successes = [
485 ('', NS(x=False, y=False, z=False)),
486 ('-x', NS(x=True, y=False, z=False)),
487 ('+y -x', NS(x=True, y=True, z=False)),
488 ('+yz -x', NS(x=True, y=True, z=True)),
489 ]
490
491
Benjamin Peterson698a18a2010-03-02 22:34:37 +0000492class TestOptionalsShortLong(ParserTestCase):
493 """Test a combination of single- and double-dash option strings"""
494
495 argument_signatures = [
496 Sig('-v', '--verbose', '-n', '--noisy', action='store_true'),
497 ]
498 failures = ['--x --verbose', '-N', 'a', '-v x']
499 successes = [
500 ('', NS(verbose=False)),
501 ('-v', NS(verbose=True)),
502 ('--verbose', NS(verbose=True)),
503 ('-n', NS(verbose=True)),
504 ('--noisy', NS(verbose=True)),
505 ]
506
507
508class TestOptionalsDest(ParserTestCase):
509 """Tests various means of setting destination"""
510
511 argument_signatures = [Sig('--foo-bar'), Sig('--baz', dest='zabbaz')]
512 failures = ['a']
513 successes = [
514 ('--foo-bar f', NS(foo_bar='f', zabbaz=None)),
515 ('--baz g', NS(foo_bar=None, zabbaz='g')),
516 ('--foo-bar h --baz i', NS(foo_bar='h', zabbaz='i')),
517 ('--baz j --foo-bar k', NS(foo_bar='k', zabbaz='j')),
518 ]
519
520
521class TestOptionalsDefault(ParserTestCase):
522 """Tests specifying a default for an Optional"""
523
524 argument_signatures = [Sig('-x'), Sig('-y', default=42)]
525 failures = ['a']
526 successes = [
527 ('', NS(x=None, y=42)),
528 ('-xx', NS(x='x', y=42)),
529 ('-yy', NS(x=None, y='y')),
530 ]
531
532
533class TestOptionalsNargsDefault(ParserTestCase):
534 """Tests not specifying the number of args for an Optional"""
535
536 argument_signatures = [Sig('-x')]
537 failures = ['a', '-x']
538 successes = [
539 ('', NS(x=None)),
540 ('-x a', NS(x='a')),
541 ]
542
543
544class TestOptionalsNargs1(ParserTestCase):
545 """Tests specifying the 1 arg for an Optional"""
546
547 argument_signatures = [Sig('-x', nargs=1)]
548 failures = ['a', '-x']
549 successes = [
550 ('', NS(x=None)),
551 ('-x a', NS(x=['a'])),
552 ]
553
554
555class TestOptionalsNargs3(ParserTestCase):
556 """Tests specifying the 3 args for an Optional"""
557
558 argument_signatures = [Sig('-x', nargs=3)]
559 failures = ['a', '-x', '-x a', '-x a b', 'a -x', 'a -x b']
560 successes = [
561 ('', NS(x=None)),
562 ('-x a b c', NS(x=['a', 'b', 'c'])),
563 ]
564
565
566class TestOptionalsNargsOptional(ParserTestCase):
567 """Tests specifying an Optional arg for an Optional"""
568
569 argument_signatures = [
570 Sig('-w', nargs='?'),
571 Sig('-x', nargs='?', const=42),
572 Sig('-y', nargs='?', default='spam'),
573 Sig('-z', nargs='?', type=int, const='42', default='84'),
574 ]
575 failures = ['2']
576 successes = [
577 ('', NS(w=None, x=None, y='spam', z=84)),
578 ('-w', NS(w=None, x=None, y='spam', z=84)),
579 ('-w 2', NS(w='2', x=None, y='spam', z=84)),
580 ('-x', NS(w=None, x=42, y='spam', z=84)),
581 ('-x 2', NS(w=None, x='2', y='spam', z=84)),
582 ('-y', NS(w=None, x=None, y=None, z=84)),
583 ('-y 2', NS(w=None, x=None, y='2', z=84)),
584 ('-z', NS(w=None, x=None, y='spam', z=42)),
585 ('-z 2', NS(w=None, x=None, y='spam', z=2)),
586 ]
587
588
589class TestOptionalsNargsZeroOrMore(ParserTestCase):
590 """Tests specifying an args for an Optional that accepts zero or more"""
591
592 argument_signatures = [
593 Sig('-x', nargs='*'),
594 Sig('-y', nargs='*', default='spam'),
595 ]
596 failures = ['a']
597 successes = [
598 ('', NS(x=None, y='spam')),
599 ('-x', NS(x=[], y='spam')),
600 ('-x a', NS(x=['a'], y='spam')),
601 ('-x a b', NS(x=['a', 'b'], y='spam')),
602 ('-y', NS(x=None, y=[])),
603 ('-y a', NS(x=None, y=['a'])),
604 ('-y a b', NS(x=None, y=['a', 'b'])),
605 ]
606
607
608class TestOptionalsNargsOneOrMore(ParserTestCase):
609 """Tests specifying an args for an Optional that accepts one or more"""
610
611 argument_signatures = [
612 Sig('-x', nargs='+'),
613 Sig('-y', nargs='+', default='spam'),
614 ]
615 failures = ['a', '-x', '-y', 'a -x', 'a -y b']
616 successes = [
617 ('', NS(x=None, y='spam')),
618 ('-x a', NS(x=['a'], y='spam')),
619 ('-x a b', NS(x=['a', 'b'], y='spam')),
620 ('-y a', NS(x=None, y=['a'])),
621 ('-y a b', NS(x=None, y=['a', 'b'])),
622 ]
623
624
625class TestOptionalsChoices(ParserTestCase):
626 """Tests specifying the choices for an Optional"""
627
628 argument_signatures = [
629 Sig('-f', choices='abc'),
630 Sig('-g', type=int, choices=range(5))]
631 failures = ['a', '-f d', '-fad', '-ga', '-g 6']
632 successes = [
633 ('', NS(f=None, g=None)),
634 ('-f a', NS(f='a', g=None)),
635 ('-f c', NS(f='c', g=None)),
636 ('-g 0', NS(f=None, g=0)),
637 ('-g 03', NS(f=None, g=3)),
638 ('-fb -g4', NS(f='b', g=4)),
639 ]
640
641
642class TestOptionalsRequired(ParserTestCase):
643 """Tests the an optional action that is required"""
644
645 argument_signatures = [
646 Sig('-x', type=int, required=True),
647 ]
648 failures = ['a', '']
649 successes = [
650 ('-x 1', NS(x=1)),
651 ('-x42', NS(x=42)),
652 ]
653
654
655class TestOptionalsActionStore(ParserTestCase):
656 """Tests the store action for an Optional"""
657
658 argument_signatures = [Sig('-x', action='store')]
659 failures = ['a', 'a -x']
660 successes = [
661 ('', NS(x=None)),
662 ('-xfoo', NS(x='foo')),
663 ]
664
665
666class TestOptionalsActionStoreConst(ParserTestCase):
667 """Tests the store_const action for an Optional"""
668
669 argument_signatures = [Sig('-y', action='store_const', const=object)]
670 failures = ['a']
671 successes = [
672 ('', NS(y=None)),
673 ('-y', NS(y=object)),
674 ]
675
676
677class TestOptionalsActionStoreFalse(ParserTestCase):
678 """Tests the store_false action for an Optional"""
679
680 argument_signatures = [Sig('-z', action='store_false')]
681 failures = ['a', '-za', '-z a']
682 successes = [
683 ('', NS(z=True)),
684 ('-z', NS(z=False)),
685 ]
686
687
688class TestOptionalsActionStoreTrue(ParserTestCase):
689 """Tests the store_true action for an Optional"""
690
691 argument_signatures = [Sig('--apple', action='store_true')]
692 failures = ['a', '--apple=b', '--apple b']
693 successes = [
694 ('', NS(apple=False)),
695 ('--apple', NS(apple=True)),
696 ]
697
698
699class TestOptionalsActionAppend(ParserTestCase):
700 """Tests the append action for an Optional"""
701
702 argument_signatures = [Sig('--baz', action='append')]
703 failures = ['a', '--baz', 'a --baz', '--baz a b']
704 successes = [
705 ('', NS(baz=None)),
706 ('--baz a', NS(baz=['a'])),
707 ('--baz a --baz b', NS(baz=['a', 'b'])),
708 ]
709
710
711class TestOptionalsActionAppendWithDefault(ParserTestCase):
712 """Tests the append action for an Optional"""
713
714 argument_signatures = [Sig('--baz', action='append', default=['X'])]
715 failures = ['a', '--baz', 'a --baz', '--baz a b']
716 successes = [
717 ('', NS(baz=['X'])),
718 ('--baz a', NS(baz=['X', 'a'])),
719 ('--baz a --baz b', NS(baz=['X', 'a', 'b'])),
720 ]
721
722
723class TestOptionalsActionAppendConst(ParserTestCase):
724 """Tests the append_const action for an Optional"""
725
726 argument_signatures = [
727 Sig('-b', action='append_const', const=Exception),
728 Sig('-c', action='append', dest='b'),
729 ]
730 failures = ['a', '-c', 'a -c', '-bx', '-b x']
731 successes = [
732 ('', NS(b=None)),
733 ('-b', NS(b=[Exception])),
734 ('-b -cx -b -cyz', NS(b=[Exception, 'x', Exception, 'yz'])),
735 ]
736
737
738class TestOptionalsActionAppendConstWithDefault(ParserTestCase):
739 """Tests the append_const action for an Optional"""
740
741 argument_signatures = [
742 Sig('-b', action='append_const', const=Exception, default=['X']),
743 Sig('-c', action='append', dest='b'),
744 ]
745 failures = ['a', '-c', 'a -c', '-bx', '-b x']
746 successes = [
747 ('', NS(b=['X'])),
748 ('-b', NS(b=['X', Exception])),
749 ('-b -cx -b -cyz', NS(b=['X', Exception, 'x', Exception, 'yz'])),
750 ]
751
752
753class TestOptionalsActionCount(ParserTestCase):
754 """Tests the count action for an Optional"""
755
756 argument_signatures = [Sig('-x', action='count')]
757 failures = ['a', '-x a', '-x b', '-x a -x b']
758 successes = [
759 ('', NS(x=None)),
760 ('-x', NS(x=1)),
761 ]
762
763
764# ================
765# Positional tests
766# ================
767
768class TestPositionalsNargsNone(ParserTestCase):
769 """Test a Positional that doesn't specify nargs"""
770
771 argument_signatures = [Sig('foo')]
772 failures = ['', '-x', 'a b']
773 successes = [
774 ('a', NS(foo='a')),
775 ]
776
777
778class TestPositionalsNargs1(ParserTestCase):
779 """Test a Positional that specifies an nargs of 1"""
780
781 argument_signatures = [Sig('foo', nargs=1)]
782 failures = ['', '-x', 'a b']
783 successes = [
784 ('a', NS(foo=['a'])),
785 ]
786
787
788class TestPositionalsNargs2(ParserTestCase):
789 """Test a Positional that specifies an nargs of 2"""
790
791 argument_signatures = [Sig('foo', nargs=2)]
792 failures = ['', 'a', '-x', 'a b c']
793 successes = [
794 ('a b', NS(foo=['a', 'b'])),
795 ]
796
797
798class TestPositionalsNargsZeroOrMore(ParserTestCase):
799 """Test a Positional that specifies unlimited nargs"""
800
801 argument_signatures = [Sig('foo', nargs='*')]
802 failures = ['-x']
803 successes = [
804 ('', NS(foo=[])),
805 ('a', NS(foo=['a'])),
806 ('a b', NS(foo=['a', 'b'])),
807 ]
808
809
810class TestPositionalsNargsZeroOrMoreDefault(ParserTestCase):
811 """Test a Positional that specifies unlimited nargs and a default"""
812
813 argument_signatures = [Sig('foo', nargs='*', default='bar')]
814 failures = ['-x']
815 successes = [
816 ('', NS(foo='bar')),
817 ('a', NS(foo=['a'])),
818 ('a b', NS(foo=['a', 'b'])),
819 ]
820
821
822class TestPositionalsNargsOneOrMore(ParserTestCase):
823 """Test a Positional that specifies one or more nargs"""
824
825 argument_signatures = [Sig('foo', nargs='+')]
826 failures = ['', '-x']
827 successes = [
828 ('a', NS(foo=['a'])),
829 ('a b', NS(foo=['a', 'b'])),
830 ]
831
832
833class TestPositionalsNargsOptional(ParserTestCase):
834 """Tests an Optional Positional"""
835
836 argument_signatures = [Sig('foo', nargs='?')]
837 failures = ['-x', 'a b']
838 successes = [
839 ('', NS(foo=None)),
840 ('a', NS(foo='a')),
841 ]
842
843
844class TestPositionalsNargsOptionalDefault(ParserTestCase):
845 """Tests an Optional Positional with a default value"""
846
847 argument_signatures = [Sig('foo', nargs='?', default=42)]
848 failures = ['-x', 'a b']
849 successes = [
850 ('', NS(foo=42)),
851 ('a', NS(foo='a')),
852 ]
853
854
855class TestPositionalsNargsOptionalConvertedDefault(ParserTestCase):
856 """Tests an Optional Positional with a default value
857 that needs to be converted to the appropriate type.
858 """
859
860 argument_signatures = [
861 Sig('foo', nargs='?', type=int, default='42'),
862 ]
863 failures = ['-x', 'a b', '1 2']
864 successes = [
865 ('', NS(foo=42)),
866 ('1', NS(foo=1)),
867 ]
868
869
870class TestPositionalsNargsNoneNone(ParserTestCase):
871 """Test two Positionals that don't specify nargs"""
872
873 argument_signatures = [Sig('foo'), Sig('bar')]
874 failures = ['', '-x', 'a', 'a b c']
875 successes = [
876 ('a b', NS(foo='a', bar='b')),
877 ]
878
879
880class TestPositionalsNargsNone1(ParserTestCase):
881 """Test a Positional with no nargs followed by one with 1"""
882
883 argument_signatures = [Sig('foo'), Sig('bar', nargs=1)]
884 failures = ['', '--foo', 'a', 'a b c']
885 successes = [
886 ('a b', NS(foo='a', bar=['b'])),
887 ]
888
889
890class TestPositionalsNargs2None(ParserTestCase):
891 """Test a Positional with 2 nargs followed by one with none"""
892
893 argument_signatures = [Sig('foo', nargs=2), Sig('bar')]
894 failures = ['', '--foo', 'a', 'a b', 'a b c d']
895 successes = [
896 ('a b c', NS(foo=['a', 'b'], bar='c')),
897 ]
898
899
900class TestPositionalsNargsNoneZeroOrMore(ParserTestCase):
901 """Test a Positional with no nargs followed by one with unlimited"""
902
903 argument_signatures = [Sig('foo'), Sig('bar', nargs='*')]
904 failures = ['', '--foo']
905 successes = [
906 ('a', NS(foo='a', bar=[])),
907 ('a b', NS(foo='a', bar=['b'])),
908 ('a b c', NS(foo='a', bar=['b', 'c'])),
909 ]
910
911
912class TestPositionalsNargsNoneOneOrMore(ParserTestCase):
913 """Test a Positional with no nargs followed by one with one or more"""
914
915 argument_signatures = [Sig('foo'), Sig('bar', nargs='+')]
916 failures = ['', '--foo', 'a']
917 successes = [
918 ('a b', NS(foo='a', bar=['b'])),
919 ('a b c', NS(foo='a', bar=['b', 'c'])),
920 ]
921
922
923class TestPositionalsNargsNoneOptional(ParserTestCase):
924 """Test a Positional with no nargs followed by one with an Optional"""
925
926 argument_signatures = [Sig('foo'), Sig('bar', nargs='?')]
927 failures = ['', '--foo', 'a b c']
928 successes = [
929 ('a', NS(foo='a', bar=None)),
930 ('a b', NS(foo='a', bar='b')),
931 ]
932
933
934class TestPositionalsNargsZeroOrMoreNone(ParserTestCase):
935 """Test a Positional with unlimited nargs followed by one with none"""
936
937 argument_signatures = [Sig('foo', nargs='*'), Sig('bar')]
938 failures = ['', '--foo']
939 successes = [
940 ('a', NS(foo=[], bar='a')),
941 ('a b', NS(foo=['a'], bar='b')),
942 ('a b c', NS(foo=['a', 'b'], bar='c')),
943 ]
944
945
946class TestPositionalsNargsOneOrMoreNone(ParserTestCase):
947 """Test a Positional with one or more nargs followed by one with none"""
948
949 argument_signatures = [Sig('foo', nargs='+'), Sig('bar')]
950 failures = ['', '--foo', 'a']
951 successes = [
952 ('a b', NS(foo=['a'], bar='b')),
953 ('a b c', NS(foo=['a', 'b'], bar='c')),
954 ]
955
956
957class TestPositionalsNargsOptionalNone(ParserTestCase):
958 """Test a Positional with an Optional nargs followed by one with none"""
959
960 argument_signatures = [Sig('foo', nargs='?', default=42), Sig('bar')]
961 failures = ['', '--foo', 'a b c']
962 successes = [
963 ('a', NS(foo=42, bar='a')),
964 ('a b', NS(foo='a', bar='b')),
965 ]
966
967
968class TestPositionalsNargs2ZeroOrMore(ParserTestCase):
969 """Test a Positional with 2 nargs followed by one with unlimited"""
970
971 argument_signatures = [Sig('foo', nargs=2), Sig('bar', nargs='*')]
972 failures = ['', '--foo', 'a']
973 successes = [
974 ('a b', NS(foo=['a', 'b'], bar=[])),
975 ('a b c', NS(foo=['a', 'b'], bar=['c'])),
976 ]
977
978
979class TestPositionalsNargs2OneOrMore(ParserTestCase):
980 """Test a Positional with 2 nargs followed by one with one or more"""
981
982 argument_signatures = [Sig('foo', nargs=2), Sig('bar', nargs='+')]
983 failures = ['', '--foo', 'a', 'a b']
984 successes = [
985 ('a b c', NS(foo=['a', 'b'], bar=['c'])),
986 ]
987
988
989class TestPositionalsNargs2Optional(ParserTestCase):
990 """Test a Positional with 2 nargs followed by one optional"""
991
992 argument_signatures = [Sig('foo', nargs=2), Sig('bar', nargs='?')]
993 failures = ['', '--foo', 'a', 'a b c d']
994 successes = [
995 ('a b', NS(foo=['a', 'b'], bar=None)),
996 ('a b c', NS(foo=['a', 'b'], bar='c')),
997 ]
998
999
1000class TestPositionalsNargsZeroOrMore1(ParserTestCase):
1001 """Test a Positional with unlimited nargs followed by one with 1"""
1002
1003 argument_signatures = [Sig('foo', nargs='*'), Sig('bar', nargs=1)]
1004 failures = ['', '--foo', ]
1005 successes = [
1006 ('a', NS(foo=[], bar=['a'])),
1007 ('a b', NS(foo=['a'], bar=['b'])),
1008 ('a b c', NS(foo=['a', 'b'], bar=['c'])),
1009 ]
1010
1011
1012class TestPositionalsNargsOneOrMore1(ParserTestCase):
1013 """Test a Positional with one or more nargs followed by one with 1"""
1014
1015 argument_signatures = [Sig('foo', nargs='+'), Sig('bar', nargs=1)]
1016 failures = ['', '--foo', 'a']
1017 successes = [
1018 ('a b', NS(foo=['a'], bar=['b'])),
1019 ('a b c', NS(foo=['a', 'b'], bar=['c'])),
1020 ]
1021
1022
1023class TestPositionalsNargsOptional1(ParserTestCase):
1024 """Test a Positional with an Optional nargs followed by one with 1"""
1025
1026 argument_signatures = [Sig('foo', nargs='?'), Sig('bar', nargs=1)]
1027 failures = ['', '--foo', 'a b c']
1028 successes = [
1029 ('a', NS(foo=None, bar=['a'])),
1030 ('a b', NS(foo='a', bar=['b'])),
1031 ]
1032
1033
1034class TestPositionalsNargsNoneZeroOrMore1(ParserTestCase):
1035 """Test three Positionals: no nargs, unlimited nargs and 1 nargs"""
1036
1037 argument_signatures = [
1038 Sig('foo'),
1039 Sig('bar', nargs='*'),
1040 Sig('baz', nargs=1),
1041 ]
1042 failures = ['', '--foo', 'a']
1043 successes = [
1044 ('a b', NS(foo='a', bar=[], baz=['b'])),
1045 ('a b c', NS(foo='a', bar=['b'], baz=['c'])),
1046 ]
1047
1048
1049class TestPositionalsNargsNoneOneOrMore1(ParserTestCase):
1050 """Test three Positionals: no nargs, one or more nargs and 1 nargs"""
1051
1052 argument_signatures = [
1053 Sig('foo'),
1054 Sig('bar', nargs='+'),
1055 Sig('baz', nargs=1),
1056 ]
1057 failures = ['', '--foo', 'a', 'b']
1058 successes = [
1059 ('a b c', NS(foo='a', bar=['b'], baz=['c'])),
1060 ('a b c d', NS(foo='a', bar=['b', 'c'], baz=['d'])),
1061 ]
1062
1063
1064class TestPositionalsNargsNoneOptional1(ParserTestCase):
1065 """Test three Positionals: no nargs, optional narg and 1 nargs"""
1066
1067 argument_signatures = [
1068 Sig('foo'),
1069 Sig('bar', nargs='?', default=0.625),
1070 Sig('baz', nargs=1),
1071 ]
1072 failures = ['', '--foo', 'a']
1073 successes = [
1074 ('a b', NS(foo='a', bar=0.625, baz=['b'])),
1075 ('a b c', NS(foo='a', bar='b', baz=['c'])),
1076 ]
1077
1078
1079class TestPositionalsNargsOptionalOptional(ParserTestCase):
1080 """Test two optional nargs"""
1081
1082 argument_signatures = [
1083 Sig('foo', nargs='?'),
1084 Sig('bar', nargs='?', default=42),
1085 ]
1086 failures = ['--foo', 'a b c']
1087 successes = [
1088 ('', NS(foo=None, bar=42)),
1089 ('a', NS(foo='a', bar=42)),
1090 ('a b', NS(foo='a', bar='b')),
1091 ]
1092
1093
1094class TestPositionalsNargsOptionalZeroOrMore(ParserTestCase):
1095 """Test an Optional narg followed by unlimited nargs"""
1096
1097 argument_signatures = [Sig('foo', nargs='?'), Sig('bar', nargs='*')]
1098 failures = ['--foo']
1099 successes = [
1100 ('', NS(foo=None, bar=[])),
1101 ('a', NS(foo='a', bar=[])),
1102 ('a b', NS(foo='a', bar=['b'])),
1103 ('a b c', NS(foo='a', bar=['b', 'c'])),
1104 ]
1105
1106
1107class TestPositionalsNargsOptionalOneOrMore(ParserTestCase):
1108 """Test an Optional narg followed by one or more nargs"""
1109
1110 argument_signatures = [Sig('foo', nargs='?'), Sig('bar', nargs='+')]
1111 failures = ['', '--foo']
1112 successes = [
1113 ('a', NS(foo=None, bar=['a'])),
1114 ('a b', NS(foo='a', bar=['b'])),
1115 ('a b c', NS(foo='a', bar=['b', 'c'])),
1116 ]
1117
1118
1119class TestPositionalsChoicesString(ParserTestCase):
1120 """Test a set of single-character choices"""
1121
1122 argument_signatures = [Sig('spam', choices=set('abcdefg'))]
1123 failures = ['', '--foo', 'h', '42', 'ef']
1124 successes = [
1125 ('a', NS(spam='a')),
1126 ('g', NS(spam='g')),
1127 ]
1128
1129
1130class TestPositionalsChoicesInt(ParserTestCase):
1131 """Test a set of integer choices"""
1132
1133 argument_signatures = [Sig('spam', type=int, choices=range(20))]
1134 failures = ['', '--foo', 'h', '42', 'ef']
1135 successes = [
1136 ('4', NS(spam=4)),
1137 ('15', NS(spam=15)),
1138 ]
1139
1140
1141class TestPositionalsActionAppend(ParserTestCase):
1142 """Test the 'append' action"""
1143
1144 argument_signatures = [
1145 Sig('spam', action='append'),
1146 Sig('spam', action='append', nargs=2),
1147 ]
1148 failures = ['', '--foo', 'a', 'a b', 'a b c d']
1149 successes = [
1150 ('a b c', NS(spam=['a', ['b', 'c']])),
1151 ]
1152
1153# ========================================
1154# Combined optionals and positionals tests
1155# ========================================
1156
1157class TestOptionalsNumericAndPositionals(ParserTestCase):
1158 """Tests negative number args when numeric options are present"""
1159
1160 argument_signatures = [
1161 Sig('x', nargs='?'),
1162 Sig('-4', dest='y', action='store_true'),
1163 ]
1164 failures = ['-2', '-315']
1165 successes = [
1166 ('', NS(x=None, y=False)),
1167 ('a', NS(x='a', y=False)),
1168 ('-4', NS(x=None, y=True)),
1169 ('-4 a', NS(x='a', y=True)),
1170 ]
1171
1172
1173class TestOptionalsAlmostNumericAndPositionals(ParserTestCase):
1174 """Tests negative number args when almost numeric options are present"""
1175
1176 argument_signatures = [
1177 Sig('x', nargs='?'),
1178 Sig('-k4', dest='y', action='store_true'),
1179 ]
1180 failures = ['-k3']
1181 successes = [
1182 ('', NS(x=None, y=False)),
1183 ('-2', NS(x='-2', y=False)),
1184 ('a', NS(x='a', y=False)),
1185 ('-k4', NS(x=None, y=True)),
1186 ('-k4 a', NS(x='a', y=True)),
1187 ]
1188
1189
1190class TestEmptyAndSpaceContainingArguments(ParserTestCase):
1191
1192 argument_signatures = [
1193 Sig('x', nargs='?'),
1194 Sig('-y', '--yyy', dest='y'),
1195 ]
1196 failures = ['-y']
1197 successes = [
1198 ([''], NS(x='', y=None)),
1199 (['a badger'], NS(x='a badger', y=None)),
1200 (['-a badger'], NS(x='-a badger', y=None)),
1201 (['-y', ''], NS(x=None, y='')),
1202 (['-y', 'a badger'], NS(x=None, y='a badger')),
1203 (['-y', '-a badger'], NS(x=None, y='-a badger')),
1204 (['--yyy=a badger'], NS(x=None, y='a badger')),
1205 (['--yyy=-a badger'], NS(x=None, y='-a badger')),
1206 ]
1207
1208
1209class TestPrefixCharacterOnlyArguments(ParserTestCase):
1210
1211 parser_signature = Sig(prefix_chars='-+')
1212 argument_signatures = [
1213 Sig('-', dest='x', nargs='?', const='badger'),
1214 Sig('+', dest='y', type=int, default=42),
1215 Sig('-+-', dest='z', action='store_true'),
1216 ]
1217 failures = ['-y', '+ -']
1218 successes = [
1219 ('', NS(x=None, y=42, z=False)),
1220 ('-', NS(x='badger', y=42, z=False)),
1221 ('- X', NS(x='X', y=42, z=False)),
1222 ('+ -3', NS(x=None, y=-3, z=False)),
1223 ('-+-', NS(x=None, y=42, z=True)),
1224 ('- ===', NS(x='===', y=42, z=False)),
1225 ]
1226
1227
1228class TestNargsZeroOrMore(ParserTestCase):
1229 """Tests specifying an args for an Optional that accepts zero or more"""
1230
1231 argument_signatures = [Sig('-x', nargs='*'), Sig('y', nargs='*')]
1232 failures = []
1233 successes = [
1234 ('', NS(x=None, y=[])),
1235 ('-x', NS(x=[], y=[])),
1236 ('-x a', NS(x=['a'], y=[])),
1237 ('-x a -- b', NS(x=['a'], y=['b'])),
1238 ('a', NS(x=None, y=['a'])),
1239 ('a -x', NS(x=[], y=['a'])),
1240 ('a -x b', NS(x=['b'], y=['a'])),
1241 ]
1242
1243
1244class TestNargsRemainder(ParserTestCase):
1245 """Tests specifying a positional with nargs=REMAINDER"""
1246
1247 argument_signatures = [Sig('x'), Sig('y', nargs='...'), Sig('-z')]
1248 failures = ['', '-z', '-z Z']
1249 successes = [
1250 ('X', NS(x='X', y=[], z=None)),
1251 ('-z Z X', NS(x='X', y=[], z='Z')),
1252 ('X A B -z Z', NS(x='X', y=['A', 'B', '-z', 'Z'], z=None)),
1253 ('X Y --foo', NS(x='X', y=['Y', '--foo'], z=None)),
1254 ]
1255
1256
1257class TestOptionLike(ParserTestCase):
1258 """Tests options that may or may not be arguments"""
1259
1260 argument_signatures = [
1261 Sig('-x', type=float),
1262 Sig('-3', type=float, dest='y'),
1263 Sig('z', nargs='*'),
1264 ]
1265 failures = ['-x', '-y2.5', '-xa', '-x -a',
1266 '-x -3', '-x -3.5', '-3 -3.5',
1267 '-x -2.5', '-x -2.5 a', '-3 -.5',
1268 'a x -1', '-x -1 a', '-3 -1 a']
1269 successes = [
1270 ('', NS(x=None, y=None, z=[])),
1271 ('-x 2.5', NS(x=2.5, y=None, z=[])),
1272 ('-x 2.5 a', NS(x=2.5, y=None, z=['a'])),
1273 ('-3.5', NS(x=None, y=0.5, z=[])),
1274 ('-3-.5', NS(x=None, y=-0.5, z=[])),
1275 ('-3 .5', NS(x=None, y=0.5, z=[])),
1276 ('a -3.5', NS(x=None, y=0.5, z=['a'])),
1277 ('a', NS(x=None, y=None, z=['a'])),
1278 ('a -x 1', NS(x=1.0, y=None, z=['a'])),
1279 ('-x 1 a', NS(x=1.0, y=None, z=['a'])),
1280 ('-3 1 a', NS(x=None, y=1.0, z=['a'])),
1281 ]
1282
1283
1284class TestDefaultSuppress(ParserTestCase):
1285 """Test actions with suppressed defaults"""
1286
1287 argument_signatures = [
1288 Sig('foo', nargs='?', default=argparse.SUPPRESS),
1289 Sig('bar', nargs='*', default=argparse.SUPPRESS),
1290 Sig('--baz', action='store_true', default=argparse.SUPPRESS),
1291 ]
1292 failures = ['-x']
1293 successes = [
1294 ('', NS()),
1295 ('a', NS(foo='a')),
1296 ('a b', NS(foo='a', bar=['b'])),
1297 ('--baz', NS(baz=True)),
1298 ('a --baz', NS(foo='a', baz=True)),
1299 ('--baz a b', NS(foo='a', bar=['b'], baz=True)),
1300 ]
1301
1302
1303class TestParserDefaultSuppress(ParserTestCase):
1304 """Test actions with a parser-level default of SUPPRESS"""
1305
1306 parser_signature = Sig(argument_default=argparse.SUPPRESS)
1307 argument_signatures = [
1308 Sig('foo', nargs='?'),
1309 Sig('bar', nargs='*'),
1310 Sig('--baz', action='store_true'),
1311 ]
1312 failures = ['-x']
1313 successes = [
1314 ('', NS()),
1315 ('a', NS(foo='a')),
1316 ('a b', NS(foo='a', bar=['b'])),
1317 ('--baz', NS(baz=True)),
1318 ('a --baz', NS(foo='a', baz=True)),
1319 ('--baz a b', NS(foo='a', bar=['b'], baz=True)),
1320 ]
1321
1322
1323class TestParserDefault42(ParserTestCase):
1324 """Test actions with a parser-level default of 42"""
1325
1326 parser_signature = Sig(argument_default=42, version='1.0')
1327 argument_signatures = [
1328 Sig('foo', nargs='?'),
1329 Sig('bar', nargs='*'),
1330 Sig('--baz', action='store_true'),
1331 ]
1332 failures = ['-x']
1333 successes = [
1334 ('', NS(foo=42, bar=42, baz=42)),
1335 ('a', NS(foo='a', bar=42, baz=42)),
1336 ('a b', NS(foo='a', bar=['b'], baz=42)),
1337 ('--baz', NS(foo=42, bar=42, baz=True)),
1338 ('a --baz', NS(foo='a', bar=42, baz=True)),
1339 ('--baz a b', NS(foo='a', bar=['b'], baz=True)),
1340 ]
1341
1342
1343class TestArgumentsFromFile(TempDirMixin, ParserTestCase):
1344 """Test reading arguments from a file"""
1345
1346 def setUp(self):
1347 super(TestArgumentsFromFile, self).setUp()
1348 file_texts = [
1349 ('hello', 'hello world!\n'),
1350 ('recursive', '-a\n'
1351 'A\n'
1352 '@hello'),
1353 ('invalid', '@no-such-path\n'),
1354 ]
1355 for path, text in file_texts:
1356 file = open(path, 'w')
1357 file.write(text)
1358 file.close()
1359
1360 parser_signature = Sig(fromfile_prefix_chars='@')
1361 argument_signatures = [
1362 Sig('-a'),
1363 Sig('x'),
1364 Sig('y', nargs='+'),
1365 ]
1366 failures = ['', '-b', 'X', '@invalid', '@missing']
1367 successes = [
1368 ('X Y', NS(a=None, x='X', y=['Y'])),
1369 ('X -a A Y Z', NS(a='A', x='X', y=['Y', 'Z'])),
1370 ('@hello X', NS(a=None, x='hello world!', y=['X'])),
1371 ('X @hello', NS(a=None, x='X', y=['hello world!'])),
1372 ('-a B @recursive Y Z', NS(a='A', x='hello world!', y=['Y', 'Z'])),
1373 ('X @recursive Z -a B', NS(a='B', x='X', y=['hello world!', 'Z'])),
1374 ]
1375
1376
1377class TestArgumentsFromFileConverter(TempDirMixin, ParserTestCase):
1378 """Test reading arguments from a file"""
1379
1380 def setUp(self):
1381 super(TestArgumentsFromFileConverter, self).setUp()
1382 file_texts = [
1383 ('hello', 'hello world!\n'),
1384 ]
1385 for path, text in file_texts:
1386 file = open(path, 'w')
1387 file.write(text)
1388 file.close()
1389
1390 class FromFileConverterArgumentParser(ErrorRaisingArgumentParser):
1391
1392 def convert_arg_line_to_args(self, arg_line):
1393 for arg in arg_line.split():
1394 if not arg.strip():
1395 continue
1396 yield arg
1397 parser_class = FromFileConverterArgumentParser
1398 parser_signature = Sig(fromfile_prefix_chars='@')
1399 argument_signatures = [
1400 Sig('y', nargs='+'),
1401 ]
1402 failures = []
1403 successes = [
1404 ('@hello X', NS(y=['hello', 'world!', 'X'])),
1405 ]
1406
1407
1408# =====================
1409# Type conversion tests
1410# =====================
1411
1412class TestFileTypeRepr(TestCase):
1413
1414 def test_r(self):
1415 type = argparse.FileType('r')
1416 self.assertEqual("FileType('r')", repr(type))
1417
1418 def test_wb_1(self):
1419 type = argparse.FileType('wb', 1)
1420 self.assertEqual("FileType('wb', 1)", repr(type))
1421
1422
1423class RFile(object):
1424 seen = {}
1425
1426 def __init__(self, name):
1427 self.name = name
1428
1429 def __eq__(self, other):
1430 if other in self.seen:
1431 text = self.seen[other]
1432 else:
1433 text = self.seen[other] = other.read()
1434 other.close()
1435 if not isinstance(text, str):
1436 text = text.decode('ascii')
1437 return self.name == other.name == text
1438
1439
1440class TestFileTypeR(TempDirMixin, ParserTestCase):
1441 """Test the FileType option/argument type for reading files"""
1442
1443 def setUp(self):
1444 super(TestFileTypeR, self).setUp()
1445 for file_name in ['foo', 'bar']:
1446 file = open(os.path.join(self.temp_dir, file_name), 'w')
1447 file.write(file_name)
1448 file.close()
Steven Bethardb0270112011-01-24 21:02:50 +00001449 self.create_readonly_file('readonly')
Benjamin Peterson698a18a2010-03-02 22:34:37 +00001450
1451 argument_signatures = [
1452 Sig('-x', type=argparse.FileType()),
1453 Sig('spam', type=argparse.FileType('r')),
1454 ]
Steven Bethardb0270112011-01-24 21:02:50 +00001455 failures = ['-x', '', 'non-existent-file.txt']
Benjamin Peterson698a18a2010-03-02 22:34:37 +00001456 successes = [
1457 ('foo', NS(x=None, spam=RFile('foo'))),
1458 ('-x foo bar', NS(x=RFile('foo'), spam=RFile('bar'))),
1459 ('bar -x foo', NS(x=RFile('foo'), spam=RFile('bar'))),
1460 ('-x - -', NS(x=sys.stdin, spam=sys.stdin)),
Steven Bethardb0270112011-01-24 21:02:50 +00001461 ('readonly', NS(x=None, spam=RFile('readonly'))),
Benjamin Peterson698a18a2010-03-02 22:34:37 +00001462 ]
1463
1464
1465class TestFileTypeRB(TempDirMixin, ParserTestCase):
1466 """Test the FileType option/argument type for reading files"""
1467
1468 def setUp(self):
1469 super(TestFileTypeRB, self).setUp()
1470 for file_name in ['foo', 'bar']:
1471 file = open(os.path.join(self.temp_dir, file_name), 'w')
1472 file.write(file_name)
1473 file.close()
1474
1475 argument_signatures = [
1476 Sig('-x', type=argparse.FileType('rb')),
1477 Sig('spam', type=argparse.FileType('rb')),
1478 ]
1479 failures = ['-x', '']
1480 successes = [
1481 ('foo', NS(x=None, spam=RFile('foo'))),
1482 ('-x foo bar', NS(x=RFile('foo'), spam=RFile('bar'))),
1483 ('bar -x foo', NS(x=RFile('foo'), spam=RFile('bar'))),
1484 ('-x - -', NS(x=sys.stdin, spam=sys.stdin)),
1485 ]
1486
1487
1488class WFile(object):
1489 seen = set()
1490
1491 def __init__(self, name):
1492 self.name = name
1493
1494 def __eq__(self, other):
1495 if other not in self.seen:
1496 text = 'Check that file is writable.'
1497 if 'b' in other.mode:
1498 text = text.encode('ascii')
1499 other.write(text)
1500 other.close()
1501 self.seen.add(other)
1502 return self.name == other.name
1503
1504
1505class TestFileTypeW(TempDirMixin, ParserTestCase):
1506 """Test the FileType option/argument type for writing files"""
1507
Steven Bethardb0270112011-01-24 21:02:50 +00001508 def setUp(self):
1509 super(TestFileTypeW, self).setUp()
1510 self.create_readonly_file('readonly')
1511
Benjamin Peterson698a18a2010-03-02 22:34:37 +00001512 argument_signatures = [
1513 Sig('-x', type=argparse.FileType('w')),
1514 Sig('spam', type=argparse.FileType('w')),
1515 ]
Steven Bethardb0270112011-01-24 21:02:50 +00001516 failures = ['-x', '', 'readonly']
Benjamin Peterson698a18a2010-03-02 22:34:37 +00001517 successes = [
1518 ('foo', NS(x=None, spam=WFile('foo'))),
1519 ('-x foo bar', NS(x=WFile('foo'), spam=WFile('bar'))),
1520 ('bar -x foo', NS(x=WFile('foo'), spam=WFile('bar'))),
1521 ('-x - -', NS(x=sys.stdout, spam=sys.stdout)),
1522 ]
1523
1524
1525class TestFileTypeWB(TempDirMixin, ParserTestCase):
1526
1527 argument_signatures = [
1528 Sig('-x', type=argparse.FileType('wb')),
1529 Sig('spam', type=argparse.FileType('wb')),
1530 ]
1531 failures = ['-x', '']
1532 successes = [
1533 ('foo', NS(x=None, spam=WFile('foo'))),
1534 ('-x foo bar', NS(x=WFile('foo'), spam=WFile('bar'))),
1535 ('bar -x foo', NS(x=WFile('foo'), spam=WFile('bar'))),
1536 ('-x - -', NS(x=sys.stdout, spam=sys.stdout)),
1537 ]
1538
1539
1540class TestTypeCallable(ParserTestCase):
1541 """Test some callables as option/argument types"""
1542
1543 argument_signatures = [
1544 Sig('--eggs', type=complex),
1545 Sig('spam', type=float),
1546 ]
1547 failures = ['a', '42j', '--eggs a', '--eggs 2i']
1548 successes = [
1549 ('--eggs=42 42', NS(eggs=42, spam=42.0)),
1550 ('--eggs 2j -- -1.5', NS(eggs=2j, spam=-1.5)),
1551 ('1024.675', NS(eggs=None, spam=1024.675)),
1552 ]
1553
1554
1555class TestTypeUserDefined(ParserTestCase):
1556 """Test a user-defined option/argument type"""
1557
1558 class MyType(TestCase):
1559
1560 def __init__(self, value):
1561 self.value = value
1562
1563 def __eq__(self, other):
1564 return (type(self), self.value) == (type(other), other.value)
1565
1566 argument_signatures = [
1567 Sig('-x', type=MyType),
1568 Sig('spam', type=MyType),
1569 ]
1570 failures = []
1571 successes = [
1572 ('a -x b', NS(x=MyType('b'), spam=MyType('a'))),
1573 ('-xf g', NS(x=MyType('f'), spam=MyType('g'))),
1574 ]
1575
1576
1577class TestTypeClassicClass(ParserTestCase):
1578 """Test a classic class type"""
1579
1580 class C:
1581
1582 def __init__(self, value):
1583 self.value = value
1584
1585 def __eq__(self, other):
1586 return (type(self), self.value) == (type(other), other.value)
1587
1588 argument_signatures = [
1589 Sig('-x', type=C),
1590 Sig('spam', type=C),
1591 ]
1592 failures = []
1593 successes = [
1594 ('a -x b', NS(x=C('b'), spam=C('a'))),
1595 ('-xf g', NS(x=C('f'), spam=C('g'))),
1596 ]
1597
1598
1599class TestTypeRegistration(TestCase):
1600 """Test a user-defined type by registering it"""
1601
1602 def test(self):
1603
1604 def get_my_type(string):
1605 return 'my_type{%s}' % string
1606
1607 parser = argparse.ArgumentParser()
1608 parser.register('type', 'my_type', get_my_type)
1609 parser.add_argument('-x', type='my_type')
1610 parser.add_argument('y', type='my_type')
1611
1612 self.assertEqual(parser.parse_args('1'.split()),
1613 NS(x=None, y='my_type{1}'))
1614 self.assertEqual(parser.parse_args('-x 1 42'.split()),
1615 NS(x='my_type{1}', y='my_type{42}'))
1616
1617
1618# ============
1619# Action tests
1620# ============
1621
1622class TestActionUserDefined(ParserTestCase):
1623 """Test a user-defined option/argument action"""
1624
1625 class OptionalAction(argparse.Action):
1626
1627 def __call__(self, parser, namespace, value, option_string=None):
1628 try:
1629 # check destination and option string
1630 assert self.dest == 'spam', 'dest: %s' % self.dest
1631 assert option_string == '-s', 'flag: %s' % option_string
1632 # when option is before argument, badger=2, and when
1633 # option is after argument, badger=<whatever was set>
1634 expected_ns = NS(spam=0.25)
1635 if value in [0.125, 0.625]:
1636 expected_ns.badger = 2
1637 elif value in [2.0]:
1638 expected_ns.badger = 84
1639 else:
1640 raise AssertionError('value: %s' % value)
1641 assert expected_ns == namespace, ('expected %s, got %s' %
1642 (expected_ns, namespace))
1643 except AssertionError:
1644 e = sys.exc_info()[1]
1645 raise ArgumentParserError('opt_action failed: %s' % e)
1646 setattr(namespace, 'spam', value)
1647
1648 class PositionalAction(argparse.Action):
1649
1650 def __call__(self, parser, namespace, value, option_string=None):
1651 try:
1652 assert option_string is None, ('option_string: %s' %
1653 option_string)
1654 # check destination
1655 assert self.dest == 'badger', 'dest: %s' % self.dest
1656 # when argument is before option, spam=0.25, and when
1657 # option is after argument, spam=<whatever was set>
1658 expected_ns = NS(badger=2)
1659 if value in [42, 84]:
1660 expected_ns.spam = 0.25
1661 elif value in [1]:
1662 expected_ns.spam = 0.625
1663 elif value in [2]:
1664 expected_ns.spam = 0.125
1665 else:
1666 raise AssertionError('value: %s' % value)
1667 assert expected_ns == namespace, ('expected %s, got %s' %
1668 (expected_ns, namespace))
1669 except AssertionError:
1670 e = sys.exc_info()[1]
1671 raise ArgumentParserError('arg_action failed: %s' % e)
1672 setattr(namespace, 'badger', value)
1673
1674 argument_signatures = [
1675 Sig('-s', dest='spam', action=OptionalAction,
1676 type=float, default=0.25),
1677 Sig('badger', action=PositionalAction,
1678 type=int, nargs='?', default=2),
1679 ]
1680 failures = []
1681 successes = [
1682 ('-s0.125', NS(spam=0.125, badger=2)),
1683 ('42', NS(spam=0.25, badger=42)),
1684 ('-s 0.625 1', NS(spam=0.625, badger=1)),
1685 ('84 -s2', NS(spam=2.0, badger=84)),
1686 ]
1687
1688
1689class TestActionRegistration(TestCase):
1690 """Test a user-defined action supplied by registering it"""
1691
1692 class MyAction(argparse.Action):
1693
1694 def __call__(self, parser, namespace, values, option_string=None):
1695 setattr(namespace, self.dest, 'foo[%s]' % values)
1696
1697 def test(self):
1698
1699 parser = argparse.ArgumentParser()
1700 parser.register('action', 'my_action', self.MyAction)
1701 parser.add_argument('badger', action='my_action')
1702
1703 self.assertEqual(parser.parse_args(['1']), NS(badger='foo[1]'))
1704 self.assertEqual(parser.parse_args(['42']), NS(badger='foo[42]'))
1705
1706
1707# ================
1708# Subparsers tests
1709# ================
1710
1711class TestAddSubparsers(TestCase):
1712 """Test the add_subparsers method"""
1713
1714 def assertArgumentParserError(self, *args, **kwargs):
1715 self.assertRaises(ArgumentParserError, *args, **kwargs)
1716
Steven Bethardfd311a72010-12-18 11:19:23 +00001717 def _get_parser(self, subparser_help=False, prefix_chars=None,
1718 aliases=False):
Benjamin Peterson698a18a2010-03-02 22:34:37 +00001719 # create a parser with a subparsers argument
R. David Murray88c49fe2010-08-03 17:56:09 +00001720 if prefix_chars:
1721 parser = ErrorRaisingArgumentParser(
1722 prog='PROG', description='main description', prefix_chars=prefix_chars)
1723 parser.add_argument(
1724 prefix_chars[0] * 2 + 'foo', action='store_true', help='foo help')
1725 else:
1726 parser = ErrorRaisingArgumentParser(
1727 prog='PROG', description='main description')
1728 parser.add_argument(
1729 '--foo', action='store_true', help='foo help')
Benjamin Peterson698a18a2010-03-02 22:34:37 +00001730 parser.add_argument(
1731 'bar', type=float, help='bar help')
1732
1733 # check that only one subparsers argument can be added
Steven Bethardfd311a72010-12-18 11:19:23 +00001734 subparsers_kwargs = {}
1735 if aliases:
1736 subparsers_kwargs['metavar'] = 'COMMAND'
1737 subparsers_kwargs['title'] = 'commands'
1738 else:
1739 subparsers_kwargs['help'] = 'command help'
1740 subparsers = parser.add_subparsers(**subparsers_kwargs)
Benjamin Peterson698a18a2010-03-02 22:34:37 +00001741 self.assertArgumentParserError(parser.add_subparsers)
1742
1743 # add first sub-parser
1744 parser1_kwargs = dict(description='1 description')
1745 if subparser_help:
1746 parser1_kwargs['help'] = '1 help'
Steven Bethardfd311a72010-12-18 11:19:23 +00001747 if aliases:
1748 parser1_kwargs['aliases'] = ['1alias1', '1alias2']
Benjamin Peterson698a18a2010-03-02 22:34:37 +00001749 parser1 = subparsers.add_parser('1', **parser1_kwargs)
1750 parser1.add_argument('-w', type=int, help='w help')
1751 parser1.add_argument('x', choices='abc', help='x help')
1752
1753 # add second sub-parser
1754 parser2_kwargs = dict(description='2 description')
1755 if subparser_help:
1756 parser2_kwargs['help'] = '2 help'
1757 parser2 = subparsers.add_parser('2', **parser2_kwargs)
1758 parser2.add_argument('-y', choices='123', help='y help')
1759 parser2.add_argument('z', type=complex, nargs='*', help='z help')
1760
1761 # return the main parser
1762 return parser
1763
1764 def setUp(self):
Steven Bethard1f1c2472010-11-01 13:56:09 +00001765 super().setUp()
Benjamin Peterson698a18a2010-03-02 22:34:37 +00001766 self.parser = self._get_parser()
1767 self.command_help_parser = self._get_parser(subparser_help=True)
1768
1769 def test_parse_args_failures(self):
1770 # check some failure cases:
1771 for args_str in ['', 'a', 'a a', '0.5 a', '0.5 1',
1772 '0.5 1 -y', '0.5 2 -w']:
1773 args = args_str.split()
1774 self.assertArgumentParserError(self.parser.parse_args, args)
1775
1776 def test_parse_args(self):
1777 # check some non-failure cases:
1778 self.assertEqual(
1779 self.parser.parse_args('0.5 1 b -w 7'.split()),
1780 NS(foo=False, bar=0.5, w=7, x='b'),
1781 )
1782 self.assertEqual(
1783 self.parser.parse_args('0.25 --foo 2 -y 2 3j -- -1j'.split()),
1784 NS(foo=True, bar=0.25, y='2', z=[3j, -1j]),
1785 )
1786 self.assertEqual(
1787 self.parser.parse_args('--foo 0.125 1 c'.split()),
1788 NS(foo=True, bar=0.125, w=None, x='c'),
1789 )
1790
Steven Bethardfca2e8a2010-11-02 12:47:22 +00001791 def test_parse_known_args(self):
1792 self.assertEqual(
1793 self.parser.parse_known_args('0.5 1 b -w 7'.split()),
1794 (NS(foo=False, bar=0.5, w=7, x='b'), []),
1795 )
1796 self.assertEqual(
1797 self.parser.parse_known_args('0.5 -p 1 b -w 7'.split()),
1798 (NS(foo=False, bar=0.5, w=7, x='b'), ['-p']),
1799 )
1800 self.assertEqual(
1801 self.parser.parse_known_args('0.5 1 b -w 7 -p'.split()),
1802 (NS(foo=False, bar=0.5, w=7, x='b'), ['-p']),
1803 )
1804 self.assertEqual(
1805 self.parser.parse_known_args('0.5 1 b -q -rs -w 7'.split()),
1806 (NS(foo=False, bar=0.5, w=7, x='b'), ['-q', '-rs']),
1807 )
1808 self.assertEqual(
1809 self.parser.parse_known_args('0.5 -W 1 b -X Y -w 7 Z'.split()),
1810 (NS(foo=False, bar=0.5, w=7, x='b'), ['-W', '-X', 'Y', 'Z']),
1811 )
1812
Benjamin Peterson698a18a2010-03-02 22:34:37 +00001813 def test_dest(self):
1814 parser = ErrorRaisingArgumentParser()
1815 parser.add_argument('--foo', action='store_true')
1816 subparsers = parser.add_subparsers(dest='bar')
1817 parser1 = subparsers.add_parser('1')
1818 parser1.add_argument('baz')
1819 self.assertEqual(NS(foo=False, bar='1', baz='2'),
1820 parser.parse_args('1 2'.split()))
1821
1822 def test_help(self):
1823 self.assertEqual(self.parser.format_usage(),
1824 'usage: PROG [-h] [--foo] bar {1,2} ...\n')
1825 self.assertEqual(self.parser.format_help(), textwrap.dedent('''\
1826 usage: PROG [-h] [--foo] bar {1,2} ...
1827
1828 main description
1829
1830 positional arguments:
1831 bar bar help
1832 {1,2} command help
1833
1834 optional arguments:
1835 -h, --help show this help message and exit
1836 --foo foo help
1837 '''))
1838
R. David Murray88c49fe2010-08-03 17:56:09 +00001839 def test_help_extra_prefix_chars(self):
1840 # Make sure - is still used for help if it is a non-first prefix char
1841 parser = self._get_parser(prefix_chars='+:-')
1842 self.assertEqual(parser.format_usage(),
1843 'usage: PROG [-h] [++foo] bar {1,2} ...\n')
1844 self.assertEqual(parser.format_help(), textwrap.dedent('''\
1845 usage: PROG [-h] [++foo] bar {1,2} ...
1846
1847 main description
1848
1849 positional arguments:
1850 bar bar help
1851 {1,2} command help
1852
1853 optional arguments:
1854 -h, --help show this help message and exit
1855 ++foo foo help
1856 '''))
1857
1858
1859 def test_help_alternate_prefix_chars(self):
1860 parser = self._get_parser(prefix_chars='+:/')
1861 self.assertEqual(parser.format_usage(),
1862 'usage: PROG [+h] [++foo] bar {1,2} ...\n')
1863 self.assertEqual(parser.format_help(), textwrap.dedent('''\
1864 usage: PROG [+h] [++foo] bar {1,2} ...
1865
1866 main description
1867
1868 positional arguments:
1869 bar bar help
1870 {1,2} command help
1871
1872 optional arguments:
1873 +h, ++help show this help message and exit
1874 ++foo foo help
1875 '''))
1876
Benjamin Peterson698a18a2010-03-02 22:34:37 +00001877 def test_parser_command_help(self):
1878 self.assertEqual(self.command_help_parser.format_usage(),
1879 'usage: PROG [-h] [--foo] bar {1,2} ...\n')
1880 self.assertEqual(self.command_help_parser.format_help(),
1881 textwrap.dedent('''\
1882 usage: PROG [-h] [--foo] bar {1,2} ...
1883
1884 main description
1885
1886 positional arguments:
1887 bar bar help
1888 {1,2} command help
1889 1 1 help
1890 2 2 help
1891
1892 optional arguments:
1893 -h, --help show this help message and exit
1894 --foo foo help
1895 '''))
1896
1897 def test_subparser_title_help(self):
1898 parser = ErrorRaisingArgumentParser(prog='PROG',
1899 description='main description')
1900 parser.add_argument('--foo', action='store_true', help='foo help')
1901 parser.add_argument('bar', help='bar help')
1902 subparsers = parser.add_subparsers(title='subcommands',
1903 description='command help',
1904 help='additional text')
1905 parser1 = subparsers.add_parser('1')
1906 parser2 = subparsers.add_parser('2')
1907 self.assertEqual(parser.format_usage(),
1908 'usage: PROG [-h] [--foo] bar {1,2} ...\n')
1909 self.assertEqual(parser.format_help(), textwrap.dedent('''\
1910 usage: PROG [-h] [--foo] bar {1,2} ...
1911
1912 main description
1913
1914 positional arguments:
1915 bar bar help
1916
1917 optional arguments:
1918 -h, --help show this help message and exit
1919 --foo foo help
1920
1921 subcommands:
1922 command help
1923
1924 {1,2} additional text
1925 '''))
1926
1927 def _test_subparser_help(self, args_str, expected_help):
1928 try:
1929 self.parser.parse_args(args_str.split())
1930 except ArgumentParserError:
1931 err = sys.exc_info()[1]
1932 if err.stdout != expected_help:
1933 print(repr(expected_help))
1934 print(repr(err.stdout))
1935 self.assertEqual(err.stdout, expected_help)
1936
1937 def test_subparser1_help(self):
1938 self._test_subparser_help('5.0 1 -h', textwrap.dedent('''\
1939 usage: PROG bar 1 [-h] [-w W] {a,b,c}
1940
1941 1 description
1942
1943 positional arguments:
1944 {a,b,c} x help
1945
1946 optional arguments:
1947 -h, --help show this help message and exit
1948 -w W w help
1949 '''))
1950
1951 def test_subparser2_help(self):
1952 self._test_subparser_help('5.0 2 -h', textwrap.dedent('''\
1953 usage: PROG bar 2 [-h] [-y {1,2,3}] [z [z ...]]
1954
1955 2 description
1956
1957 positional arguments:
1958 z z help
1959
1960 optional arguments:
1961 -h, --help show this help message and exit
1962 -y {1,2,3} y help
1963 '''))
1964
Steven Bethardfd311a72010-12-18 11:19:23 +00001965 def test_alias_invocation(self):
1966 parser = self._get_parser(aliases=True)
1967 self.assertEqual(
1968 parser.parse_known_args('0.5 1alias1 b'.split()),
1969 (NS(foo=False, bar=0.5, w=None, x='b'), []),
1970 )
1971 self.assertEqual(
1972 parser.parse_known_args('0.5 1alias2 b'.split()),
1973 (NS(foo=False, bar=0.5, w=None, x='b'), []),
1974 )
1975
1976 def test_error_alias_invocation(self):
1977 parser = self._get_parser(aliases=True)
1978 self.assertArgumentParserError(parser.parse_args,
1979 '0.5 1alias3 b'.split())
1980
1981 def test_alias_help(self):
1982 parser = self._get_parser(aliases=True, subparser_help=True)
1983 self.maxDiff = None
1984 self.assertEqual(parser.format_help(), textwrap.dedent("""\
1985 usage: PROG [-h] [--foo] bar COMMAND ...
1986
1987 main description
1988
1989 positional arguments:
1990 bar bar help
1991
1992 optional arguments:
1993 -h, --help show this help message and exit
1994 --foo foo help
1995
1996 commands:
1997 COMMAND
1998 1 (1alias1, 1alias2)
1999 1 help
2000 2 2 help
2001 """))
2002
Benjamin Peterson698a18a2010-03-02 22:34:37 +00002003# ============
2004# Groups tests
2005# ============
2006
2007class TestPositionalsGroups(TestCase):
2008 """Tests that order of group positionals matches construction order"""
2009
2010 def test_nongroup_first(self):
2011 parser = ErrorRaisingArgumentParser()
2012 parser.add_argument('foo')
2013 group = parser.add_argument_group('g')
2014 group.add_argument('bar')
2015 parser.add_argument('baz')
2016 expected = NS(foo='1', bar='2', baz='3')
2017 result = parser.parse_args('1 2 3'.split())
2018 self.assertEqual(expected, result)
2019
2020 def test_group_first(self):
2021 parser = ErrorRaisingArgumentParser()
2022 group = parser.add_argument_group('xxx')
2023 group.add_argument('foo')
2024 parser.add_argument('bar')
2025 parser.add_argument('baz')
2026 expected = NS(foo='1', bar='2', baz='3')
2027 result = parser.parse_args('1 2 3'.split())
2028 self.assertEqual(expected, result)
2029
2030 def test_interleaved_groups(self):
2031 parser = ErrorRaisingArgumentParser()
2032 group = parser.add_argument_group('xxx')
2033 parser.add_argument('foo')
2034 group.add_argument('bar')
2035 parser.add_argument('baz')
2036 group = parser.add_argument_group('yyy')
2037 group.add_argument('frell')
2038 expected = NS(foo='1', bar='2', baz='3', frell='4')
2039 result = parser.parse_args('1 2 3 4'.split())
2040 self.assertEqual(expected, result)
2041
2042# ===================
2043# Parent parser tests
2044# ===================
2045
2046class TestParentParsers(TestCase):
2047 """Tests that parsers can be created with parent parsers"""
2048
2049 def assertArgumentParserError(self, *args, **kwargs):
2050 self.assertRaises(ArgumentParserError, *args, **kwargs)
2051
2052 def setUp(self):
Steven Bethard1f1c2472010-11-01 13:56:09 +00002053 super().setUp()
Benjamin Peterson698a18a2010-03-02 22:34:37 +00002054 self.wxyz_parent = ErrorRaisingArgumentParser(add_help=False)
2055 self.wxyz_parent.add_argument('--w')
2056 x_group = self.wxyz_parent.add_argument_group('x')
2057 x_group.add_argument('-y')
2058 self.wxyz_parent.add_argument('z')
2059
2060 self.abcd_parent = ErrorRaisingArgumentParser(add_help=False)
2061 self.abcd_parent.add_argument('a')
2062 self.abcd_parent.add_argument('-b')
2063 c_group = self.abcd_parent.add_argument_group('c')
2064 c_group.add_argument('--d')
2065
2066 self.w_parent = ErrorRaisingArgumentParser(add_help=False)
2067 self.w_parent.add_argument('--w')
2068
2069 self.z_parent = ErrorRaisingArgumentParser(add_help=False)
2070 self.z_parent.add_argument('z')
2071
2072 # parents with mutually exclusive groups
2073 self.ab_mutex_parent = ErrorRaisingArgumentParser(add_help=False)
2074 group = self.ab_mutex_parent.add_mutually_exclusive_group()
2075 group.add_argument('-a', action='store_true')
2076 group.add_argument('-b', action='store_true')
2077
2078 self.main_program = os.path.basename(sys.argv[0])
2079
2080 def test_single_parent(self):
2081 parser = ErrorRaisingArgumentParser(parents=[self.wxyz_parent])
2082 self.assertEqual(parser.parse_args('-y 1 2 --w 3'.split()),
2083 NS(w='3', y='1', z='2'))
2084
2085 def test_single_parent_mutex(self):
2086 self._test_mutex_ab(self.ab_mutex_parent.parse_args)
2087 parser = ErrorRaisingArgumentParser(parents=[self.ab_mutex_parent])
2088 self._test_mutex_ab(parser.parse_args)
2089
2090 def test_single_granparent_mutex(self):
2091 parents = [self.ab_mutex_parent]
2092 parser = ErrorRaisingArgumentParser(add_help=False, parents=parents)
2093 parser = ErrorRaisingArgumentParser(parents=[parser])
2094 self._test_mutex_ab(parser.parse_args)
2095
2096 def _test_mutex_ab(self, parse_args):
2097 self.assertEqual(parse_args([]), NS(a=False, b=False))
2098 self.assertEqual(parse_args(['-a']), NS(a=True, b=False))
2099 self.assertEqual(parse_args(['-b']), NS(a=False, b=True))
2100 self.assertArgumentParserError(parse_args, ['-a', '-b'])
2101 self.assertArgumentParserError(parse_args, ['-b', '-a'])
2102 self.assertArgumentParserError(parse_args, ['-c'])
2103 self.assertArgumentParserError(parse_args, ['-a', '-c'])
2104 self.assertArgumentParserError(parse_args, ['-b', '-c'])
2105
2106 def test_multiple_parents(self):
2107 parents = [self.abcd_parent, self.wxyz_parent]
2108 parser = ErrorRaisingArgumentParser(parents=parents)
2109 self.assertEqual(parser.parse_args('--d 1 --w 2 3 4'.split()),
2110 NS(a='3', b=None, d='1', w='2', y=None, z='4'))
2111
2112 def test_multiple_parents_mutex(self):
2113 parents = [self.ab_mutex_parent, self.wxyz_parent]
2114 parser = ErrorRaisingArgumentParser(parents=parents)
2115 self.assertEqual(parser.parse_args('-a --w 2 3'.split()),
2116 NS(a=True, b=False, w='2', y=None, z='3'))
2117 self.assertArgumentParserError(
2118 parser.parse_args, '-a --w 2 3 -b'.split())
2119 self.assertArgumentParserError(
2120 parser.parse_args, '-a -b --w 2 3'.split())
2121
2122 def test_conflicting_parents(self):
2123 self.assertRaises(
2124 argparse.ArgumentError,
2125 argparse.ArgumentParser,
2126 parents=[self.w_parent, self.wxyz_parent])
2127
2128 def test_conflicting_parents_mutex(self):
2129 self.assertRaises(
2130 argparse.ArgumentError,
2131 argparse.ArgumentParser,
2132 parents=[self.abcd_parent, self.ab_mutex_parent])
2133
2134 def test_same_argument_name_parents(self):
2135 parents = [self.wxyz_parent, self.z_parent]
2136 parser = ErrorRaisingArgumentParser(parents=parents)
2137 self.assertEqual(parser.parse_args('1 2'.split()),
2138 NS(w=None, y=None, z='2'))
2139
2140 def test_subparser_parents(self):
2141 parser = ErrorRaisingArgumentParser()
2142 subparsers = parser.add_subparsers()
2143 abcde_parser = subparsers.add_parser('bar', parents=[self.abcd_parent])
2144 abcde_parser.add_argument('e')
2145 self.assertEqual(parser.parse_args('bar -b 1 --d 2 3 4'.split()),
2146 NS(a='3', b='1', d='2', e='4'))
2147
2148 def test_subparser_parents_mutex(self):
2149 parser = ErrorRaisingArgumentParser()
2150 subparsers = parser.add_subparsers()
2151 parents = [self.ab_mutex_parent]
2152 abc_parser = subparsers.add_parser('foo', parents=parents)
2153 c_group = abc_parser.add_argument_group('c_group')
2154 c_group.add_argument('c')
2155 parents = [self.wxyz_parent, self.ab_mutex_parent]
2156 wxyzabe_parser = subparsers.add_parser('bar', parents=parents)
2157 wxyzabe_parser.add_argument('e')
2158 self.assertEqual(parser.parse_args('foo -a 4'.split()),
2159 NS(a=True, b=False, c='4'))
2160 self.assertEqual(parser.parse_args('bar -b --w 2 3 4'.split()),
2161 NS(a=False, b=True, w='2', y=None, z='3', e='4'))
2162 self.assertArgumentParserError(
2163 parser.parse_args, 'foo -a -b 4'.split())
2164 self.assertArgumentParserError(
2165 parser.parse_args, 'bar -b -a 4'.split())
2166
2167 def test_parent_help(self):
2168 parents = [self.abcd_parent, self.wxyz_parent]
2169 parser = ErrorRaisingArgumentParser(parents=parents)
2170 parser_help = parser.format_help()
2171 self.assertEqual(parser_help, textwrap.dedent('''\
2172 usage: {} [-h] [-b B] [--d D] [--w W] [-y Y] a z
2173
2174 positional arguments:
2175 a
2176 z
2177
2178 optional arguments:
2179 -h, --help show this help message and exit
2180 -b B
2181 --w W
2182
2183 c:
2184 --d D
2185
2186 x:
2187 -y Y
2188 '''.format(self.main_program)))
2189
2190 def test_groups_parents(self):
2191 parent = ErrorRaisingArgumentParser(add_help=False)
2192 g = parent.add_argument_group(title='g', description='gd')
2193 g.add_argument('-w')
2194 g.add_argument('-x')
2195 m = parent.add_mutually_exclusive_group()
2196 m.add_argument('-y')
2197 m.add_argument('-z')
2198 parser = ErrorRaisingArgumentParser(parents=[parent])
2199
2200 self.assertRaises(ArgumentParserError, parser.parse_args,
2201 ['-y', 'Y', '-z', 'Z'])
2202
2203 parser_help = parser.format_help()
2204 self.assertEqual(parser_help, textwrap.dedent('''\
2205 usage: {} [-h] [-w W] [-x X] [-y Y | -z Z]
2206
2207 optional arguments:
2208 -h, --help show this help message and exit
2209 -y Y
2210 -z Z
2211
2212 g:
2213 gd
2214
2215 -w W
2216 -x X
2217 '''.format(self.main_program)))
2218
2219# ==============================
2220# Mutually exclusive group tests
2221# ==============================
2222
2223class TestMutuallyExclusiveGroupErrors(TestCase):
2224
2225 def test_invalid_add_argument_group(self):
2226 parser = ErrorRaisingArgumentParser()
2227 raises = self.assertRaises
2228 raises(TypeError, parser.add_mutually_exclusive_group, title='foo')
2229
2230 def test_invalid_add_argument(self):
2231 parser = ErrorRaisingArgumentParser()
2232 group = parser.add_mutually_exclusive_group()
2233 add_argument = group.add_argument
2234 raises = self.assertRaises
2235 raises(ValueError, add_argument, '--foo', required=True)
2236 raises(ValueError, add_argument, 'bar')
2237 raises(ValueError, add_argument, 'bar', nargs='+')
2238 raises(ValueError, add_argument, 'bar', nargs=1)
2239 raises(ValueError, add_argument, 'bar', nargs=argparse.PARSER)
2240
Steven Bethard49998ee2010-11-01 16:29:26 +00002241 def test_help(self):
2242 parser = ErrorRaisingArgumentParser(prog='PROG')
2243 group1 = parser.add_mutually_exclusive_group()
2244 group1.add_argument('--foo', action='store_true')
2245 group1.add_argument('--bar', action='store_false')
2246 group2 = parser.add_mutually_exclusive_group()
2247 group2.add_argument('--soup', action='store_true')
2248 group2.add_argument('--nuts', action='store_false')
2249 expected = '''\
2250 usage: PROG [-h] [--foo | --bar] [--soup | --nuts]
2251
2252 optional arguments:
2253 -h, --help show this help message and exit
2254 --foo
2255 --bar
2256 --soup
2257 --nuts
2258 '''
2259 self.assertEqual(parser.format_help(), textwrap.dedent(expected))
Benjamin Peterson698a18a2010-03-02 22:34:37 +00002260
2261class MEMixin(object):
2262
2263 def test_failures_when_not_required(self):
2264 parse_args = self.get_parser(required=False).parse_args
2265 error = ArgumentParserError
2266 for args_string in self.failures:
2267 self.assertRaises(error, parse_args, args_string.split())
2268
2269 def test_failures_when_required(self):
2270 parse_args = self.get_parser(required=True).parse_args
2271 error = ArgumentParserError
2272 for args_string in self.failures + ['']:
2273 self.assertRaises(error, parse_args, args_string.split())
2274
2275 def test_successes_when_not_required(self):
2276 parse_args = self.get_parser(required=False).parse_args
2277 successes = self.successes + self.successes_when_not_required
2278 for args_string, expected_ns in successes:
2279 actual_ns = parse_args(args_string.split())
2280 self.assertEqual(actual_ns, expected_ns)
2281
2282 def test_successes_when_required(self):
2283 parse_args = self.get_parser(required=True).parse_args
2284 for args_string, expected_ns in self.successes:
2285 actual_ns = parse_args(args_string.split())
2286 self.assertEqual(actual_ns, expected_ns)
2287
2288 def test_usage_when_not_required(self):
2289 format_usage = self.get_parser(required=False).format_usage
2290 expected_usage = self.usage_when_not_required
2291 self.assertEqual(format_usage(), textwrap.dedent(expected_usage))
2292
2293 def test_usage_when_required(self):
2294 format_usage = self.get_parser(required=True).format_usage
2295 expected_usage = self.usage_when_required
2296 self.assertEqual(format_usage(), textwrap.dedent(expected_usage))
2297
2298 def test_help_when_not_required(self):
2299 format_help = self.get_parser(required=False).format_help
2300 help = self.usage_when_not_required + self.help
2301 self.assertEqual(format_help(), textwrap.dedent(help))
2302
2303 def test_help_when_required(self):
2304 format_help = self.get_parser(required=True).format_help
2305 help = self.usage_when_required + self.help
2306 self.assertEqual(format_help(), textwrap.dedent(help))
2307
2308
2309class TestMutuallyExclusiveSimple(MEMixin, TestCase):
2310
2311 def get_parser(self, required=None):
2312 parser = ErrorRaisingArgumentParser(prog='PROG')
2313 group = parser.add_mutually_exclusive_group(required=required)
2314 group.add_argument('--bar', help='bar help')
2315 group.add_argument('--baz', nargs='?', const='Z', help='baz help')
2316 return parser
2317
2318 failures = ['--bar X --baz Y', '--bar X --baz']
2319 successes = [
2320 ('--bar X', NS(bar='X', baz=None)),
2321 ('--bar X --bar Z', NS(bar='Z', baz=None)),
2322 ('--baz Y', NS(bar=None, baz='Y')),
2323 ('--baz', NS(bar=None, baz='Z')),
2324 ]
2325 successes_when_not_required = [
2326 ('', NS(bar=None, baz=None)),
2327 ]
2328
2329 usage_when_not_required = '''\
2330 usage: PROG [-h] [--bar BAR | --baz [BAZ]]
2331 '''
2332 usage_when_required = '''\
2333 usage: PROG [-h] (--bar BAR | --baz [BAZ])
2334 '''
2335 help = '''\
2336
2337 optional arguments:
2338 -h, --help show this help message and exit
2339 --bar BAR bar help
2340 --baz [BAZ] baz help
2341 '''
2342
2343
2344class TestMutuallyExclusiveLong(MEMixin, TestCase):
2345
2346 def get_parser(self, required=None):
2347 parser = ErrorRaisingArgumentParser(prog='PROG')
2348 parser.add_argument('--abcde', help='abcde help')
2349 parser.add_argument('--fghij', help='fghij help')
2350 group = parser.add_mutually_exclusive_group(required=required)
2351 group.add_argument('--klmno', help='klmno help')
2352 group.add_argument('--pqrst', help='pqrst help')
2353 return parser
2354
2355 failures = ['--klmno X --pqrst Y']
2356 successes = [
2357 ('--klmno X', NS(abcde=None, fghij=None, klmno='X', pqrst=None)),
2358 ('--abcde Y --klmno X',
2359 NS(abcde='Y', fghij=None, klmno='X', pqrst=None)),
2360 ('--pqrst X', NS(abcde=None, fghij=None, klmno=None, pqrst='X')),
2361 ('--pqrst X --fghij Y',
2362 NS(abcde=None, fghij='Y', klmno=None, pqrst='X')),
2363 ]
2364 successes_when_not_required = [
2365 ('', NS(abcde=None, fghij=None, klmno=None, pqrst=None)),
2366 ]
2367
2368 usage_when_not_required = '''\
2369 usage: PROG [-h] [--abcde ABCDE] [--fghij FGHIJ]
2370 [--klmno KLMNO | --pqrst PQRST]
2371 '''
2372 usage_when_required = '''\
2373 usage: PROG [-h] [--abcde ABCDE] [--fghij FGHIJ]
2374 (--klmno KLMNO | --pqrst PQRST)
2375 '''
2376 help = '''\
2377
2378 optional arguments:
2379 -h, --help show this help message and exit
2380 --abcde ABCDE abcde help
2381 --fghij FGHIJ fghij help
2382 --klmno KLMNO klmno help
2383 --pqrst PQRST pqrst help
2384 '''
2385
2386
2387class TestMutuallyExclusiveFirstSuppressed(MEMixin, TestCase):
2388
2389 def get_parser(self, required):
2390 parser = ErrorRaisingArgumentParser(prog='PROG')
2391 group = parser.add_mutually_exclusive_group(required=required)
2392 group.add_argument('-x', help=argparse.SUPPRESS)
2393 group.add_argument('-y', action='store_false', help='y help')
2394 return parser
2395
2396 failures = ['-x X -y']
2397 successes = [
2398 ('-x X', NS(x='X', y=True)),
2399 ('-x X -x Y', NS(x='Y', y=True)),
2400 ('-y', NS(x=None, y=False)),
2401 ]
2402 successes_when_not_required = [
2403 ('', NS(x=None, y=True)),
2404 ]
2405
2406 usage_when_not_required = '''\
2407 usage: PROG [-h] [-y]
2408 '''
2409 usage_when_required = '''\
2410 usage: PROG [-h] -y
2411 '''
2412 help = '''\
2413
2414 optional arguments:
2415 -h, --help show this help message and exit
2416 -y y help
2417 '''
2418
2419
2420class TestMutuallyExclusiveManySuppressed(MEMixin, TestCase):
2421
2422 def get_parser(self, required):
2423 parser = ErrorRaisingArgumentParser(prog='PROG')
2424 group = parser.add_mutually_exclusive_group(required=required)
2425 add = group.add_argument
2426 add('--spam', action='store_true', help=argparse.SUPPRESS)
2427 add('--badger', action='store_false', help=argparse.SUPPRESS)
2428 add('--bladder', help=argparse.SUPPRESS)
2429 return parser
2430
2431 failures = [
2432 '--spam --badger',
2433 '--badger --bladder B',
2434 '--bladder B --spam',
2435 ]
2436 successes = [
2437 ('--spam', NS(spam=True, badger=True, bladder=None)),
2438 ('--badger', NS(spam=False, badger=False, bladder=None)),
2439 ('--bladder B', NS(spam=False, badger=True, bladder='B')),
2440 ('--spam --spam', NS(spam=True, badger=True, bladder=None)),
2441 ]
2442 successes_when_not_required = [
2443 ('', NS(spam=False, badger=True, bladder=None)),
2444 ]
2445
2446 usage_when_required = usage_when_not_required = '''\
2447 usage: PROG [-h]
2448 '''
2449 help = '''\
2450
2451 optional arguments:
2452 -h, --help show this help message and exit
2453 '''
2454
2455
2456class TestMutuallyExclusiveOptionalAndPositional(MEMixin, TestCase):
2457
2458 def get_parser(self, required):
2459 parser = ErrorRaisingArgumentParser(prog='PROG')
2460 group = parser.add_mutually_exclusive_group(required=required)
2461 group.add_argument('--foo', action='store_true', help='FOO')
2462 group.add_argument('--spam', help='SPAM')
2463 group.add_argument('badger', nargs='*', default='X', help='BADGER')
2464 return parser
2465
2466 failures = [
2467 '--foo --spam S',
2468 '--spam S X',
2469 'X --foo',
2470 'X Y Z --spam S',
2471 '--foo X Y',
2472 ]
2473 successes = [
2474 ('--foo', NS(foo=True, spam=None, badger='X')),
2475 ('--spam S', NS(foo=False, spam='S', badger='X')),
2476 ('X', NS(foo=False, spam=None, badger=['X'])),
2477 ('X Y Z', NS(foo=False, spam=None, badger=['X', 'Y', 'Z'])),
2478 ]
2479 successes_when_not_required = [
2480 ('', NS(foo=False, spam=None, badger='X')),
2481 ]
2482
2483 usage_when_not_required = '''\
2484 usage: PROG [-h] [--foo | --spam SPAM | badger [badger ...]]
2485 '''
2486 usage_when_required = '''\
2487 usage: PROG [-h] (--foo | --spam SPAM | badger [badger ...])
2488 '''
2489 help = '''\
2490
2491 positional arguments:
2492 badger BADGER
2493
2494 optional arguments:
2495 -h, --help show this help message and exit
2496 --foo FOO
2497 --spam SPAM SPAM
2498 '''
2499
2500
2501class TestMutuallyExclusiveOptionalsMixed(MEMixin, TestCase):
2502
2503 def get_parser(self, required):
2504 parser = ErrorRaisingArgumentParser(prog='PROG')
2505 parser.add_argument('-x', action='store_true', help='x help')
2506 group = parser.add_mutually_exclusive_group(required=required)
2507 group.add_argument('-a', action='store_true', help='a help')
2508 group.add_argument('-b', action='store_true', help='b help')
2509 parser.add_argument('-y', action='store_true', help='y help')
2510 group.add_argument('-c', action='store_true', help='c help')
2511 return parser
2512
2513 failures = ['-a -b', '-b -c', '-a -c', '-a -b -c']
2514 successes = [
2515 ('-a', NS(a=True, b=False, c=False, x=False, y=False)),
2516 ('-b', NS(a=False, b=True, c=False, x=False, y=False)),
2517 ('-c', NS(a=False, b=False, c=True, x=False, y=False)),
2518 ('-a -x', NS(a=True, b=False, c=False, x=True, y=False)),
2519 ('-y -b', NS(a=False, b=True, c=False, x=False, y=True)),
2520 ('-x -y -c', NS(a=False, b=False, c=True, x=True, y=True)),
2521 ]
2522 successes_when_not_required = [
2523 ('', NS(a=False, b=False, c=False, x=False, y=False)),
2524 ('-x', NS(a=False, b=False, c=False, x=True, y=False)),
2525 ('-y', NS(a=False, b=False, c=False, x=False, y=True)),
2526 ]
2527
2528 usage_when_required = usage_when_not_required = '''\
2529 usage: PROG [-h] [-x] [-a] [-b] [-y] [-c]
2530 '''
2531 help = '''\
2532
2533 optional arguments:
2534 -h, --help show this help message and exit
2535 -x x help
2536 -a a help
2537 -b b help
2538 -y y help
2539 -c c help
2540 '''
2541
2542
Georg Brandl0f6b47a2011-01-30 12:19:35 +00002543class TestMutuallyExclusiveInGroup(MEMixin, TestCase):
2544
2545 def get_parser(self, required=None):
2546 parser = ErrorRaisingArgumentParser(prog='PROG')
2547 titled_group = parser.add_argument_group(
2548 title='Titled group', description='Group description')
2549 mutex_group = \
2550 titled_group.add_mutually_exclusive_group(required=required)
2551 mutex_group.add_argument('--bar', help='bar help')
2552 mutex_group.add_argument('--baz', help='baz help')
2553 return parser
2554
2555 failures = ['--bar X --baz Y', '--baz X --bar Y']
2556 successes = [
2557 ('--bar X', NS(bar='X', baz=None)),
2558 ('--baz Y', NS(bar=None, baz='Y')),
2559 ]
2560 successes_when_not_required = [
2561 ('', NS(bar=None, baz=None)),
2562 ]
2563
2564 usage_when_not_required = '''\
2565 usage: PROG [-h] [--bar BAR | --baz BAZ]
2566 '''
2567 usage_when_required = '''\
2568 usage: PROG [-h] (--bar BAR | --baz BAZ)
2569 '''
2570 help = '''\
2571
2572 optional arguments:
2573 -h, --help show this help message and exit
2574
2575 Titled group:
2576 Group description
2577
2578 --bar BAR bar help
2579 --baz BAZ baz help
2580 '''
2581
2582
Benjamin Peterson698a18a2010-03-02 22:34:37 +00002583class TestMutuallyExclusiveOptionalsAndPositionalsMixed(MEMixin, TestCase):
2584
2585 def get_parser(self, required):
2586 parser = ErrorRaisingArgumentParser(prog='PROG')
2587 parser.add_argument('x', help='x help')
2588 parser.add_argument('-y', action='store_true', help='y help')
2589 group = parser.add_mutually_exclusive_group(required=required)
2590 group.add_argument('a', nargs='?', help='a help')
2591 group.add_argument('-b', action='store_true', help='b help')
2592 group.add_argument('-c', action='store_true', help='c help')
2593 return parser
2594
2595 failures = ['X A -b', '-b -c', '-c X A']
2596 successes = [
2597 ('X A', NS(a='A', b=False, c=False, x='X', y=False)),
2598 ('X -b', NS(a=None, b=True, c=False, x='X', y=False)),
2599 ('X -c', NS(a=None, b=False, c=True, x='X', y=False)),
2600 ('X A -y', NS(a='A', b=False, c=False, x='X', y=True)),
2601 ('X -y -b', NS(a=None, b=True, c=False, x='X', y=True)),
2602 ]
2603 successes_when_not_required = [
2604 ('X', NS(a=None, b=False, c=False, x='X', y=False)),
2605 ('X -y', NS(a=None, b=False, c=False, x='X', y=True)),
2606 ]
2607
2608 usage_when_required = usage_when_not_required = '''\
2609 usage: PROG [-h] [-y] [-b] [-c] x [a]
2610 '''
2611 help = '''\
2612
2613 positional arguments:
2614 x x help
2615 a a help
2616
2617 optional arguments:
2618 -h, --help show this help message and exit
2619 -y y help
2620 -b b help
2621 -c c help
2622 '''
2623
2624# =================================================
2625# Mutually exclusive group in parent parser tests
2626# =================================================
2627
2628class MEPBase(object):
2629
2630 def get_parser(self, required=None):
2631 parent = super(MEPBase, self).get_parser(required=required)
2632 parser = ErrorRaisingArgumentParser(
2633 prog=parent.prog, add_help=False, parents=[parent])
2634 return parser
2635
2636
2637class TestMutuallyExclusiveGroupErrorsParent(
2638 MEPBase, TestMutuallyExclusiveGroupErrors):
2639 pass
2640
2641
2642class TestMutuallyExclusiveSimpleParent(
2643 MEPBase, TestMutuallyExclusiveSimple):
2644 pass
2645
2646
2647class TestMutuallyExclusiveLongParent(
2648 MEPBase, TestMutuallyExclusiveLong):
2649 pass
2650
2651
2652class TestMutuallyExclusiveFirstSuppressedParent(
2653 MEPBase, TestMutuallyExclusiveFirstSuppressed):
2654 pass
2655
2656
2657class TestMutuallyExclusiveManySuppressedParent(
2658 MEPBase, TestMutuallyExclusiveManySuppressed):
2659 pass
2660
2661
2662class TestMutuallyExclusiveOptionalAndPositionalParent(
2663 MEPBase, TestMutuallyExclusiveOptionalAndPositional):
2664 pass
2665
2666
2667class TestMutuallyExclusiveOptionalsMixedParent(
2668 MEPBase, TestMutuallyExclusiveOptionalsMixed):
2669 pass
2670
2671
2672class TestMutuallyExclusiveOptionalsAndPositionalsMixedParent(
2673 MEPBase, TestMutuallyExclusiveOptionalsAndPositionalsMixed):
2674 pass
2675
2676# =================
2677# Set default tests
2678# =================
2679
2680class TestSetDefaults(TestCase):
2681
2682 def test_set_defaults_no_args(self):
2683 parser = ErrorRaisingArgumentParser()
2684 parser.set_defaults(x='foo')
2685 parser.set_defaults(y='bar', z=1)
2686 self.assertEqual(NS(x='foo', y='bar', z=1),
2687 parser.parse_args([]))
2688 self.assertEqual(NS(x='foo', y='bar', z=1),
2689 parser.parse_args([], NS()))
2690 self.assertEqual(NS(x='baz', y='bar', z=1),
2691 parser.parse_args([], NS(x='baz')))
2692 self.assertEqual(NS(x='baz', y='bar', z=2),
2693 parser.parse_args([], NS(x='baz', z=2)))
2694
2695 def test_set_defaults_with_args(self):
2696 parser = ErrorRaisingArgumentParser()
2697 parser.set_defaults(x='foo', y='bar')
2698 parser.add_argument('-x', default='xfoox')
2699 self.assertEqual(NS(x='xfoox', y='bar'),
2700 parser.parse_args([]))
2701 self.assertEqual(NS(x='xfoox', y='bar'),
2702 parser.parse_args([], NS()))
2703 self.assertEqual(NS(x='baz', y='bar'),
2704 parser.parse_args([], NS(x='baz')))
2705 self.assertEqual(NS(x='1', y='bar'),
2706 parser.parse_args('-x 1'.split()))
2707 self.assertEqual(NS(x='1', y='bar'),
2708 parser.parse_args('-x 1'.split(), NS()))
2709 self.assertEqual(NS(x='1', y='bar'),
2710 parser.parse_args('-x 1'.split(), NS(x='baz')))
2711
2712 def test_set_defaults_subparsers(self):
2713 parser = ErrorRaisingArgumentParser()
2714 parser.set_defaults(x='foo')
2715 subparsers = parser.add_subparsers()
2716 parser_a = subparsers.add_parser('a')
2717 parser_a.set_defaults(y='bar')
2718 self.assertEqual(NS(x='foo', y='bar'),
2719 parser.parse_args('a'.split()))
2720
2721 def test_set_defaults_parents(self):
2722 parent = ErrorRaisingArgumentParser(add_help=False)
2723 parent.set_defaults(x='foo')
2724 parser = ErrorRaisingArgumentParser(parents=[parent])
2725 self.assertEqual(NS(x='foo'), parser.parse_args([]))
2726
2727 def test_set_defaults_same_as_add_argument(self):
2728 parser = ErrorRaisingArgumentParser()
2729 parser.set_defaults(w='W', x='X', y='Y', z='Z')
2730 parser.add_argument('-w')
2731 parser.add_argument('-x', default='XX')
2732 parser.add_argument('y', nargs='?')
2733 parser.add_argument('z', nargs='?', default='ZZ')
2734
2735 # defaults set previously
2736 self.assertEqual(NS(w='W', x='XX', y='Y', z='ZZ'),
2737 parser.parse_args([]))
2738
2739 # reset defaults
2740 parser.set_defaults(w='WW', x='X', y='YY', z='Z')
2741 self.assertEqual(NS(w='WW', x='X', y='YY', z='Z'),
2742 parser.parse_args([]))
2743
2744 def test_set_defaults_same_as_add_argument_group(self):
2745 parser = ErrorRaisingArgumentParser()
2746 parser.set_defaults(w='W', x='X', y='Y', z='Z')
2747 group = parser.add_argument_group('foo')
2748 group.add_argument('-w')
2749 group.add_argument('-x', default='XX')
2750 group.add_argument('y', nargs='?')
2751 group.add_argument('z', nargs='?', default='ZZ')
2752
2753
2754 # defaults set previously
2755 self.assertEqual(NS(w='W', x='XX', y='Y', z='ZZ'),
2756 parser.parse_args([]))
2757
2758 # reset defaults
2759 parser.set_defaults(w='WW', x='X', y='YY', z='Z')
2760 self.assertEqual(NS(w='WW', x='X', y='YY', z='Z'),
2761 parser.parse_args([]))
2762
2763# =================
2764# Get default tests
2765# =================
2766
2767class TestGetDefault(TestCase):
2768
2769 def test_get_default(self):
2770 parser = ErrorRaisingArgumentParser()
2771 self.assertEqual(None, parser.get_default("foo"))
2772 self.assertEqual(None, parser.get_default("bar"))
2773
2774 parser.add_argument("--foo")
2775 self.assertEqual(None, parser.get_default("foo"))
2776 self.assertEqual(None, parser.get_default("bar"))
2777
2778 parser.add_argument("--bar", type=int, default=42)
2779 self.assertEqual(None, parser.get_default("foo"))
2780 self.assertEqual(42, parser.get_default("bar"))
2781
2782 parser.set_defaults(foo="badger")
2783 self.assertEqual("badger", parser.get_default("foo"))
2784 self.assertEqual(42, parser.get_default("bar"))
2785
2786# ==========================
2787# Namespace 'contains' tests
2788# ==========================
2789
2790class TestNamespaceContainsSimple(TestCase):
2791
2792 def test_empty(self):
2793 ns = argparse.Namespace()
Ezio Melottib3aedd42010-11-20 19:04:17 +00002794 self.assertEqual('' in ns, False)
2795 self.assertEqual('' not in ns, True)
2796 self.assertEqual('x' in ns, False)
Benjamin Peterson698a18a2010-03-02 22:34:37 +00002797
2798 def test_non_empty(self):
2799 ns = argparse.Namespace(x=1, y=2)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002800 self.assertEqual('x' in ns, True)
2801 self.assertEqual('x' not in ns, False)
2802 self.assertEqual('y' in ns, True)
2803 self.assertEqual('' in ns, False)
2804 self.assertEqual('xx' in ns, False)
2805 self.assertEqual('z' in ns, False)
Benjamin Peterson698a18a2010-03-02 22:34:37 +00002806
2807# =====================
2808# Help formatting tests
2809# =====================
2810
2811class TestHelpFormattingMetaclass(type):
2812
2813 def __init__(cls, name, bases, bodydict):
2814 if name == 'HelpTestCase':
2815 return
2816
2817 class AddTests(object):
2818
2819 def __init__(self, test_class, func_suffix, std_name):
2820 self.func_suffix = func_suffix
2821 self.std_name = std_name
2822
2823 for test_func in [self.test_format,
2824 self.test_print,
2825 self.test_print_file]:
2826 test_name = '%s_%s' % (test_func.__name__, func_suffix)
2827
2828 def test_wrapper(self, test_func=test_func):
2829 test_func(self)
2830 try:
2831 test_wrapper.__name__ = test_name
2832 except TypeError:
2833 pass
2834 setattr(test_class, test_name, test_wrapper)
2835
2836 def _get_parser(self, tester):
2837 parser = argparse.ArgumentParser(
2838 *tester.parser_signature.args,
2839 **tester.parser_signature.kwargs)
2840 for argument_sig in tester.argument_signatures:
2841 parser.add_argument(*argument_sig.args,
2842 **argument_sig.kwargs)
2843 group_signatures = tester.argument_group_signatures
2844 for group_sig, argument_sigs in group_signatures:
2845 group = parser.add_argument_group(*group_sig.args,
2846 **group_sig.kwargs)
2847 for argument_sig in argument_sigs:
2848 group.add_argument(*argument_sig.args,
2849 **argument_sig.kwargs)
2850 return parser
2851
2852 def _test(self, tester, parser_text):
2853 expected_text = getattr(tester, self.func_suffix)
2854 expected_text = textwrap.dedent(expected_text)
2855 if expected_text != parser_text:
2856 print(repr(expected_text))
2857 print(repr(parser_text))
2858 for char1, char2 in zip(expected_text, parser_text):
2859 if char1 != char2:
2860 print('first diff: %r %r' % (char1, char2))
2861 break
2862 tester.assertEqual(expected_text, parser_text)
2863
2864 def test_format(self, tester):
2865 parser = self._get_parser(tester)
2866 format = getattr(parser, 'format_%s' % self.func_suffix)
2867 self._test(tester, format())
2868
2869 def test_print(self, tester):
2870 parser = self._get_parser(tester)
2871 print_ = getattr(parser, 'print_%s' % self.func_suffix)
2872 old_stream = getattr(sys, self.std_name)
Benjamin Petersonb48af542010-04-11 20:43:16 +00002873 setattr(sys, self.std_name, StdIOBuffer())
Benjamin Peterson698a18a2010-03-02 22:34:37 +00002874 try:
2875 print_()
2876 parser_text = getattr(sys, self.std_name).getvalue()
2877 finally:
2878 setattr(sys, self.std_name, old_stream)
2879 self._test(tester, parser_text)
2880
2881 def test_print_file(self, tester):
2882 parser = self._get_parser(tester)
2883 print_ = getattr(parser, 'print_%s' % self.func_suffix)
Benjamin Petersonb48af542010-04-11 20:43:16 +00002884 sfile = StdIOBuffer()
Benjamin Peterson698a18a2010-03-02 22:34:37 +00002885 print_(sfile)
2886 parser_text = sfile.getvalue()
2887 self._test(tester, parser_text)
2888
2889 # add tests for {format,print}_{usage,help,version}
2890 for func_suffix, std_name in [('usage', 'stdout'),
2891 ('help', 'stdout'),
2892 ('version', 'stderr')]:
2893 AddTests(cls, func_suffix, std_name)
2894
2895bases = TestCase,
2896HelpTestCase = TestHelpFormattingMetaclass('HelpTestCase', bases, {})
2897
2898
2899class TestHelpBiggerOptionals(HelpTestCase):
2900 """Make sure that argument help aligns when options are longer"""
2901
2902 parser_signature = Sig(prog='PROG', description='DESCRIPTION',
2903 epilog='EPILOG', version='0.1')
2904 argument_signatures = [
2905 Sig('-x', action='store_true', help='X HELP'),
2906 Sig('--y', help='Y HELP'),
2907 Sig('foo', help='FOO HELP'),
2908 Sig('bar', help='BAR HELP'),
2909 ]
2910 argument_group_signatures = []
2911 usage = '''\
2912 usage: PROG [-h] [-v] [-x] [--y Y] foo bar
2913 '''
2914 help = usage + '''\
2915
2916 DESCRIPTION
2917
2918 positional arguments:
2919 foo FOO HELP
2920 bar BAR HELP
2921
2922 optional arguments:
2923 -h, --help show this help message and exit
2924 -v, --version show program's version number and exit
2925 -x X HELP
2926 --y Y Y HELP
2927
2928 EPILOG
2929 '''
2930 version = '''\
2931 0.1
2932 '''
2933
2934
2935class TestHelpBiggerOptionalGroups(HelpTestCase):
2936 """Make sure that argument help aligns when options are longer"""
2937
2938 parser_signature = Sig(prog='PROG', description='DESCRIPTION',
2939 epilog='EPILOG', version='0.1')
2940 argument_signatures = [
2941 Sig('-x', action='store_true', help='X HELP'),
2942 Sig('--y', help='Y HELP'),
2943 Sig('foo', help='FOO HELP'),
2944 Sig('bar', help='BAR HELP'),
2945 ]
2946 argument_group_signatures = [
2947 (Sig('GROUP TITLE', description='GROUP DESCRIPTION'), [
2948 Sig('baz', help='BAZ HELP'),
2949 Sig('-z', nargs='+', help='Z HELP')]),
2950 ]
2951 usage = '''\
2952 usage: PROG [-h] [-v] [-x] [--y Y] [-z Z [Z ...]] foo bar baz
2953 '''
2954 help = usage + '''\
2955
2956 DESCRIPTION
2957
2958 positional arguments:
2959 foo FOO HELP
2960 bar BAR HELP
2961
2962 optional arguments:
2963 -h, --help show this help message and exit
2964 -v, --version show program's version number and exit
2965 -x X HELP
2966 --y Y Y HELP
2967
2968 GROUP TITLE:
2969 GROUP DESCRIPTION
2970
2971 baz BAZ HELP
2972 -z Z [Z ...] Z HELP
2973
2974 EPILOG
2975 '''
2976 version = '''\
2977 0.1
2978 '''
2979
2980
2981class TestHelpBiggerPositionals(HelpTestCase):
2982 """Make sure that help aligns when arguments are longer"""
2983
2984 parser_signature = Sig(usage='USAGE', description='DESCRIPTION')
2985 argument_signatures = [
2986 Sig('-x', action='store_true', help='X HELP'),
2987 Sig('--y', help='Y HELP'),
2988 Sig('ekiekiekifekang', help='EKI HELP'),
2989 Sig('bar', help='BAR HELP'),
2990 ]
2991 argument_group_signatures = []
2992 usage = '''\
2993 usage: USAGE
2994 '''
2995 help = usage + '''\
2996
2997 DESCRIPTION
2998
2999 positional arguments:
3000 ekiekiekifekang EKI HELP
3001 bar BAR HELP
3002
3003 optional arguments:
3004 -h, --help show this help message and exit
3005 -x X HELP
3006 --y Y Y HELP
3007 '''
3008
3009 version = ''
3010
3011
3012class TestHelpReformatting(HelpTestCase):
3013 """Make sure that text after short names starts on the first line"""
3014
3015 parser_signature = Sig(
3016 prog='PROG',
3017 description=' oddly formatted\n'
3018 'description\n'
3019 '\n'
3020 'that is so long that it should go onto multiple '
3021 'lines when wrapped')
3022 argument_signatures = [
3023 Sig('-x', metavar='XX', help='oddly\n'
3024 ' formatted -x help'),
3025 Sig('y', metavar='yyy', help='normal y help'),
3026 ]
3027 argument_group_signatures = [
3028 (Sig('title', description='\n'
3029 ' oddly formatted group\n'
3030 '\n'
3031 'description'),
3032 [Sig('-a', action='store_true',
3033 help=' oddly \n'
3034 'formatted -a help \n'
3035 ' again, so long that it should be wrapped over '
3036 'multiple lines')]),
3037 ]
3038 usage = '''\
3039 usage: PROG [-h] [-x XX] [-a] yyy
3040 '''
3041 help = usage + '''\
3042
3043 oddly formatted description that is so long that it should go onto \
3044multiple
3045 lines when wrapped
3046
3047 positional arguments:
3048 yyy normal y help
3049
3050 optional arguments:
3051 -h, --help show this help message and exit
3052 -x XX oddly formatted -x help
3053
3054 title:
3055 oddly formatted group description
3056
3057 -a oddly formatted -a help again, so long that it should \
3058be wrapped
3059 over multiple lines
3060 '''
3061 version = ''
3062
3063
3064class TestHelpWrappingShortNames(HelpTestCase):
3065 """Make sure that text after short names starts on the first line"""
3066
3067 parser_signature = Sig(prog='PROG', description= 'D\nD' * 30)
3068 argument_signatures = [
3069 Sig('-x', metavar='XX', help='XHH HX' * 20),
3070 Sig('y', metavar='yyy', help='YH YH' * 20),
3071 ]
3072 argument_group_signatures = [
3073 (Sig('ALPHAS'), [
3074 Sig('-a', action='store_true', help='AHHH HHA' * 10)]),
3075 ]
3076 usage = '''\
3077 usage: PROG [-h] [-x XX] [-a] yyy
3078 '''
3079 help = usage + '''\
3080
3081 D DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD \
3082DD DD DD
3083 DD DD DD DD D
3084
3085 positional arguments:
3086 yyy YH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH \
3087YHYH YHYH
3088 YHYH YHYH YHYH YHYH YHYH YHYH YHYH YH
3089
3090 optional arguments:
3091 -h, --help show this help message and exit
3092 -x XX XHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH \
3093HXXHH HXXHH
3094 HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HX
3095
3096 ALPHAS:
3097 -a AHHH HHAAHHH HHAAHHH HHAAHHH HHAAHHH HHAAHHH HHAAHHH \
3098HHAAHHH
3099 HHAAHHH HHAAHHH HHA
3100 '''
3101 version = ''
3102
3103
3104class TestHelpWrappingLongNames(HelpTestCase):
3105 """Make sure that text after long names starts on the next line"""
3106
3107 parser_signature = Sig(usage='USAGE', description= 'D D' * 30,
3108 version='V V'*30)
3109 argument_signatures = [
3110 Sig('-x', metavar='X' * 25, help='XH XH' * 20),
3111 Sig('y', metavar='y' * 25, help='YH YH' * 20),
3112 ]
3113 argument_group_signatures = [
3114 (Sig('ALPHAS'), [
3115 Sig('-a', metavar='A' * 25, help='AH AH' * 20),
3116 Sig('z', metavar='z' * 25, help='ZH ZH' * 20)]),
3117 ]
3118 usage = '''\
3119 usage: USAGE
3120 '''
3121 help = usage + '''\
3122
3123 D DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD \
3124DD DD DD
3125 DD DD DD DD D
3126
3127 positional arguments:
3128 yyyyyyyyyyyyyyyyyyyyyyyyy
3129 YH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH \
3130YHYH YHYH
3131 YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YH
3132
3133 optional arguments:
3134 -h, --help show this help message and exit
3135 -v, --version show program's version number and exit
3136 -x XXXXXXXXXXXXXXXXXXXXXXXXX
3137 XH XHXH XHXH XHXH XHXH XHXH XHXH XHXH XHXH \
3138XHXH XHXH
3139 XHXH XHXH XHXH XHXH XHXH XHXH XHXH XHXH XHXH XH
3140
3141 ALPHAS:
3142 -a AAAAAAAAAAAAAAAAAAAAAAAAA
3143 AH AHAH AHAH AHAH AHAH AHAH AHAH AHAH AHAH \
3144AHAH AHAH
3145 AHAH AHAH AHAH AHAH AHAH AHAH AHAH AHAH AHAH AH
3146 zzzzzzzzzzzzzzzzzzzzzzzzz
3147 ZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH \
3148ZHZH ZHZH
3149 ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZH
3150 '''
3151 version = '''\
3152 V VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV \
3153VV VV VV
3154 VV VV VV VV V
3155 '''
3156
3157
3158class TestHelpUsage(HelpTestCase):
3159 """Test basic usage messages"""
3160
3161 parser_signature = Sig(prog='PROG')
3162 argument_signatures = [
3163 Sig('-w', nargs='+', help='w'),
3164 Sig('-x', nargs='*', help='x'),
3165 Sig('a', help='a'),
3166 Sig('b', help='b', nargs=2),
3167 Sig('c', help='c', nargs='?'),
3168 ]
3169 argument_group_signatures = [
3170 (Sig('group'), [
3171 Sig('-y', nargs='?', help='y'),
3172 Sig('-z', nargs=3, help='z'),
3173 Sig('d', help='d', nargs='*'),
3174 Sig('e', help='e', nargs='+'),
3175 ])
3176 ]
3177 usage = '''\
3178 usage: PROG [-h] [-w W [W ...]] [-x [X [X ...]]] [-y [Y]] [-z Z Z Z]
3179 a b b [c] [d [d ...]] e [e ...]
3180 '''
3181 help = usage + '''\
3182
3183 positional arguments:
3184 a a
3185 b b
3186 c c
3187
3188 optional arguments:
3189 -h, --help show this help message and exit
3190 -w W [W ...] w
3191 -x [X [X ...]] x
3192
3193 group:
3194 -y [Y] y
3195 -z Z Z Z z
3196 d d
3197 e e
3198 '''
3199 version = ''
3200
3201
3202class TestHelpOnlyUserGroups(HelpTestCase):
3203 """Test basic usage messages"""
3204
3205 parser_signature = Sig(prog='PROG', add_help=False)
3206 argument_signatures = []
3207 argument_group_signatures = [
3208 (Sig('xxxx'), [
3209 Sig('-x', help='x'),
3210 Sig('a', help='a'),
3211 ]),
3212 (Sig('yyyy'), [
3213 Sig('b', help='b'),
3214 Sig('-y', help='y'),
3215 ]),
3216 ]
3217 usage = '''\
3218 usage: PROG [-x X] [-y Y] a b
3219 '''
3220 help = usage + '''\
3221
3222 xxxx:
3223 -x X x
3224 a a
3225
3226 yyyy:
3227 b b
3228 -y Y y
3229 '''
3230 version = ''
3231
3232
3233class TestHelpUsageLongProg(HelpTestCase):
3234 """Test usage messages where the prog is long"""
3235
3236 parser_signature = Sig(prog='P' * 60)
3237 argument_signatures = [
3238 Sig('-w', metavar='W'),
3239 Sig('-x', metavar='X'),
3240 Sig('a'),
3241 Sig('b'),
3242 ]
3243 argument_group_signatures = []
3244 usage = '''\
3245 usage: PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
3246 [-h] [-w W] [-x X] a b
3247 '''
3248 help = usage + '''\
3249
3250 positional arguments:
3251 a
3252 b
3253
3254 optional arguments:
3255 -h, --help show this help message and exit
3256 -w W
3257 -x X
3258 '''
3259 version = ''
3260
3261
3262class TestHelpUsageLongProgOptionsWrap(HelpTestCase):
3263 """Test usage messages where the prog is long and the optionals wrap"""
3264
3265 parser_signature = Sig(prog='P' * 60)
3266 argument_signatures = [
3267 Sig('-w', metavar='W' * 25),
3268 Sig('-x', metavar='X' * 25),
3269 Sig('-y', metavar='Y' * 25),
3270 Sig('-z', metavar='Z' * 25),
3271 Sig('a'),
3272 Sig('b'),
3273 ]
3274 argument_group_signatures = []
3275 usage = '''\
3276 usage: PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
3277 [-h] [-w WWWWWWWWWWWWWWWWWWWWWWWWW] \
3278[-x XXXXXXXXXXXXXXXXXXXXXXXXX]
3279 [-y YYYYYYYYYYYYYYYYYYYYYYYYY] [-z ZZZZZZZZZZZZZZZZZZZZZZZZZ]
3280 a b
3281 '''
3282 help = usage + '''\
3283
3284 positional arguments:
3285 a
3286 b
3287
3288 optional arguments:
3289 -h, --help show this help message and exit
3290 -w WWWWWWWWWWWWWWWWWWWWWWWWW
3291 -x XXXXXXXXXXXXXXXXXXXXXXXXX
3292 -y YYYYYYYYYYYYYYYYYYYYYYYYY
3293 -z ZZZZZZZZZZZZZZZZZZZZZZZZZ
3294 '''
3295 version = ''
3296
3297
3298class TestHelpUsageLongProgPositionalsWrap(HelpTestCase):
3299 """Test usage messages where the prog is long and the positionals wrap"""
3300
3301 parser_signature = Sig(prog='P' * 60, add_help=False)
3302 argument_signatures = [
3303 Sig('a' * 25),
3304 Sig('b' * 25),
3305 Sig('c' * 25),
3306 ]
3307 argument_group_signatures = []
3308 usage = '''\
3309 usage: PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
3310 aaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbb
3311 ccccccccccccccccccccccccc
3312 '''
3313 help = usage + '''\
3314
3315 positional arguments:
3316 aaaaaaaaaaaaaaaaaaaaaaaaa
3317 bbbbbbbbbbbbbbbbbbbbbbbbb
3318 ccccccccccccccccccccccccc
3319 '''
3320 version = ''
3321
3322
3323class TestHelpUsageOptionalsWrap(HelpTestCase):
3324 """Test usage messages where the optionals wrap"""
3325
3326 parser_signature = Sig(prog='PROG')
3327 argument_signatures = [
3328 Sig('-w', metavar='W' * 25),
3329 Sig('-x', metavar='X' * 25),
3330 Sig('-y', metavar='Y' * 25),
3331 Sig('-z', metavar='Z' * 25),
3332 Sig('a'),
3333 Sig('b'),
3334 Sig('c'),
3335 ]
3336 argument_group_signatures = []
3337 usage = '''\
3338 usage: PROG [-h] [-w WWWWWWWWWWWWWWWWWWWWWWWWW] \
3339[-x XXXXXXXXXXXXXXXXXXXXXXXXX]
3340 [-y YYYYYYYYYYYYYYYYYYYYYYYYY] \
3341[-z ZZZZZZZZZZZZZZZZZZZZZZZZZ]
3342 a b c
3343 '''
3344 help = usage + '''\
3345
3346 positional arguments:
3347 a
3348 b
3349 c
3350
3351 optional arguments:
3352 -h, --help show this help message and exit
3353 -w WWWWWWWWWWWWWWWWWWWWWWWWW
3354 -x XXXXXXXXXXXXXXXXXXXXXXXXX
3355 -y YYYYYYYYYYYYYYYYYYYYYYYYY
3356 -z ZZZZZZZZZZZZZZZZZZZZZZZZZ
3357 '''
3358 version = ''
3359
3360
3361class TestHelpUsagePositionalsWrap(HelpTestCase):
3362 """Test usage messages where the positionals wrap"""
3363
3364 parser_signature = Sig(prog='PROG')
3365 argument_signatures = [
3366 Sig('-x'),
3367 Sig('-y'),
3368 Sig('-z'),
3369 Sig('a' * 25),
3370 Sig('b' * 25),
3371 Sig('c' * 25),
3372 ]
3373 argument_group_signatures = []
3374 usage = '''\
3375 usage: PROG [-h] [-x X] [-y Y] [-z Z]
3376 aaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbb
3377 ccccccccccccccccccccccccc
3378 '''
3379 help = usage + '''\
3380
3381 positional arguments:
3382 aaaaaaaaaaaaaaaaaaaaaaaaa
3383 bbbbbbbbbbbbbbbbbbbbbbbbb
3384 ccccccccccccccccccccccccc
3385
3386 optional arguments:
3387 -h, --help show this help message and exit
3388 -x X
3389 -y Y
3390 -z Z
3391 '''
3392 version = ''
3393
3394
3395class TestHelpUsageOptionalsPositionalsWrap(HelpTestCase):
3396 """Test usage messages where the optionals and positionals wrap"""
3397
3398 parser_signature = Sig(prog='PROG')
3399 argument_signatures = [
3400 Sig('-x', metavar='X' * 25),
3401 Sig('-y', metavar='Y' * 25),
3402 Sig('-z', metavar='Z' * 25),
3403 Sig('a' * 25),
3404 Sig('b' * 25),
3405 Sig('c' * 25),
3406 ]
3407 argument_group_signatures = []
3408 usage = '''\
3409 usage: PROG [-h] [-x XXXXXXXXXXXXXXXXXXXXXXXXX] \
3410[-y YYYYYYYYYYYYYYYYYYYYYYYYY]
3411 [-z ZZZZZZZZZZZZZZZZZZZZZZZZZ]
3412 aaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbb
3413 ccccccccccccccccccccccccc
3414 '''
3415 help = usage + '''\
3416
3417 positional arguments:
3418 aaaaaaaaaaaaaaaaaaaaaaaaa
3419 bbbbbbbbbbbbbbbbbbbbbbbbb
3420 ccccccccccccccccccccccccc
3421
3422 optional arguments:
3423 -h, --help show this help message and exit
3424 -x XXXXXXXXXXXXXXXXXXXXXXXXX
3425 -y YYYYYYYYYYYYYYYYYYYYYYYYY
3426 -z ZZZZZZZZZZZZZZZZZZZZZZZZZ
3427 '''
3428 version = ''
3429
3430
3431class TestHelpUsageOptionalsOnlyWrap(HelpTestCase):
3432 """Test usage messages where there are only optionals and they wrap"""
3433
3434 parser_signature = Sig(prog='PROG')
3435 argument_signatures = [
3436 Sig('-x', metavar='X' * 25),
3437 Sig('-y', metavar='Y' * 25),
3438 Sig('-z', metavar='Z' * 25),
3439 ]
3440 argument_group_signatures = []
3441 usage = '''\
3442 usage: PROG [-h] [-x XXXXXXXXXXXXXXXXXXXXXXXXX] \
3443[-y YYYYYYYYYYYYYYYYYYYYYYYYY]
3444 [-z ZZZZZZZZZZZZZZZZZZZZZZZZZ]
3445 '''
3446 help = usage + '''\
3447
3448 optional arguments:
3449 -h, --help show this help message and exit
3450 -x XXXXXXXXXXXXXXXXXXXXXXXXX
3451 -y YYYYYYYYYYYYYYYYYYYYYYYYY
3452 -z ZZZZZZZZZZZZZZZZZZZZZZZZZ
3453 '''
3454 version = ''
3455
3456
3457class TestHelpUsagePositionalsOnlyWrap(HelpTestCase):
3458 """Test usage messages where there are only positionals and they wrap"""
3459
3460 parser_signature = Sig(prog='PROG', add_help=False)
3461 argument_signatures = [
3462 Sig('a' * 25),
3463 Sig('b' * 25),
3464 Sig('c' * 25),
3465 ]
3466 argument_group_signatures = []
3467 usage = '''\
3468 usage: PROG aaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbb
3469 ccccccccccccccccccccccccc
3470 '''
3471 help = usage + '''\
3472
3473 positional arguments:
3474 aaaaaaaaaaaaaaaaaaaaaaaaa
3475 bbbbbbbbbbbbbbbbbbbbbbbbb
3476 ccccccccccccccccccccccccc
3477 '''
3478 version = ''
3479
3480
3481class TestHelpVariableExpansion(HelpTestCase):
3482 """Test that variables are expanded properly in help messages"""
3483
3484 parser_signature = Sig(prog='PROG')
3485 argument_signatures = [
3486 Sig('-x', type=int,
3487 help='x %(prog)s %(default)s %(type)s %%'),
3488 Sig('-y', action='store_const', default=42, const='XXX',
3489 help='y %(prog)s %(default)s %(const)s'),
3490 Sig('--foo', choices='abc',
3491 help='foo %(prog)s %(default)s %(choices)s'),
3492 Sig('--bar', default='baz', choices=[1, 2], metavar='BBB',
3493 help='bar %(prog)s %(default)s %(dest)s'),
3494 Sig('spam', help='spam %(prog)s %(default)s'),
3495 Sig('badger', default=0.5, help='badger %(prog)s %(default)s'),
3496 ]
3497 argument_group_signatures = [
3498 (Sig('group'), [
3499 Sig('-a', help='a %(prog)s %(default)s'),
3500 Sig('-b', default=-1, help='b %(prog)s %(default)s'),
3501 ])
3502 ]
3503 usage = ('''\
3504 usage: PROG [-h] [-x X] [-y] [--foo {a,b,c}] [--bar BBB] [-a A] [-b B]
3505 spam badger
3506 ''')
3507 help = usage + '''\
3508
3509 positional arguments:
3510 spam spam PROG None
3511 badger badger PROG 0.5
3512
3513 optional arguments:
3514 -h, --help show this help message and exit
3515 -x X x PROG None int %
3516 -y y PROG 42 XXX
3517 --foo {a,b,c} foo PROG None a, b, c
3518 --bar BBB bar PROG baz bar
3519
3520 group:
3521 -a A a PROG None
3522 -b B b PROG -1
3523 '''
3524 version = ''
3525
3526
3527class TestHelpVariableExpansionUsageSupplied(HelpTestCase):
3528 """Test that variables are expanded properly when usage= is present"""
3529
3530 parser_signature = Sig(prog='PROG', usage='%(prog)s FOO')
3531 argument_signatures = []
3532 argument_group_signatures = []
3533 usage = ('''\
3534 usage: PROG FOO
3535 ''')
3536 help = usage + '''\
3537
3538 optional arguments:
3539 -h, --help show this help message and exit
3540 '''
3541 version = ''
3542
3543
3544class TestHelpVariableExpansionNoArguments(HelpTestCase):
3545 """Test that variables are expanded properly with no arguments"""
3546
3547 parser_signature = Sig(prog='PROG', add_help=False)
3548 argument_signatures = []
3549 argument_group_signatures = []
3550 usage = ('''\
3551 usage: PROG
3552 ''')
3553 help = usage
3554 version = ''
3555
3556
3557class TestHelpSuppressUsage(HelpTestCase):
3558 """Test that items can be suppressed in usage messages"""
3559
3560 parser_signature = Sig(prog='PROG', usage=argparse.SUPPRESS)
3561 argument_signatures = [
3562 Sig('--foo', help='foo help'),
3563 Sig('spam', help='spam help'),
3564 ]
3565 argument_group_signatures = []
3566 help = '''\
3567 positional arguments:
3568 spam spam help
3569
3570 optional arguments:
3571 -h, --help show this help message and exit
3572 --foo FOO foo help
3573 '''
3574 usage = ''
3575 version = ''
3576
3577
3578class TestHelpSuppressOptional(HelpTestCase):
3579 """Test that optional arguments can be suppressed in help messages"""
3580
3581 parser_signature = Sig(prog='PROG', add_help=False)
3582 argument_signatures = [
3583 Sig('--foo', help=argparse.SUPPRESS),
3584 Sig('spam', help='spam help'),
3585 ]
3586 argument_group_signatures = []
3587 usage = '''\
3588 usage: PROG spam
3589 '''
3590 help = usage + '''\
3591
3592 positional arguments:
3593 spam spam help
3594 '''
3595 version = ''
3596
3597
3598class TestHelpSuppressOptionalGroup(HelpTestCase):
3599 """Test that optional groups can be suppressed in help messages"""
3600
3601 parser_signature = Sig(prog='PROG')
3602 argument_signatures = [
3603 Sig('--foo', help='foo help'),
3604 Sig('spam', help='spam help'),
3605 ]
3606 argument_group_signatures = [
3607 (Sig('group'), [Sig('--bar', help=argparse.SUPPRESS)]),
3608 ]
3609 usage = '''\
3610 usage: PROG [-h] [--foo FOO] spam
3611 '''
3612 help = usage + '''\
3613
3614 positional arguments:
3615 spam spam help
3616
3617 optional arguments:
3618 -h, --help show this help message and exit
3619 --foo FOO foo help
3620 '''
3621 version = ''
3622
3623
3624class TestHelpSuppressPositional(HelpTestCase):
3625 """Test that positional arguments can be suppressed in help messages"""
3626
3627 parser_signature = Sig(prog='PROG')
3628 argument_signatures = [
3629 Sig('--foo', help='foo help'),
3630 Sig('spam', help=argparse.SUPPRESS),
3631 ]
3632 argument_group_signatures = []
3633 usage = '''\
3634 usage: PROG [-h] [--foo FOO]
3635 '''
3636 help = usage + '''\
3637
3638 optional arguments:
3639 -h, --help show this help message and exit
3640 --foo FOO foo help
3641 '''
3642 version = ''
3643
3644
3645class TestHelpRequiredOptional(HelpTestCase):
3646 """Test that required options don't look optional"""
3647
3648 parser_signature = Sig(prog='PROG')
3649 argument_signatures = [
3650 Sig('--foo', required=True, help='foo help'),
3651 ]
3652 argument_group_signatures = []
3653 usage = '''\
3654 usage: PROG [-h] --foo FOO
3655 '''
3656 help = usage + '''\
3657
3658 optional arguments:
3659 -h, --help show this help message and exit
3660 --foo FOO foo help
3661 '''
3662 version = ''
3663
3664
3665class TestHelpAlternatePrefixChars(HelpTestCase):
3666 """Test that options display with different prefix characters"""
3667
3668 parser_signature = Sig(prog='PROG', prefix_chars='^;', add_help=False)
3669 argument_signatures = [
3670 Sig('^^foo', action='store_true', help='foo help'),
3671 Sig(';b', ';;bar', help='bar help'),
3672 ]
3673 argument_group_signatures = []
3674 usage = '''\
3675 usage: PROG [^^foo] [;b BAR]
3676 '''
3677 help = usage + '''\
3678
3679 optional arguments:
3680 ^^foo foo help
3681 ;b BAR, ;;bar BAR bar help
3682 '''
3683 version = ''
3684
3685
3686class TestHelpNoHelpOptional(HelpTestCase):
3687 """Test that the --help argument can be suppressed help messages"""
3688
3689 parser_signature = Sig(prog='PROG', add_help=False)
3690 argument_signatures = [
3691 Sig('--foo', help='foo help'),
3692 Sig('spam', help='spam help'),
3693 ]
3694 argument_group_signatures = []
3695 usage = '''\
3696 usage: PROG [--foo FOO] spam
3697 '''
3698 help = usage + '''\
3699
3700 positional arguments:
3701 spam spam help
3702
3703 optional arguments:
3704 --foo FOO foo help
3705 '''
3706 version = ''
3707
3708
3709class TestHelpVersionOptional(HelpTestCase):
3710 """Test that the --version argument can be suppressed help messages"""
3711
3712 parser_signature = Sig(prog='PROG', version='1.0')
3713 argument_signatures = [
3714 Sig('--foo', help='foo help'),
3715 Sig('spam', help='spam help'),
3716 ]
3717 argument_group_signatures = []
3718 usage = '''\
3719 usage: PROG [-h] [-v] [--foo FOO] spam
3720 '''
3721 help = usage + '''\
3722
3723 positional arguments:
3724 spam spam help
3725
3726 optional arguments:
3727 -h, --help show this help message and exit
3728 -v, --version show program's version number and exit
3729 --foo FOO foo help
3730 '''
3731 version = '''\
3732 1.0
3733 '''
3734
3735
3736class TestHelpNone(HelpTestCase):
3737 """Test that no errors occur if no help is specified"""
3738
3739 parser_signature = Sig(prog='PROG')
3740 argument_signatures = [
3741 Sig('--foo'),
3742 Sig('spam'),
3743 ]
3744 argument_group_signatures = []
3745 usage = '''\
3746 usage: PROG [-h] [--foo FOO] spam
3747 '''
3748 help = usage + '''\
3749
3750 positional arguments:
3751 spam
3752
3753 optional arguments:
3754 -h, --help show this help message and exit
3755 --foo FOO
3756 '''
3757 version = ''
3758
3759
3760class TestHelpTupleMetavar(HelpTestCase):
3761 """Test specifying metavar as a tuple"""
3762
3763 parser_signature = Sig(prog='PROG')
3764 argument_signatures = [
3765 Sig('-w', help='w', nargs='+', metavar=('W1', 'W2')),
3766 Sig('-x', help='x', nargs='*', metavar=('X1', 'X2')),
3767 Sig('-y', help='y', nargs=3, metavar=('Y1', 'Y2', 'Y3')),
3768 Sig('-z', help='z', nargs='?', metavar=('Z1', )),
3769 ]
3770 argument_group_signatures = []
3771 usage = '''\
3772 usage: PROG [-h] [-w W1 [W2 ...]] [-x [X1 [X2 ...]]] [-y Y1 Y2 Y3] \
3773[-z [Z1]]
3774 '''
3775 help = usage + '''\
3776
3777 optional arguments:
3778 -h, --help show this help message and exit
3779 -w W1 [W2 ...] w
3780 -x [X1 [X2 ...]] x
3781 -y Y1 Y2 Y3 y
3782 -z [Z1] z
3783 '''
3784 version = ''
3785
3786
3787class TestHelpRawText(HelpTestCase):
3788 """Test the RawTextHelpFormatter"""
3789
3790 parser_signature = Sig(
3791 prog='PROG', formatter_class=argparse.RawTextHelpFormatter,
3792 description='Keep the formatting\n'
3793 ' exactly as it is written\n'
3794 '\n'
3795 'here\n')
3796
3797 argument_signatures = [
3798 Sig('--foo', help=' foo help should also\n'
3799 'appear as given here'),
3800 Sig('spam', help='spam help'),
3801 ]
3802 argument_group_signatures = [
3803 (Sig('title', description=' This text\n'
3804 ' should be indented\n'
3805 ' exactly like it is here\n'),
3806 [Sig('--bar', help='bar help')]),
3807 ]
3808 usage = '''\
3809 usage: PROG [-h] [--foo FOO] [--bar BAR] spam
3810 '''
3811 help = usage + '''\
3812
3813 Keep the formatting
3814 exactly as it is written
3815
3816 here
3817
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 should also
3824 appear as given here
3825
3826 title:
3827 This text
3828 should be indented
3829 exactly like it is here
3830
3831 --bar BAR bar help
3832 '''
3833 version = ''
3834
3835
3836class TestHelpRawDescription(HelpTestCase):
3837 """Test the RawTextHelpFormatter"""
3838
3839 parser_signature = Sig(
3840 prog='PROG', formatter_class=argparse.RawDescriptionHelpFormatter,
3841 description='Keep the formatting\n'
3842 ' exactly as it is written\n'
3843 '\n'
3844 'here\n')
3845
3846 argument_signatures = [
3847 Sig('--foo', help=' foo help should not\n'
3848 ' retain this odd formatting'),
3849 Sig('spam', help='spam help'),
3850 ]
3851 argument_group_signatures = [
3852 (Sig('title', description=' This text\n'
3853 ' should be indented\n'
3854 ' exactly like it is here\n'),
3855 [Sig('--bar', help='bar help')]),
3856 ]
3857 usage = '''\
3858 usage: PROG [-h] [--foo FOO] [--bar BAR] spam
3859 '''
3860 help = usage + '''\
3861
3862 Keep the formatting
3863 exactly as it is written
3864
3865 here
3866
3867 positional arguments:
3868 spam spam help
3869
3870 optional arguments:
3871 -h, --help show this help message and exit
3872 --foo FOO foo help should not retain this odd formatting
3873
3874 title:
3875 This text
3876 should be indented
3877 exactly like it is here
3878
3879 --bar BAR bar help
3880 '''
3881 version = ''
3882
3883
3884class TestHelpArgumentDefaults(HelpTestCase):
3885 """Test the ArgumentDefaultsHelpFormatter"""
3886
3887 parser_signature = Sig(
3888 prog='PROG', formatter_class=argparse.ArgumentDefaultsHelpFormatter,
3889 description='description')
3890
3891 argument_signatures = [
3892 Sig('--foo', help='foo help - oh and by the way, %(default)s'),
3893 Sig('--bar', action='store_true', help='bar help'),
3894 Sig('spam', help='spam help'),
3895 Sig('badger', nargs='?', default='wooden', help='badger help'),
3896 ]
3897 argument_group_signatures = [
3898 (Sig('title', description='description'),
3899 [Sig('--baz', type=int, default=42, help='baz help')]),
3900 ]
3901 usage = '''\
3902 usage: PROG [-h] [--foo FOO] [--bar] [--baz BAZ] spam [badger]
3903 '''
3904 help = usage + '''\
3905
3906 description
3907
3908 positional arguments:
3909 spam spam help
3910 badger badger help (default: wooden)
3911
3912 optional arguments:
3913 -h, --help show this help message and exit
3914 --foo FOO foo help - oh and by the way, None
3915 --bar bar help (default: False)
3916
3917 title:
3918 description
3919
3920 --baz BAZ baz help (default: 42)
3921 '''
3922 version = ''
3923
Steven Bethard50fe5932010-05-24 03:47:38 +00003924class TestHelpVersionAction(HelpTestCase):
3925 """Test the default help for the version action"""
3926
3927 parser_signature = Sig(prog='PROG', description='description')
3928 argument_signatures = [Sig('-V', '--version', action='version', version='3.6')]
3929 argument_group_signatures = []
3930 usage = '''\
3931 usage: PROG [-h] [-V]
3932 '''
3933 help = usage + '''\
3934
3935 description
3936
3937 optional arguments:
3938 -h, --help show this help message and exit
3939 -V, --version show program's version number and exit
3940 '''
3941 version = ''
3942
Benjamin Peterson698a18a2010-03-02 22:34:37 +00003943# =====================================
3944# Optional/Positional constructor tests
3945# =====================================
3946
3947class TestInvalidArgumentConstructors(TestCase):
3948 """Test a bunch of invalid Argument constructors"""
3949
3950 def assertTypeError(self, *args, **kwargs):
3951 parser = argparse.ArgumentParser()
3952 self.assertRaises(TypeError, parser.add_argument,
3953 *args, **kwargs)
3954
3955 def assertValueError(self, *args, **kwargs):
3956 parser = argparse.ArgumentParser()
3957 self.assertRaises(ValueError, parser.add_argument,
3958 *args, **kwargs)
3959
3960 def test_invalid_keyword_arguments(self):
3961 self.assertTypeError('-x', bar=None)
3962 self.assertTypeError('-y', callback='foo')
3963 self.assertTypeError('-y', callback_args=())
3964 self.assertTypeError('-y', callback_kwargs={})
3965
3966 def test_missing_destination(self):
3967 self.assertTypeError()
3968 for action in ['append', 'store']:
3969 self.assertTypeError(action=action)
3970
3971 def test_invalid_option_strings(self):
3972 self.assertValueError('--')
3973 self.assertValueError('---')
3974
3975 def test_invalid_type(self):
3976 self.assertValueError('--foo', type='int')
3977
3978 def test_invalid_action(self):
3979 self.assertValueError('-x', action='foo')
3980 self.assertValueError('foo', action='baz')
3981 parser = argparse.ArgumentParser()
3982 try:
3983 parser.add_argument("--foo", action="store-true")
3984 except ValueError:
3985 e = sys.exc_info()[1]
3986 expected = 'unknown action'
3987 msg = 'expected %r, found %r' % (expected, e)
3988 self.assertTrue(expected in str(e), msg)
3989
3990 def test_multiple_dest(self):
3991 parser = argparse.ArgumentParser()
3992 parser.add_argument(dest='foo')
3993 try:
3994 parser.add_argument('bar', dest='baz')
3995 except ValueError:
3996 e = sys.exc_info()[1]
3997 expected = 'dest supplied twice for positional argument'
3998 msg = 'expected %r, found %r' % (expected, e)
3999 self.assertTrue(expected in str(e), msg)
4000
4001 def test_no_argument_actions(self):
4002 for action in ['store_const', 'store_true', 'store_false',
4003 'append_const', 'count']:
4004 for attrs in [dict(type=int), dict(nargs='+'),
4005 dict(choices='ab')]:
4006 self.assertTypeError('-x', action=action, **attrs)
4007
4008 def test_no_argument_no_const_actions(self):
4009 # options with zero arguments
4010 for action in ['store_true', 'store_false', 'count']:
4011
4012 # const is always disallowed
4013 self.assertTypeError('-x', const='foo', action=action)
4014
4015 # nargs is always disallowed
4016 self.assertTypeError('-x', nargs='*', action=action)
4017
4018 def test_more_than_one_argument_actions(self):
4019 for action in ['store', 'append']:
4020
4021 # nargs=0 is disallowed
4022 self.assertValueError('-x', nargs=0, action=action)
4023 self.assertValueError('spam', nargs=0, action=action)
4024
4025 # const is disallowed with non-optional arguments
4026 for nargs in [1, '*', '+']:
4027 self.assertValueError('-x', const='foo',
4028 nargs=nargs, action=action)
4029 self.assertValueError('spam', const='foo',
4030 nargs=nargs, action=action)
4031
4032 def test_required_const_actions(self):
4033 for action in ['store_const', 'append_const']:
4034
4035 # nargs is always disallowed
4036 self.assertTypeError('-x', nargs='+', action=action)
4037
4038 def test_parsers_action_missing_params(self):
4039 self.assertTypeError('command', action='parsers')
4040 self.assertTypeError('command', action='parsers', prog='PROG')
4041 self.assertTypeError('command', action='parsers',
4042 parser_class=argparse.ArgumentParser)
4043
4044 def test_required_positional(self):
4045 self.assertTypeError('foo', required=True)
4046
4047 def test_user_defined_action(self):
4048
4049 class Success(Exception):
4050 pass
4051
4052 class Action(object):
4053
4054 def __init__(self,
4055 option_strings,
4056 dest,
4057 const,
4058 default,
4059 required=False):
4060 if dest == 'spam':
4061 if const is Success:
4062 if default is Success:
4063 raise Success()
4064
4065 def __call__(self, *args, **kwargs):
4066 pass
4067
4068 parser = argparse.ArgumentParser()
4069 self.assertRaises(Success, parser.add_argument, '--spam',
4070 action=Action, default=Success, const=Success)
4071 self.assertRaises(Success, parser.add_argument, 'spam',
4072 action=Action, default=Success, const=Success)
4073
4074# ================================
4075# Actions returned by add_argument
4076# ================================
4077
4078class TestActionsReturned(TestCase):
4079
4080 def test_dest(self):
4081 parser = argparse.ArgumentParser()
4082 action = parser.add_argument('--foo')
4083 self.assertEqual(action.dest, 'foo')
4084 action = parser.add_argument('-b', '--bar')
4085 self.assertEqual(action.dest, 'bar')
4086 action = parser.add_argument('-x', '-y')
4087 self.assertEqual(action.dest, 'x')
4088
4089 def test_misc(self):
4090 parser = argparse.ArgumentParser()
4091 action = parser.add_argument('--foo', nargs='?', const=42,
4092 default=84, type=int, choices=[1, 2],
4093 help='FOO', metavar='BAR', dest='baz')
4094 self.assertEqual(action.nargs, '?')
4095 self.assertEqual(action.const, 42)
4096 self.assertEqual(action.default, 84)
4097 self.assertEqual(action.type, int)
4098 self.assertEqual(action.choices, [1, 2])
4099 self.assertEqual(action.help, 'FOO')
4100 self.assertEqual(action.metavar, 'BAR')
4101 self.assertEqual(action.dest, 'baz')
4102
4103
4104# ================================
4105# Argument conflict handling tests
4106# ================================
4107
4108class TestConflictHandling(TestCase):
4109
4110 def test_bad_type(self):
4111 self.assertRaises(ValueError, argparse.ArgumentParser,
4112 conflict_handler='foo')
4113
4114 def test_conflict_error(self):
4115 parser = argparse.ArgumentParser()
4116 parser.add_argument('-x')
4117 self.assertRaises(argparse.ArgumentError,
4118 parser.add_argument, '-x')
4119 parser.add_argument('--spam')
4120 self.assertRaises(argparse.ArgumentError,
4121 parser.add_argument, '--spam')
4122
4123 def test_resolve_error(self):
4124 get_parser = argparse.ArgumentParser
4125 parser = get_parser(prog='PROG', conflict_handler='resolve')
4126
4127 parser.add_argument('-x', help='OLD X')
4128 parser.add_argument('-x', help='NEW X')
4129 self.assertEqual(parser.format_help(), textwrap.dedent('''\
4130 usage: PROG [-h] [-x X]
4131
4132 optional arguments:
4133 -h, --help show this help message and exit
4134 -x X NEW X
4135 '''))
4136
4137 parser.add_argument('--spam', metavar='OLD_SPAM')
4138 parser.add_argument('--spam', metavar='NEW_SPAM')
4139 self.assertEqual(parser.format_help(), textwrap.dedent('''\
4140 usage: PROG [-h] [-x X] [--spam NEW_SPAM]
4141
4142 optional arguments:
4143 -h, --help show this help message and exit
4144 -x X NEW X
4145 --spam NEW_SPAM
4146 '''))
4147
4148
4149# =============================
4150# Help and Version option tests
4151# =============================
4152
4153class TestOptionalsHelpVersionActions(TestCase):
4154 """Test the help and version actions"""
4155
4156 def _get_error(self, func, *args, **kwargs):
4157 try:
4158 func(*args, **kwargs)
4159 except ArgumentParserError:
4160 return sys.exc_info()[1]
4161 else:
4162 self.assertRaises(ArgumentParserError, func, *args, **kwargs)
4163
4164 def assertPrintHelpExit(self, parser, args_str):
4165 self.assertEqual(
4166 parser.format_help(),
4167 self._get_error(parser.parse_args, args_str.split()).stdout)
4168
4169 def assertPrintVersionExit(self, parser, args_str):
4170 self.assertEqual(
4171 parser.format_version(),
4172 self._get_error(parser.parse_args, args_str.split()).stderr)
4173
4174 def assertArgumentParserError(self, parser, *args):
4175 self.assertRaises(ArgumentParserError, parser.parse_args, args)
4176
4177 def test_version(self):
4178 parser = ErrorRaisingArgumentParser(version='1.0')
4179 self.assertPrintHelpExit(parser, '-h')
4180 self.assertPrintHelpExit(parser, '--help')
4181 self.assertPrintVersionExit(parser, '-v')
4182 self.assertPrintVersionExit(parser, '--version')
4183
4184 def test_version_format(self):
4185 parser = ErrorRaisingArgumentParser(prog='PPP', version='%(prog)s 3.5')
4186 msg = self._get_error(parser.parse_args, ['-v']).stderr
4187 self.assertEqual('PPP 3.5\n', msg)
4188
4189 def test_version_no_help(self):
4190 parser = ErrorRaisingArgumentParser(add_help=False, version='1.0')
4191 self.assertArgumentParserError(parser, '-h')
4192 self.assertArgumentParserError(parser, '--help')
4193 self.assertPrintVersionExit(parser, '-v')
4194 self.assertPrintVersionExit(parser, '--version')
4195
4196 def test_version_action(self):
4197 parser = ErrorRaisingArgumentParser(prog='XXX')
4198 parser.add_argument('-V', action='version', version='%(prog)s 3.7')
4199 msg = self._get_error(parser.parse_args, ['-V']).stderr
4200 self.assertEqual('XXX 3.7\n', msg)
4201
4202 def test_no_help(self):
4203 parser = ErrorRaisingArgumentParser(add_help=False)
4204 self.assertArgumentParserError(parser, '-h')
4205 self.assertArgumentParserError(parser, '--help')
4206 self.assertArgumentParserError(parser, '-v')
4207 self.assertArgumentParserError(parser, '--version')
4208
4209 def test_alternate_help_version(self):
4210 parser = ErrorRaisingArgumentParser()
4211 parser.add_argument('-x', action='help')
4212 parser.add_argument('-y', action='version')
4213 self.assertPrintHelpExit(parser, '-x')
4214 self.assertPrintVersionExit(parser, '-y')
4215 self.assertArgumentParserError(parser, '-v')
4216 self.assertArgumentParserError(parser, '--version')
4217
4218 def test_help_version_extra_arguments(self):
4219 parser = ErrorRaisingArgumentParser(version='1.0')
4220 parser.add_argument('-x', action='store_true')
4221 parser.add_argument('y')
4222
4223 # try all combinations of valid prefixes and suffixes
4224 valid_prefixes = ['', '-x', 'foo', '-x bar', 'baz -x']
4225 valid_suffixes = valid_prefixes + ['--bad-option', 'foo bar baz']
4226 for prefix in valid_prefixes:
4227 for suffix in valid_suffixes:
4228 format = '%s %%s %s' % (prefix, suffix)
4229 self.assertPrintHelpExit(parser, format % '-h')
4230 self.assertPrintHelpExit(parser, format % '--help')
4231 self.assertPrintVersionExit(parser, format % '-v')
4232 self.assertPrintVersionExit(parser, format % '--version')
4233
4234
4235# ======================
4236# str() and repr() tests
4237# ======================
4238
4239class TestStrings(TestCase):
4240 """Test str() and repr() on Optionals and Positionals"""
4241
4242 def assertStringEqual(self, obj, result_string):
4243 for func in [str, repr]:
4244 self.assertEqual(func(obj), result_string)
4245
4246 def test_optional(self):
4247 option = argparse.Action(
4248 option_strings=['--foo', '-a', '-b'],
4249 dest='b',
4250 type='int',
4251 nargs='+',
4252 default=42,
4253 choices=[1, 2, 3],
4254 help='HELP',
4255 metavar='METAVAR')
4256 string = (
4257 "Action(option_strings=['--foo', '-a', '-b'], dest='b', "
4258 "nargs='+', const=None, default=42, type='int', "
4259 "choices=[1, 2, 3], help='HELP', metavar='METAVAR')")
4260 self.assertStringEqual(option, string)
4261
4262 def test_argument(self):
4263 argument = argparse.Action(
4264 option_strings=[],
4265 dest='x',
4266 type=float,
4267 nargs='?',
4268 default=2.5,
4269 choices=[0.5, 1.5, 2.5],
4270 help='H HH H',
4271 metavar='MV MV MV')
4272 string = (
4273 "Action(option_strings=[], dest='x', nargs='?', "
4274 "const=None, default=2.5, type=%r, choices=[0.5, 1.5, 2.5], "
4275 "help='H HH H', metavar='MV MV MV')" % float)
4276 self.assertStringEqual(argument, string)
4277
4278 def test_namespace(self):
4279 ns = argparse.Namespace(foo=42, bar='spam')
4280 string = "Namespace(bar='spam', foo=42)"
4281 self.assertStringEqual(ns, string)
4282
4283 def test_parser(self):
4284 parser = argparse.ArgumentParser(prog='PROG')
4285 string = (
4286 "ArgumentParser(prog='PROG', usage=None, description=None, "
4287 "version=None, formatter_class=%r, conflict_handler='error', "
4288 "add_help=True)" % argparse.HelpFormatter)
4289 self.assertStringEqual(parser, string)
4290
4291# ===============
4292# Namespace tests
4293# ===============
4294
4295class TestNamespace(TestCase):
4296
4297 def test_constructor(self):
4298 ns = argparse.Namespace()
4299 self.assertRaises(AttributeError, getattr, ns, 'x')
4300
4301 ns = argparse.Namespace(a=42, b='spam')
4302 self.assertEqual(ns.a, 42)
4303 self.assertEqual(ns.b, 'spam')
4304
4305 def test_equality(self):
4306 ns1 = argparse.Namespace(a=1, b=2)
4307 ns2 = argparse.Namespace(b=2, a=1)
4308 ns3 = argparse.Namespace(a=1)
4309 ns4 = argparse.Namespace(b=2)
4310
4311 self.assertEqual(ns1, ns2)
4312 self.assertNotEqual(ns1, ns3)
4313 self.assertNotEqual(ns1, ns4)
4314 self.assertNotEqual(ns2, ns3)
4315 self.assertNotEqual(ns2, ns4)
4316 self.assertTrue(ns1 != ns3)
4317 self.assertTrue(ns1 != ns4)
4318 self.assertTrue(ns2 != ns3)
4319 self.assertTrue(ns2 != ns4)
4320
4321
4322# ===================
4323# File encoding tests
4324# ===================
4325
4326class TestEncoding(TestCase):
4327
4328 def _test_module_encoding(self, path):
4329 path, _ = os.path.splitext(path)
4330 path += ".py"
Antoine Pitroub86680e2010-10-14 21:15:17 +00004331 with codecs.open(path, 'r', 'utf8') as f:
4332 f.read()
Benjamin Peterson698a18a2010-03-02 22:34:37 +00004333
4334 def test_argparse_module_encoding(self):
4335 self._test_module_encoding(argparse.__file__)
4336
4337 def test_test_argparse_module_encoding(self):
4338 self._test_module_encoding(__file__)
4339
4340# ===================
4341# ArgumentError tests
4342# ===================
4343
4344class TestArgumentError(TestCase):
4345
4346 def test_argument_error(self):
4347 msg = "my error here"
4348 error = argparse.ArgumentError(None, msg)
4349 self.assertEqual(str(error), msg)
4350
4351# =======================
4352# ArgumentTypeError tests
4353# =======================
4354
R. David Murray722b5fd2010-11-20 03:48:58 +00004355class TestArgumentTypeError(TestCase):
Benjamin Peterson698a18a2010-03-02 22:34:37 +00004356
4357 def test_argument_type_error(self):
4358
4359 def spam(string):
4360 raise argparse.ArgumentTypeError('spam!')
4361
4362 parser = ErrorRaisingArgumentParser(prog='PROG', add_help=False)
4363 parser.add_argument('x', type=spam)
4364 try:
4365 parser.parse_args(['XXX'])
4366 except ArgumentParserError:
4367 expected = 'usage: PROG x\nPROG: error: argument x: spam!\n'
4368 msg = sys.exc_info()[1].stderr
4369 self.assertEqual(expected, msg)
4370 else:
4371 self.fail()
4372
4373# ======================
4374# parse_known_args tests
4375# ======================
4376
4377class TestParseKnownArgs(TestCase):
4378
4379 def test_optionals(self):
4380 parser = argparse.ArgumentParser()
4381 parser.add_argument('--foo')
4382 args, extras = parser.parse_known_args('--foo F --bar --baz'.split())
4383 self.assertEqual(NS(foo='F'), args)
4384 self.assertEqual(['--bar', '--baz'], extras)
4385
4386 def test_mixed(self):
4387 parser = argparse.ArgumentParser()
4388 parser.add_argument('-v', nargs='?', const=1, type=int)
4389 parser.add_argument('--spam', action='store_false')
4390 parser.add_argument('badger')
4391
4392 argv = ["B", "C", "--foo", "-v", "3", "4"]
4393 args, extras = parser.parse_known_args(argv)
4394 self.assertEqual(NS(v=3, spam=True, badger="B"), args)
4395 self.assertEqual(["C", "--foo", "4"], extras)
4396
4397# ============================
4398# from argparse import * tests
4399# ============================
4400
4401class TestImportStar(TestCase):
4402
4403 def test(self):
4404 for name in argparse.__all__:
4405 self.assertTrue(hasattr(argparse, name))
4406
Steven Bethard72c55382010-11-01 15:23:12 +00004407 def test_all_exports_everything_but_modules(self):
4408 items = [
4409 name
4410 for name, value in vars(argparse).items()
Éric Araujo12159152010-12-04 17:31:49 +00004411 if not (name.startswith("_") or name == 'ngettext')
Steven Bethard72c55382010-11-01 15:23:12 +00004412 if not inspect.ismodule(value)
4413 ]
4414 self.assertEqual(sorted(items), sorted(argparse.__all__))
4415
Benjamin Peterson698a18a2010-03-02 22:34:37 +00004416def test_main():
Florent Xicluna41fe6152010-04-02 18:52:12 +00004417 # silence warnings about version argument - these are expected
4418 with support.check_warnings(
4419 ('The "version" argument to ArgumentParser is deprecated.',
4420 DeprecationWarning),
4421 ('The (format|print)_version method is deprecated',
4422 DeprecationWarning)):
Benjamin Peterson698a18a2010-03-02 22:34:37 +00004423 support.run_unittest(__name__)
Benjamin Peterson4fd181c2010-03-02 23:46:42 +00004424 # Remove global references to avoid looking like we have refleaks.
4425 RFile.seen = {}
4426 WFile.seen = set()
4427
Benjamin Peterson698a18a2010-03-02 22:34:37 +00004428
4429
4430if __name__ == '__main__':
4431 test_main()