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