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