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