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