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