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