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