1# Author: Steven J. Bethard <steven.bethard@gmail.com>. 2 3import inspect 4import os 5import shutil 6import stat 7import sys 8import textwrap 9import tempfile 10import unittest 11import argparse 12 13from io import StringIO 14 15from test import support 16from unittest import mock 17class StdIOBuffer(StringIO): 18 pass 19 20class TestCase(unittest.TestCase): 21 22 def setUp(self): 23 # The tests assume that line wrapping occurs at 80 columns, but this 24 # behaviour can be overridden by setting the COLUMNS environment 25 # variable. To ensure that this width is used, set COLUMNS to 80. 26 env = support.EnvironmentVarGuard() 27 env['COLUMNS'] = '80' 28 self.addCleanup(env.__exit__) 29 30 31class TempDirMixin(object): 32 33 def setUp(self): 34 self.temp_dir = tempfile.mkdtemp() 35 self.old_dir = os.getcwd() 36 os.chdir(self.temp_dir) 37 38 def tearDown(self): 39 os.chdir(self.old_dir) 40 for root, dirs, files in os.walk(self.temp_dir, topdown=False): 41 for name in files: 42 os.chmod(os.path.join(self.temp_dir, name), stat.S_IWRITE) 43 shutil.rmtree(self.temp_dir, True) 44 45 def create_readonly_file(self, filename): 46 file_path = os.path.join(self.temp_dir, filename) 47 with open(file_path, 'w') as file: 48 file.write(filename) 49 os.chmod(file_path, stat.S_IREAD) 50 51class Sig(object): 52 53 def __init__(self, *args, **kwargs): 54 self.args = args 55 self.kwargs = kwargs 56 57 58class NS(object): 59 60 def __init__(self, **kwargs): 61 self.__dict__.update(kwargs) 62 63 def __repr__(self): 64 sorted_items = sorted(self.__dict__.items()) 65 kwarg_str = ', '.join(['%s=%r' % tup for tup in sorted_items]) 66 return '%s(%s)' % (type(self).__name__, kwarg_str) 67 68 def __eq__(self, other): 69 return vars(self) == vars(other) 70 71 72class ArgumentParserError(Exception): 73 74 def __init__(self, message, stdout=None, stderr=None, error_code=None): 75 Exception.__init__(self, message, stdout, stderr) 76 self.message = message 77 self.stdout = stdout 78 self.stderr = stderr 79 self.error_code = error_code 80 81 82def stderr_to_parser_error(parse_args, *args, **kwargs): 83 # if this is being called recursively and stderr or stdout is already being 84 # redirected, simply call the function and let the enclosing function 85 # catch the exception 86 if isinstance(sys.stderr, StdIOBuffer) or isinstance(sys.stdout, StdIOBuffer): 87 return parse_args(*args, **kwargs) 88 89 # if this is not being called recursively, redirect stderr and 90 # use it as the ArgumentParserError message 91 old_stdout = sys.stdout 92 old_stderr = sys.stderr 93 sys.stdout = StdIOBuffer() 94 sys.stderr = StdIOBuffer() 95 try: 96 try: 97 result = parse_args(*args, **kwargs) 98 for key in list(vars(result)): 99 if getattr(result, key) is sys.stdout: 100 setattr(result, key, old_stdout) 101 if getattr(result, key) is sys.stderr: 102 setattr(result, key, old_stderr) 103 return result 104 except SystemExit: 105 code = sys.exc_info()[1].code 106 stdout = sys.stdout.getvalue() 107 stderr = sys.stderr.getvalue() 108 raise ArgumentParserError( 109 "SystemExit", stdout, stderr, code) from None 110 finally: 111 sys.stdout = old_stdout 112 sys.stderr = old_stderr 113 114 115class ErrorRaisingArgumentParser(argparse.ArgumentParser): 116 117 def parse_args(self, *args, **kwargs): 118 parse_args = super(ErrorRaisingArgumentParser, self).parse_args 119 return stderr_to_parser_error(parse_args, *args, **kwargs) 120 121 def exit(self, *args, **kwargs): 122 exit = super(ErrorRaisingArgumentParser, self).exit 123 return stderr_to_parser_error(exit, *args, **kwargs) 124 125 def error(self, *args, **kwargs): 126 error = super(ErrorRaisingArgumentParser, self).error 127 return stderr_to_parser_error(error, *args, **kwargs) 128 129 130class ParserTesterMetaclass(type): 131 """Adds parser tests using the class attributes. 132 133 Classes of this type should specify the following attributes: 134 135 argument_signatures -- a list of Sig objects which specify 136 the signatures of Argument objects to be created 137 failures -- a list of args lists that should cause the parser 138 to fail 139 successes -- a list of (initial_args, options, remaining_args) tuples 140 where initial_args specifies the string args to be parsed, 141 options is a dict that should match the vars() of the options 142 parsed out of initial_args, and remaining_args should be any 143 remaining unparsed arguments 144 """ 145 146 def __init__(cls, name, bases, bodydict): 147 if name == 'ParserTestCase': 148 return 149 150 # default parser signature is empty 151 if not hasattr(cls, 'parser_signature'): 152 cls.parser_signature = Sig() 153 if not hasattr(cls, 'parser_class'): 154 cls.parser_class = ErrorRaisingArgumentParser 155 156 # --------------------------------------- 157 # functions for adding optional arguments 158 # --------------------------------------- 159 def no_groups(parser, argument_signatures): 160 """Add all arguments directly to the parser""" 161 for sig in argument_signatures: 162 parser.add_argument(*sig.args, **sig.kwargs) 163 164 def one_group(parser, argument_signatures): 165 """Add all arguments under a single group in the parser""" 166 group = parser.add_argument_group('foo') 167 for sig in argument_signatures: 168 group.add_argument(*sig.args, **sig.kwargs) 169 170 def many_groups(parser, argument_signatures): 171 """Add each argument in its own group to the parser""" 172 for i, sig in enumerate(argument_signatures): 173 group = parser.add_argument_group('foo:%i' % i) 174 group.add_argument(*sig.args, **sig.kwargs) 175 176 # -------------------------- 177 # functions for parsing args 178 # -------------------------- 179 def listargs(parser, args): 180 """Parse the args by passing in a list""" 181 return parser.parse_args(args) 182 183 def sysargs(parser, args): 184 """Parse the args by defaulting to sys.argv""" 185 old_sys_argv = sys.argv 186 sys.argv = [old_sys_argv[0]] + args 187 try: 188 return parser.parse_args() 189 finally: 190 sys.argv = old_sys_argv 191 192 # class that holds the combination of one optional argument 193 # addition method and one arg parsing method 194 class AddTests(object): 195 196 def __init__(self, tester_cls, add_arguments, parse_args): 197 self._add_arguments = add_arguments 198 self._parse_args = parse_args 199 200 add_arguments_name = self._add_arguments.__name__ 201 parse_args_name = self._parse_args.__name__ 202 for test_func in [self.test_failures, self.test_successes]: 203 func_name = test_func.__name__ 204 names = func_name, add_arguments_name, parse_args_name 205 test_name = '_'.join(names) 206 207 def wrapper(self, test_func=test_func): 208 test_func(self) 209 try: 210 wrapper.__name__ = test_name 211 except TypeError: 212 pass 213 setattr(tester_cls, test_name, wrapper) 214 215 def _get_parser(self, tester): 216 args = tester.parser_signature.args 217 kwargs = tester.parser_signature.kwargs 218 parser = tester.parser_class(*args, **kwargs) 219 self._add_arguments(parser, tester.argument_signatures) 220 return parser 221 222 def test_failures(self, tester): 223 parser = self._get_parser(tester) 224 for args_str in tester.failures: 225 args = args_str.split() 226 with tester.assertRaises(ArgumentParserError, msg=args): 227 parser.parse_args(args) 228 229 def test_successes(self, tester): 230 parser = self._get_parser(tester) 231 for args, expected_ns in tester.successes: 232 if isinstance(args, str): 233 args = args.split() 234 result_ns = self._parse_args(parser, args) 235 tester.assertEqual(expected_ns, result_ns) 236 237 # add tests for each combination of an optionals adding method 238 # and an arg parsing method 239 for add_arguments in [no_groups, one_group, many_groups]: 240 for parse_args in [listargs, sysargs]: 241 AddTests(cls, add_arguments, parse_args) 242 243bases = TestCase, 244ParserTestCase = ParserTesterMetaclass('ParserTestCase', bases, {}) 245 246# =============== 247# Optionals tests 248# =============== 249 250class TestOptionalsSingleDash(ParserTestCase): 251 """Test an Optional with a single-dash option string""" 252 253 argument_signatures = [Sig('-x')] 254 failures = ['-x', 'a', '--foo', '-x --foo', '-x -y'] 255 successes = [ 256 ('', NS(x=None)), 257 ('-x a', NS(x='a')), 258 ('-xa', NS(x='a')), 259 ('-x -1', NS(x='-1')), 260 ('-x-1', NS(x='-1')), 261 ] 262 263 264class TestOptionalsSingleDashCombined(ParserTestCase): 265 """Test an Optional with a single-dash option string""" 266 267 argument_signatures = [ 268 Sig('-x', action='store_true'), 269 Sig('-yyy', action='store_const', const=42), 270 Sig('-z'), 271 ] 272 failures = ['a', '--foo', '-xa', '-x --foo', '-x -z', '-z -x', 273 '-yx', '-yz a', '-yyyx', '-yyyza', '-xyza'] 274 successes = [ 275 ('', NS(x=False, yyy=None, z=None)), 276 ('-x', NS(x=True, yyy=None, z=None)), 277 ('-za', NS(x=False, yyy=None, z='a')), 278 ('-z a', NS(x=False, yyy=None, z='a')), 279 ('-xza', NS(x=True, yyy=None, z='a')), 280 ('-xz a', NS(x=True, yyy=None, z='a')), 281 ('-x -za', NS(x=True, yyy=None, z='a')), 282 ('-x -z a', NS(x=True, yyy=None, z='a')), 283 ('-y', NS(x=False, yyy=42, z=None)), 284 ('-yyy', NS(x=False, yyy=42, z=None)), 285 ('-x -yyy -za', NS(x=True, yyy=42, z='a')), 286 ('-x -yyy -z a', NS(x=True, yyy=42, z='a')), 287 ] 288 289 290class TestOptionalsSingleDashLong(ParserTestCase): 291 """Test an Optional with a multi-character single-dash option string""" 292 293 argument_signatures = [Sig('-foo')] 294 failures = ['-foo', 'a', '--foo', '-foo --foo', '-foo -y', '-fooa'] 295 successes = [ 296 ('', NS(foo=None)), 297 ('-foo a', NS(foo='a')), 298 ('-foo -1', NS(foo='-1')), 299 ('-fo a', NS(foo='a')), 300 ('-f a', NS(foo='a')), 301 ] 302 303 304class TestOptionalsSingleDashSubsetAmbiguous(ParserTestCase): 305 """Test Optionals where option strings are subsets of each other""" 306 307 argument_signatures = [Sig('-f'), Sig('-foobar'), Sig('-foorab')] 308 failures = ['-f', '-foo', '-fo', '-foo b', '-foob', '-fooba', '-foora'] 309 successes = [ 310 ('', NS(f=None, foobar=None, foorab=None)), 311 ('-f a', NS(f='a', foobar=None, foorab=None)), 312 ('-fa', NS(f='a', foobar=None, foorab=None)), 313 ('-foa', NS(f='oa', foobar=None, foorab=None)), 314 ('-fooa', NS(f='ooa', foobar=None, foorab=None)), 315 ('-foobar a', NS(f=None, foobar='a', foorab=None)), 316 ('-foorab a', NS(f=None, foobar=None, foorab='a')), 317 ] 318 319 320class TestOptionalsSingleDashAmbiguous(ParserTestCase): 321 """Test Optionals that partially match but are not subsets""" 322 323 argument_signatures = [Sig('-foobar'), Sig('-foorab')] 324 failures = ['-f', '-f a', '-fa', '-foa', '-foo', '-fo', '-foo b'] 325 successes = [ 326 ('', NS(foobar=None, foorab=None)), 327 ('-foob a', NS(foobar='a', foorab=None)), 328 ('-foor a', NS(foobar=None, foorab='a')), 329 ('-fooba a', NS(foobar='a', foorab=None)), 330 ('-foora a', NS(foobar=None, foorab='a')), 331 ('-foobar a', NS(foobar='a', foorab=None)), 332 ('-foorab a', NS(foobar=None, foorab='a')), 333 ] 334 335 336class TestOptionalsNumeric(ParserTestCase): 337 """Test an Optional with a short opt string""" 338 339 argument_signatures = [Sig('-1', dest='one')] 340 failures = ['-1', 'a', '-1 --foo', '-1 -y', '-1 -1', '-1 -2'] 341 successes = [ 342 ('', NS(one=None)), 343 ('-1 a', NS(one='a')), 344 ('-1a', NS(one='a')), 345 ('-1-2', NS(one='-2')), 346 ] 347 348 349class TestOptionalsDoubleDash(ParserTestCase): 350 """Test an Optional with a double-dash option string""" 351 352 argument_signatures = [Sig('--foo')] 353 failures = ['--foo', '-f', '-f a', 'a', '--foo -x', '--foo --bar'] 354 successes = [ 355 ('', NS(foo=None)), 356 ('--foo a', NS(foo='a')), 357 ('--foo=a', NS(foo='a')), 358 ('--foo -2.5', NS(foo='-2.5')), 359 ('--foo=-2.5', NS(foo='-2.5')), 360 ] 361 362 363class TestOptionalsDoubleDashPartialMatch(ParserTestCase): 364 """Tests partial matching with a double-dash option string""" 365 366 argument_signatures = [ 367 Sig('--badger', action='store_true'), 368 Sig('--bat'), 369 ] 370 failures = ['--bar', '--b', '--ba', '--b=2', '--ba=4', '--badge 5'] 371 successes = [ 372 ('', NS(badger=False, bat=None)), 373 ('--bat X', NS(badger=False, bat='X')), 374 ('--bad', NS(badger=True, bat=None)), 375 ('--badg', NS(badger=True, bat=None)), 376 ('--badge', NS(badger=True, bat=None)), 377 ('--badger', NS(badger=True, bat=None)), 378 ] 379 380 381class TestOptionalsDoubleDashPrefixMatch(ParserTestCase): 382 """Tests when one double-dash option string is a prefix of another""" 383 384 argument_signatures = [ 385 Sig('--badger', action='store_true'), 386 Sig('--ba'), 387 ] 388 failures = ['--bar', '--b', '--ba', '--b=2', '--badge 5'] 389 successes = [ 390 ('', NS(badger=False, ba=None)), 391 ('--ba X', NS(badger=False, ba='X')), 392 ('--ba=X', NS(badger=False, ba='X')), 393 ('--bad', NS(badger=True, ba=None)), 394 ('--badg', NS(badger=True, ba=None)), 395 ('--badge', NS(badger=True, ba=None)), 396 ('--badger', NS(badger=True, ba=None)), 397 ] 398 399 400class TestOptionalsSingleDoubleDash(ParserTestCase): 401 """Test an Optional with single- and double-dash option strings""" 402 403 argument_signatures = [ 404 Sig('-f', action='store_true'), 405 Sig('--bar'), 406 Sig('-baz', action='store_const', const=42), 407 ] 408 failures = ['--bar', '-fbar', '-fbaz', '-bazf', '-b B', 'B'] 409 successes = [ 410 ('', NS(f=False, bar=None, baz=None)), 411 ('-f', NS(f=True, bar=None, baz=None)), 412 ('--ba B', NS(f=False, bar='B', baz=None)), 413 ('-f --bar B', NS(f=True, bar='B', baz=None)), 414 ('-f -b', NS(f=True, bar=None, baz=42)), 415 ('-ba -f', NS(f=True, bar=None, baz=42)), 416 ] 417 418 419class TestOptionalsAlternatePrefixChars(ParserTestCase): 420 """Test an Optional with option strings with custom prefixes""" 421 422 parser_signature = Sig(prefix_chars='+:/', add_help=False) 423 argument_signatures = [ 424 Sig('+f', action='store_true'), 425 Sig('::bar'), 426 Sig('/baz', action='store_const', const=42), 427 ] 428 failures = ['--bar', '-fbar', '-b B', 'B', '-f', '--bar B', '-baz', '-h', '--help', '+h', '::help', '/help'] 429 successes = [ 430 ('', NS(f=False, bar=None, baz=None)), 431 ('+f', NS(f=True, bar=None, baz=None)), 432 ('::ba B', NS(f=False, bar='B', baz=None)), 433 ('+f ::bar B', NS(f=True, bar='B', baz=None)), 434 ('+f /b', NS(f=True, bar=None, baz=42)), 435 ('/ba +f', NS(f=True, bar=None, baz=42)), 436 ] 437 438 439class TestOptionalsAlternatePrefixCharsAddedHelp(ParserTestCase): 440 """When ``-`` not in prefix_chars, default operators created for help 441 should use the prefix_chars in use rather than - or -- 442 http://bugs.python.org/issue9444""" 443 444 parser_signature = Sig(prefix_chars='+:/', add_help=True) 445 argument_signatures = [ 446 Sig('+f', action='store_true'), 447 Sig('::bar'), 448 Sig('/baz', action='store_const', const=42), 449 ] 450 failures = ['--bar', '-fbar', '-b B', 'B', '-f', '--bar B', '-baz'] 451 successes = [ 452 ('', NS(f=False, bar=None, baz=None)), 453 ('+f', NS(f=True, bar=None, baz=None)), 454 ('::ba B', NS(f=False, bar='B', baz=None)), 455 ('+f ::bar B', NS(f=True, bar='B', baz=None)), 456 ('+f /b', NS(f=True, bar=None, baz=42)), 457 ('/ba +f', NS(f=True, bar=None, baz=42)) 458 ] 459 460 461class TestOptionalsAlternatePrefixCharsMultipleShortArgs(ParserTestCase): 462 """Verify that Optionals must be called with their defined prefixes""" 463 464 parser_signature = Sig(prefix_chars='+-', add_help=False) 465 argument_signatures = [ 466 Sig('-x', action='store_true'), 467 Sig('+y', action='store_true'), 468 Sig('+z', action='store_true'), 469 ] 470 failures = ['-w', 471 '-xyz', 472 '+x', 473 '-y', 474 '+xyz', 475 ] 476 successes = [ 477 ('', NS(x=False, y=False, z=False)), 478 ('-x', NS(x=True, y=False, z=False)), 479 ('+y -x', NS(x=True, y=True, z=False)), 480 ('+yz -x', NS(x=True, y=True, z=True)), 481 ] 482 483 484class TestOptionalsShortLong(ParserTestCase): 485 """Test a combination of single- and double-dash option strings""" 486 487 argument_signatures = [ 488 Sig('-v', '--verbose', '-n', '--noisy', action='store_true'), 489 ] 490 failures = ['--x --verbose', '-N', 'a', '-v x'] 491 successes = [ 492 ('', NS(verbose=False)), 493 ('-v', NS(verbose=True)), 494 ('--verbose', NS(verbose=True)), 495 ('-n', NS(verbose=True)), 496 ('--noisy', NS(verbose=True)), 497 ] 498 499 500class TestOptionalsDest(ParserTestCase): 501 """Tests various means of setting destination""" 502 503 argument_signatures = [Sig('--foo-bar'), Sig('--baz', dest='zabbaz')] 504 failures = ['a'] 505 successes = [ 506 ('--foo-bar f', NS(foo_bar='f', zabbaz=None)), 507 ('--baz g', NS(foo_bar=None, zabbaz='g')), 508 ('--foo-bar h --baz i', NS(foo_bar='h', zabbaz='i')), 509 ('--baz j --foo-bar k', NS(foo_bar='k', zabbaz='j')), 510 ] 511 512 513class TestOptionalsDefault(ParserTestCase): 514 """Tests specifying a default for an Optional""" 515 516 argument_signatures = [Sig('-x'), Sig('-y', default=42)] 517 failures = ['a'] 518 successes = [ 519 ('', NS(x=None, y=42)), 520 ('-xx', NS(x='x', y=42)), 521 ('-yy', NS(x=None, y='y')), 522 ] 523 524 525class TestOptionalsNargsDefault(ParserTestCase): 526 """Tests not specifying the number of args for an Optional""" 527 528 argument_signatures = [Sig('-x')] 529 failures = ['a', '-x'] 530 successes = [ 531 ('', NS(x=None)), 532 ('-x a', NS(x='a')), 533 ] 534 535 536class TestOptionalsNargs1(ParserTestCase): 537 """Tests specifying 1 arg for an Optional""" 538 539 argument_signatures = [Sig('-x', nargs=1)] 540 failures = ['a', '-x'] 541 successes = [ 542 ('', NS(x=None)), 543 ('-x a', NS(x=['a'])), 544 ] 545 546 547class TestOptionalsNargs3(ParserTestCase): 548 """Tests specifying 3 args for an Optional""" 549 550 argument_signatures = [Sig('-x', nargs=3)] 551 failures = ['a', '-x', '-x a', '-x a b', 'a -x', 'a -x b'] 552 successes = [ 553 ('', NS(x=None)), 554 ('-x a b c', NS(x=['a', 'b', 'c'])), 555 ] 556 557 558class TestOptionalsNargsOptional(ParserTestCase): 559 """Tests specifying an Optional arg for an Optional""" 560 561 argument_signatures = [ 562 Sig('-w', nargs='?'), 563 Sig('-x', nargs='?', const=42), 564 Sig('-y', nargs='?', default='spam'), 565 Sig('-z', nargs='?', type=int, const='42', default='84'), 566 ] 567 failures = ['2'] 568 successes = [ 569 ('', NS(w=None, x=None, y='spam', z=84)), 570 ('-w', NS(w=None, x=None, y='spam', z=84)), 571 ('-w 2', NS(w='2', x=None, y='spam', z=84)), 572 ('-x', NS(w=None, x=42, y='spam', z=84)), 573 ('-x 2', NS(w=None, x='2', y='spam', z=84)), 574 ('-y', NS(w=None, x=None, y=None, z=84)), 575 ('-y 2', NS(w=None, x=None, y='2', z=84)), 576 ('-z', NS(w=None, x=None, y='spam', z=42)), 577 ('-z 2', NS(w=None, x=None, y='spam', z=2)), 578 ] 579 580 581class TestOptionalsNargsZeroOrMore(ParserTestCase): 582 """Tests specifying args for an Optional that accepts zero or more""" 583 584 argument_signatures = [ 585 Sig('-x', nargs='*'), 586 Sig('-y', nargs='*', default='spam'), 587 ] 588 failures = ['a'] 589 successes = [ 590 ('', NS(x=None, y='spam')), 591 ('-x', NS(x=[], y='spam')), 592 ('-x a', NS(x=['a'], y='spam')), 593 ('-x a b', NS(x=['a', 'b'], y='spam')), 594 ('-y', NS(x=None, y=[])), 595 ('-y a', NS(x=None, y=['a'])), 596 ('-y a b', NS(x=None, y=['a', 'b'])), 597 ] 598 599 600class TestOptionalsNargsOneOrMore(ParserTestCase): 601 """Tests specifying args for an Optional that accepts one or more""" 602 603 argument_signatures = [ 604 Sig('-x', nargs='+'), 605 Sig('-y', nargs='+', default='spam'), 606 ] 607 failures = ['a', '-x', '-y', 'a -x', 'a -y b'] 608 successes = [ 609 ('', NS(x=None, y='spam')), 610 ('-x a', NS(x=['a'], y='spam')), 611 ('-x a b', NS(x=['a', 'b'], y='spam')), 612 ('-y a', NS(x=None, y=['a'])), 613 ('-y a b', NS(x=None, y=['a', 'b'])), 614 ] 615 616 617class TestOptionalsChoices(ParserTestCase): 618 """Tests specifying the choices for an Optional""" 619 620 argument_signatures = [ 621 Sig('-f', choices='abc'), 622 Sig('-g', type=int, choices=range(5))] 623 failures = ['a', '-f d', '-fad', '-ga', '-g 6'] 624 successes = [ 625 ('', NS(f=None, g=None)), 626 ('-f a', NS(f='a', g=None)), 627 ('-f c', NS(f='c', g=None)), 628 ('-g 0', NS(f=None, g=0)), 629 ('-g 03', NS(f=None, g=3)), 630 ('-fb -g4', NS(f='b', g=4)), 631 ] 632 633 634class TestOptionalsRequired(ParserTestCase): 635 """Tests an optional action that is required""" 636 637 argument_signatures = [ 638 Sig('-x', type=int, required=True), 639 ] 640 failures = ['a', ''] 641 successes = [ 642 ('-x 1', NS(x=1)), 643 ('-x42', NS(x=42)), 644 ] 645 646 647class TestOptionalsActionStore(ParserTestCase): 648 """Tests the store action for an Optional""" 649 650 argument_signatures = [Sig('-x', action='store')] 651 failures = ['a', 'a -x'] 652 successes = [ 653 ('', NS(x=None)), 654 ('-xfoo', NS(x='foo')), 655 ] 656 657 658class TestOptionalsActionStoreConst(ParserTestCase): 659 """Tests the store_const action for an Optional""" 660 661 argument_signatures = [Sig('-y', action='store_const', const=object)] 662 failures = ['a'] 663 successes = [ 664 ('', NS(y=None)), 665 ('-y', NS(y=object)), 666 ] 667 668 669class TestOptionalsActionStoreFalse(ParserTestCase): 670 """Tests the store_false action for an Optional""" 671 672 argument_signatures = [Sig('-z', action='store_false')] 673 failures = ['a', '-za', '-z a'] 674 successes = [ 675 ('', NS(z=True)), 676 ('-z', NS(z=False)), 677 ] 678 679 680class TestOptionalsActionStoreTrue(ParserTestCase): 681 """Tests the store_true action for an Optional""" 682 683 argument_signatures = [Sig('--apple', action='store_true')] 684 failures = ['a', '--apple=b', '--apple b'] 685 successes = [ 686 ('', NS(apple=False)), 687 ('--apple', NS(apple=True)), 688 ] 689 690class TestBooleanOptionalAction(ParserTestCase): 691 """Tests BooleanOptionalAction""" 692 693 argument_signatures = [Sig('--foo', action=argparse.BooleanOptionalAction)] 694 failures = ['--foo bar', '--foo=bar'] 695 successes = [ 696 ('', NS(foo=None)), 697 ('--foo', NS(foo=True)), 698 ('--no-foo', NS(foo=False)), 699 ('--foo --no-foo', NS(foo=False)), # useful for aliases 700 ('--no-foo --foo', NS(foo=True)), 701 ] 702 703 def test_const(self): 704 # See bpo-40862 705 parser = argparse.ArgumentParser() 706 with self.assertRaises(TypeError) as cm: 707 parser.add_argument('--foo', const=True, action=argparse.BooleanOptionalAction) 708 709 self.assertIn("got an unexpected keyword argument 'const'", str(cm.exception)) 710 711class TestBooleanOptionalActionRequired(ParserTestCase): 712 """Tests BooleanOptionalAction required""" 713 714 argument_signatures = [ 715 Sig('--foo', required=True, action=argparse.BooleanOptionalAction) 716 ] 717 failures = [''] 718 successes = [ 719 ('--foo', NS(foo=True)), 720 ('--no-foo', NS(foo=False)), 721 ] 722 723class TestOptionalsActionAppend(ParserTestCase): 724 """Tests the append action for an Optional""" 725 726 argument_signatures = [Sig('--baz', action='append')] 727 failures = ['a', '--baz', 'a --baz', '--baz a b'] 728 successes = [ 729 ('', NS(baz=None)), 730 ('--baz a', NS(baz=['a'])), 731 ('--baz a --baz b', NS(baz=['a', 'b'])), 732 ] 733 734 735class TestOptionalsActionAppendWithDefault(ParserTestCase): 736 """Tests the append action for an Optional""" 737 738 argument_signatures = [Sig('--baz', action='append', default=['X'])] 739 failures = ['a', '--baz', 'a --baz', '--baz a b'] 740 successes = [ 741 ('', NS(baz=['X'])), 742 ('--baz a', NS(baz=['X', 'a'])), 743 ('--baz a --baz b', NS(baz=['X', 'a', 'b'])), 744 ] 745 746 747class TestOptionalsActionAppendConst(ParserTestCase): 748 """Tests the append_const action for an Optional""" 749 750 argument_signatures = [ 751 Sig('-b', action='append_const', const=Exception), 752 Sig('-c', action='append', dest='b'), 753 ] 754 failures = ['a', '-c', 'a -c', '-bx', '-b x'] 755 successes = [ 756 ('', NS(b=None)), 757 ('-b', NS(b=[Exception])), 758 ('-b -cx -b -cyz', NS(b=[Exception, 'x', Exception, 'yz'])), 759 ] 760 761 762class TestOptionalsActionAppendConstWithDefault(ParserTestCase): 763 """Tests the append_const action for an Optional""" 764 765 argument_signatures = [ 766 Sig('-b', action='append_const', const=Exception, default=['X']), 767 Sig('-c', action='append', dest='b'), 768 ] 769 failures = ['a', '-c', 'a -c', '-bx', '-b x'] 770 successes = [ 771 ('', NS(b=['X'])), 772 ('-b', NS(b=['X', Exception])), 773 ('-b -cx -b -cyz', NS(b=['X', Exception, 'x', Exception, 'yz'])), 774 ] 775 776 777class TestOptionalsActionCount(ParserTestCase): 778 """Tests the count action for an Optional""" 779 780 argument_signatures = [Sig('-x', action='count')] 781 failures = ['a', '-x a', '-x b', '-x a -x b'] 782 successes = [ 783 ('', NS(x=None)), 784 ('-x', NS(x=1)), 785 ] 786 787 788class TestOptionalsAllowLongAbbreviation(ParserTestCase): 789 """Allow long options to be abbreviated unambiguously""" 790 791 argument_signatures = [ 792 Sig('--foo'), 793 Sig('--foobaz'), 794 Sig('--fooble', action='store_true'), 795 ] 796 failures = ['--foob 5', '--foob'] 797 successes = [ 798 ('', NS(foo=None, foobaz=None, fooble=False)), 799 ('--foo 7', NS(foo='7', foobaz=None, fooble=False)), 800 ('--fooba a', NS(foo=None, foobaz='a', fooble=False)), 801 ('--foobl --foo g', NS(foo='g', foobaz=None, fooble=True)), 802 ] 803 804 805class TestOptionalsDisallowLongAbbreviation(ParserTestCase): 806 """Do not allow abbreviations of long options at all""" 807 808 parser_signature = Sig(allow_abbrev=False) 809 argument_signatures = [ 810 Sig('--foo'), 811 Sig('--foodle', action='store_true'), 812 Sig('--foonly'), 813 ] 814 failures = ['-foon 3', '--foon 3', '--food', '--food --foo 2'] 815 successes = [ 816 ('', NS(foo=None, foodle=False, foonly=None)), 817 ('--foo 3', NS(foo='3', foodle=False, foonly=None)), 818 ('--foonly 7 --foodle --foo 2', NS(foo='2', foodle=True, foonly='7')), 819 ] 820 821 822class TestOptionalsDisallowLongAbbreviationPrefixChars(ParserTestCase): 823 """Disallowing abbreviations works with alternative prefix characters""" 824 825 parser_signature = Sig(prefix_chars='+', allow_abbrev=False) 826 argument_signatures = [ 827 Sig('++foo'), 828 Sig('++foodle', action='store_true'), 829 Sig('++foonly'), 830 ] 831 failures = ['+foon 3', '++foon 3', '++food', '++food ++foo 2'] 832 successes = [ 833 ('', NS(foo=None, foodle=False, foonly=None)), 834 ('++foo 3', NS(foo='3', foodle=False, foonly=None)), 835 ('++foonly 7 ++foodle ++foo 2', NS(foo='2', foodle=True, foonly='7')), 836 ] 837 838 839class TestDisallowLongAbbreviationAllowsShortGrouping(ParserTestCase): 840 """Do not allow abbreviations of long options at all""" 841 842 parser_signature = Sig(allow_abbrev=False) 843 argument_signatures = [ 844 Sig('-r'), 845 Sig('-c', action='count'), 846 ] 847 failures = ['-r', '-c -r'] 848 successes = [ 849 ('', NS(r=None, c=None)), 850 ('-ra', NS(r='a', c=None)), 851 ('-rcc', NS(r='cc', c=None)), 852 ('-cc', NS(r=None, c=2)), 853 ('-cc -ra', NS(r='a', c=2)), 854 ('-ccrcc', NS(r='cc', c=2)), 855 ] 856 857 858class TestDisallowLongAbbreviationAllowsShortGroupingPrefix(ParserTestCase): 859 """Short option grouping works with custom prefix and allow_abbrev=False""" 860 861 parser_signature = Sig(prefix_chars='+', allow_abbrev=False) 862 argument_signatures = [ 863 Sig('+r'), 864 Sig('+c', action='count'), 865 ] 866 failures = ['+r', '+c +r'] 867 successes = [ 868 ('', NS(r=None, c=None)), 869 ('+ra', NS(r='a', c=None)), 870 ('+rcc', NS(r='cc', c=None)), 871 ('+cc', NS(r=None, c=2)), 872 ('+cc +ra', NS(r='a', c=2)), 873 ('+ccrcc', NS(r='cc', c=2)), 874 ] 875 876 877# ================ 878# Positional tests 879# ================ 880 881class TestPositionalsNargsNone(ParserTestCase): 882 """Test a Positional that doesn't specify nargs""" 883 884 argument_signatures = [Sig('foo')] 885 failures = ['', '-x', 'a b'] 886 successes = [ 887 ('a', NS(foo='a')), 888 ] 889 890 891class TestPositionalsNargs1(ParserTestCase): 892 """Test a Positional that specifies an nargs of 1""" 893 894 argument_signatures = [Sig('foo', nargs=1)] 895 failures = ['', '-x', 'a b'] 896 successes = [ 897 ('a', NS(foo=['a'])), 898 ] 899 900 901class TestPositionalsNargs2(ParserTestCase): 902 """Test a Positional that specifies an nargs of 2""" 903 904 argument_signatures = [Sig('foo', nargs=2)] 905 failures = ['', 'a', '-x', 'a b c'] 906 successes = [ 907 ('a b', NS(foo=['a', 'b'])), 908 ] 909 910 911class TestPositionalsNargsZeroOrMore(ParserTestCase): 912 """Test a Positional that specifies unlimited nargs""" 913 914 argument_signatures = [Sig('foo', nargs='*')] 915 failures = ['-x'] 916 successes = [ 917 ('', NS(foo=[])), 918 ('a', NS(foo=['a'])), 919 ('a b', NS(foo=['a', 'b'])), 920 ] 921 922 923class TestPositionalsNargsZeroOrMoreDefault(ParserTestCase): 924 """Test a Positional that specifies unlimited nargs and a default""" 925 926 argument_signatures = [Sig('foo', nargs='*', default='bar')] 927 failures = ['-x'] 928 successes = [ 929 ('', NS(foo='bar')), 930 ('a', NS(foo=['a'])), 931 ('a b', NS(foo=['a', 'b'])), 932 ] 933 934 935class TestPositionalsNargsOneOrMore(ParserTestCase): 936 """Test a Positional that specifies one or more nargs""" 937 938 argument_signatures = [Sig('foo', nargs='+')] 939 failures = ['', '-x'] 940 successes = [ 941 ('a', NS(foo=['a'])), 942 ('a b', NS(foo=['a', 'b'])), 943 ] 944 945 946class TestPositionalsNargsOptional(ParserTestCase): 947 """Tests an Optional Positional""" 948 949 argument_signatures = [Sig('foo', nargs='?')] 950 failures = ['-x', 'a b'] 951 successes = [ 952 ('', NS(foo=None)), 953 ('a', NS(foo='a')), 954 ] 955 956 957class TestPositionalsNargsOptionalDefault(ParserTestCase): 958 """Tests an Optional Positional with a default value""" 959 960 argument_signatures = [Sig('foo', nargs='?', default=42)] 961 failures = ['-x', 'a b'] 962 successes = [ 963 ('', NS(foo=42)), 964 ('a', NS(foo='a')), 965 ] 966 967 968class TestPositionalsNargsOptionalConvertedDefault(ParserTestCase): 969 """Tests an Optional Positional with a default value 970 that needs to be converted to the appropriate type. 971 """ 972 973 argument_signatures = [ 974 Sig('foo', nargs='?', type=int, default='42'), 975 ] 976 failures = ['-x', 'a b', '1 2'] 977 successes = [ 978 ('', NS(foo=42)), 979 ('1', NS(foo=1)), 980 ] 981 982 983class TestPositionalsNargsNoneNone(ParserTestCase): 984 """Test two Positionals that don't specify nargs""" 985 986 argument_signatures = [Sig('foo'), Sig('bar')] 987 failures = ['', '-x', 'a', 'a b c'] 988 successes = [ 989 ('a b', NS(foo='a', bar='b')), 990 ] 991 992 993class TestPositionalsNargsNone1(ParserTestCase): 994 """Test a Positional with no nargs followed by one with 1""" 995 996 argument_signatures = [Sig('foo'), Sig('bar', nargs=1)] 997 failures = ['', '--foo', 'a', 'a b c'] 998 successes = [ 999 ('a b', NS(foo='a', bar=['b'])), 1000 ] 1001 1002 1003class TestPositionalsNargs2None(ParserTestCase): 1004 """Test a Positional with 2 nargs followed by one with none""" 1005 1006 argument_signatures = [Sig('foo', nargs=2), Sig('bar')] 1007 failures = ['', '--foo', 'a', 'a b', 'a b c d'] 1008 successes = [ 1009 ('a b c', NS(foo=['a', 'b'], bar='c')), 1010 ] 1011 1012 1013class TestPositionalsNargsNoneZeroOrMore(ParserTestCase): 1014 """Test a Positional with no nargs followed by one with unlimited""" 1015 1016 argument_signatures = [Sig('foo'), Sig('bar', nargs='*')] 1017 failures = ['', '--foo'] 1018 successes = [ 1019 ('a', NS(foo='a', bar=[])), 1020 ('a b', NS(foo='a', bar=['b'])), 1021 ('a b c', NS(foo='a', bar=['b', 'c'])), 1022 ] 1023 1024 1025class TestPositionalsNargsNoneOneOrMore(ParserTestCase): 1026 """Test a Positional with no nargs followed by one with one or more""" 1027 1028 argument_signatures = [Sig('foo'), Sig('bar', nargs='+')] 1029 failures = ['', '--foo', 'a'] 1030 successes = [ 1031 ('a b', NS(foo='a', bar=['b'])), 1032 ('a b c', NS(foo='a', bar=['b', 'c'])), 1033 ] 1034 1035 1036class TestPositionalsNargsNoneOptional(ParserTestCase): 1037 """Test a Positional with no nargs followed by one with an Optional""" 1038 1039 argument_signatures = [Sig('foo'), Sig('bar', nargs='?')] 1040 failures = ['', '--foo', 'a b c'] 1041 successes = [ 1042 ('a', NS(foo='a', bar=None)), 1043 ('a b', NS(foo='a', bar='b')), 1044 ] 1045 1046 1047class TestPositionalsNargsZeroOrMoreNone(ParserTestCase): 1048 """Test a Positional with unlimited nargs followed by one with none""" 1049 1050 argument_signatures = [Sig('foo', nargs='*'), Sig('bar')] 1051 failures = ['', '--foo'] 1052 successes = [ 1053 ('a', NS(foo=[], bar='a')), 1054 ('a b', NS(foo=['a'], bar='b')), 1055 ('a b c', NS(foo=['a', 'b'], bar='c')), 1056 ] 1057 1058 1059class TestPositionalsNargsOneOrMoreNone(ParserTestCase): 1060 """Test a Positional with one or more nargs followed by one with none""" 1061 1062 argument_signatures = [Sig('foo', nargs='+'), Sig('bar')] 1063 failures = ['', '--foo', 'a'] 1064 successes = [ 1065 ('a b', NS(foo=['a'], bar='b')), 1066 ('a b c', NS(foo=['a', 'b'], bar='c')), 1067 ] 1068 1069 1070class TestPositionalsNargsOptionalNone(ParserTestCase): 1071 """Test a Positional with an Optional nargs followed by one with none""" 1072 1073 argument_signatures = [Sig('foo', nargs='?', default=42), Sig('bar')] 1074 failures = ['', '--foo', 'a b c'] 1075 successes = [ 1076 ('a', NS(foo=42, bar='a')), 1077 ('a b', NS(foo='a', bar='b')), 1078 ] 1079 1080 1081class TestPositionalsNargs2ZeroOrMore(ParserTestCase): 1082 """Test a Positional with 2 nargs followed by one with unlimited""" 1083 1084 argument_signatures = [Sig('foo', nargs=2), Sig('bar', nargs='*')] 1085 failures = ['', '--foo', 'a'] 1086 successes = [ 1087 ('a b', NS(foo=['a', 'b'], bar=[])), 1088 ('a b c', NS(foo=['a', 'b'], bar=['c'])), 1089 ] 1090 1091 1092class TestPositionalsNargs2OneOrMore(ParserTestCase): 1093 """Test a Positional with 2 nargs followed by one with one or more""" 1094 1095 argument_signatures = [Sig('foo', nargs=2), Sig('bar', nargs='+')] 1096 failures = ['', '--foo', 'a', 'a b'] 1097 successes = [ 1098 ('a b c', NS(foo=['a', 'b'], bar=['c'])), 1099 ] 1100 1101 1102class TestPositionalsNargs2Optional(ParserTestCase): 1103 """Test a Positional with 2 nargs followed by one optional""" 1104 1105 argument_signatures = [Sig('foo', nargs=2), Sig('bar', nargs='?')] 1106 failures = ['', '--foo', 'a', 'a b c d'] 1107 successes = [ 1108 ('a b', NS(foo=['a', 'b'], bar=None)), 1109 ('a b c', NS(foo=['a', 'b'], bar='c')), 1110 ] 1111 1112 1113class TestPositionalsNargsZeroOrMore1(ParserTestCase): 1114 """Test a Positional with unlimited nargs followed by one with 1""" 1115 1116 argument_signatures = [Sig('foo', nargs='*'), Sig('bar', nargs=1)] 1117 failures = ['', '--foo', ] 1118 successes = [ 1119 ('a', NS(foo=[], bar=['a'])), 1120 ('a b', NS(foo=['a'], bar=['b'])), 1121 ('a b c', NS(foo=['a', 'b'], bar=['c'])), 1122 ] 1123 1124 1125class TestPositionalsNargsOneOrMore1(ParserTestCase): 1126 """Test a Positional with one or more nargs followed by one with 1""" 1127 1128 argument_signatures = [Sig('foo', nargs='+'), Sig('bar', nargs=1)] 1129 failures = ['', '--foo', 'a'] 1130 successes = [ 1131 ('a b', NS(foo=['a'], bar=['b'])), 1132 ('a b c', NS(foo=['a', 'b'], bar=['c'])), 1133 ] 1134 1135 1136class TestPositionalsNargsOptional1(ParserTestCase): 1137 """Test a Positional with an Optional nargs followed by one with 1""" 1138 1139 argument_signatures = [Sig('foo', nargs='?'), Sig('bar', nargs=1)] 1140 failures = ['', '--foo', 'a b c'] 1141 successes = [ 1142 ('a', NS(foo=None, bar=['a'])), 1143 ('a b', NS(foo='a', bar=['b'])), 1144 ] 1145 1146 1147class TestPositionalsNargsNoneZeroOrMore1(ParserTestCase): 1148 """Test three Positionals: no nargs, unlimited nargs and 1 nargs""" 1149 1150 argument_signatures = [ 1151 Sig('foo'), 1152 Sig('bar', nargs='*'), 1153 Sig('baz', nargs=1), 1154 ] 1155 failures = ['', '--foo', 'a'] 1156 successes = [ 1157 ('a b', NS(foo='a', bar=[], baz=['b'])), 1158 ('a b c', NS(foo='a', bar=['b'], baz=['c'])), 1159 ] 1160 1161 1162class TestPositionalsNargsNoneOneOrMore1(ParserTestCase): 1163 """Test three Positionals: no nargs, one or more nargs and 1 nargs""" 1164 1165 argument_signatures = [ 1166 Sig('foo'), 1167 Sig('bar', nargs='+'), 1168 Sig('baz', nargs=1), 1169 ] 1170 failures = ['', '--foo', 'a', 'b'] 1171 successes = [ 1172 ('a b c', NS(foo='a', bar=['b'], baz=['c'])), 1173 ('a b c d', NS(foo='a', bar=['b', 'c'], baz=['d'])), 1174 ] 1175 1176 1177class TestPositionalsNargsNoneOptional1(ParserTestCase): 1178 """Test three Positionals: no nargs, optional narg and 1 nargs""" 1179 1180 argument_signatures = [ 1181 Sig('foo'), 1182 Sig('bar', nargs='?', default=0.625), 1183 Sig('baz', nargs=1), 1184 ] 1185 failures = ['', '--foo', 'a'] 1186 successes = [ 1187 ('a b', NS(foo='a', bar=0.625, baz=['b'])), 1188 ('a b c', NS(foo='a', bar='b', baz=['c'])), 1189 ] 1190 1191 1192class TestPositionalsNargsOptionalOptional(ParserTestCase): 1193 """Test two optional nargs""" 1194 1195 argument_signatures = [ 1196 Sig('foo', nargs='?'), 1197 Sig('bar', nargs='?', default=42), 1198 ] 1199 failures = ['--foo', 'a b c'] 1200 successes = [ 1201 ('', NS(foo=None, bar=42)), 1202 ('a', NS(foo='a', bar=42)), 1203 ('a b', NS(foo='a', bar='b')), 1204 ] 1205 1206 1207class TestPositionalsNargsOptionalZeroOrMore(ParserTestCase): 1208 """Test an Optional narg followed by unlimited nargs""" 1209 1210 argument_signatures = [Sig('foo', nargs='?'), Sig('bar', nargs='*')] 1211 failures = ['--foo'] 1212 successes = [ 1213 ('', NS(foo=None, bar=[])), 1214 ('a', NS(foo='a', bar=[])), 1215 ('a b', NS(foo='a', bar=['b'])), 1216 ('a b c', NS(foo='a', bar=['b', 'c'])), 1217 ] 1218 1219 1220class TestPositionalsNargsOptionalOneOrMore(ParserTestCase): 1221 """Test an Optional narg followed by one or more nargs""" 1222 1223 argument_signatures = [Sig('foo', nargs='?'), Sig('bar', nargs='+')] 1224 failures = ['', '--foo'] 1225 successes = [ 1226 ('a', NS(foo=None, bar=['a'])), 1227 ('a b', NS(foo='a', bar=['b'])), 1228 ('a b c', NS(foo='a', bar=['b', 'c'])), 1229 ] 1230 1231 1232class TestPositionalsChoicesString(ParserTestCase): 1233 """Test a set of single-character choices""" 1234 1235 argument_signatures = [Sig('spam', choices=set('abcdefg'))] 1236 failures = ['', '--foo', 'h', '42', 'ef'] 1237 successes = [ 1238 ('a', NS(spam='a')), 1239 ('g', NS(spam='g')), 1240 ] 1241 1242 1243class TestPositionalsChoicesInt(ParserTestCase): 1244 """Test a set of integer choices""" 1245 1246 argument_signatures = [Sig('spam', type=int, choices=range(20))] 1247 failures = ['', '--foo', 'h', '42', 'ef'] 1248 successes = [ 1249 ('4', NS(spam=4)), 1250 ('15', NS(spam=15)), 1251 ] 1252 1253 1254class TestPositionalsActionAppend(ParserTestCase): 1255 """Test the 'append' action""" 1256 1257 argument_signatures = [ 1258 Sig('spam', action='append'), 1259 Sig('spam', action='append', nargs=2), 1260 ] 1261 failures = ['', '--foo', 'a', 'a b', 'a b c d'] 1262 successes = [ 1263 ('a b c', NS(spam=['a', ['b', 'c']])), 1264 ] 1265 1266# ======================================== 1267# Combined optionals and positionals tests 1268# ======================================== 1269 1270class TestOptionalsNumericAndPositionals(ParserTestCase): 1271 """Tests negative number args when numeric options are present""" 1272 1273 argument_signatures = [ 1274 Sig('x', nargs='?'), 1275 Sig('-4', dest='y', action='store_true'), 1276 ] 1277 failures = ['-2', '-315'] 1278 successes = [ 1279 ('', NS(x=None, y=False)), 1280 ('a', NS(x='a', y=False)), 1281 ('-4', NS(x=None, y=True)), 1282 ('-4 a', NS(x='a', y=True)), 1283 ] 1284 1285 1286class TestOptionalsAlmostNumericAndPositionals(ParserTestCase): 1287 """Tests negative number args when almost numeric options are present""" 1288 1289 argument_signatures = [ 1290 Sig('x', nargs='?'), 1291 Sig('-k4', dest='y', action='store_true'), 1292 ] 1293 failures = ['-k3'] 1294 successes = [ 1295 ('', NS(x=None, y=False)), 1296 ('-2', NS(x='-2', y=False)), 1297 ('a', NS(x='a', y=False)), 1298 ('-k4', NS(x=None, y=True)), 1299 ('-k4 a', NS(x='a', y=True)), 1300 ] 1301 1302 1303class TestEmptyAndSpaceContainingArguments(ParserTestCase): 1304 1305 argument_signatures = [ 1306 Sig('x', nargs='?'), 1307 Sig('-y', '--yyy', dest='y'), 1308 ] 1309 failures = ['-y'] 1310 successes = [ 1311 ([''], NS(x='', y=None)), 1312 (['a badger'], NS(x='a badger', y=None)), 1313 (['-a badger'], NS(x='-a badger', y=None)), 1314 (['-y', ''], NS(x=None, y='')), 1315 (['-y', 'a badger'], NS(x=None, y='a badger')), 1316 (['-y', '-a badger'], NS(x=None, y='-a badger')), 1317 (['--yyy=a badger'], NS(x=None, y='a badger')), 1318 (['--yyy=-a badger'], NS(x=None, y='-a badger')), 1319 ] 1320 1321 1322class TestPrefixCharacterOnlyArguments(ParserTestCase): 1323 1324 parser_signature = Sig(prefix_chars='-+') 1325 argument_signatures = [ 1326 Sig('-', dest='x', nargs='?', const='badger'), 1327 Sig('+', dest='y', type=int, default=42), 1328 Sig('-+-', dest='z', action='store_true'), 1329 ] 1330 failures = ['-y', '+ -'] 1331 successes = [ 1332 ('', NS(x=None, y=42, z=False)), 1333 ('-', NS(x='badger', y=42, z=False)), 1334 ('- X', NS(x='X', y=42, z=False)), 1335 ('+ -3', NS(x=None, y=-3, z=False)), 1336 ('-+-', NS(x=None, y=42, z=True)), 1337 ('- ===', NS(x='===', y=42, z=False)), 1338 ] 1339 1340 1341class TestNargsZeroOrMore(ParserTestCase): 1342 """Tests specifying args for an Optional that accepts zero or more""" 1343 1344 argument_signatures = [Sig('-x', nargs='*'), Sig('y', nargs='*')] 1345 failures = [] 1346 successes = [ 1347 ('', NS(x=None, y=[])), 1348 ('-x', NS(x=[], y=[])), 1349 ('-x a', NS(x=['a'], y=[])), 1350 ('-x a -- b', NS(x=['a'], y=['b'])), 1351 ('a', NS(x=None, y=['a'])), 1352 ('a -x', NS(x=[], y=['a'])), 1353 ('a -x b', NS(x=['b'], y=['a'])), 1354 ] 1355 1356 1357class TestNargsRemainder(ParserTestCase): 1358 """Tests specifying a positional with nargs=REMAINDER""" 1359 1360 argument_signatures = [Sig('x'), Sig('y', nargs='...'), Sig('-z')] 1361 failures = ['', '-z', '-z Z'] 1362 successes = [ 1363 ('X', NS(x='X', y=[], z=None)), 1364 ('-z Z X', NS(x='X', y=[], z='Z')), 1365 ('X A B -z Z', NS(x='X', y=['A', 'B', '-z', 'Z'], z=None)), 1366 ('X Y --foo', NS(x='X', y=['Y', '--foo'], z=None)), 1367 ] 1368 1369 1370class TestOptionLike(ParserTestCase): 1371 """Tests options that may or may not be arguments""" 1372 1373 argument_signatures = [ 1374 Sig('-x', type=float), 1375 Sig('-3', type=float, dest='y'), 1376 Sig('z', nargs='*'), 1377 ] 1378 failures = ['-x', '-y2.5', '-xa', '-x -a', 1379 '-x -3', '-x -3.5', '-3 -3.5', 1380 '-x -2.5', '-x -2.5 a', '-3 -.5', 1381 'a x -1', '-x -1 a', '-3 -1 a'] 1382 successes = [ 1383 ('', NS(x=None, y=None, z=[])), 1384 ('-x 2.5', NS(x=2.5, y=None, z=[])), 1385 ('-x 2.5 a', NS(x=2.5, y=None, z=['a'])), 1386 ('-3.5', NS(x=None, y=0.5, z=[])), 1387 ('-3-.5', NS(x=None, y=-0.5, z=[])), 1388 ('-3 .5', NS(x=None, y=0.5, z=[])), 1389 ('a -3.5', NS(x=None, y=0.5, z=['a'])), 1390 ('a', NS(x=None, y=None, z=['a'])), 1391 ('a -x 1', NS(x=1.0, y=None, z=['a'])), 1392 ('-x 1 a', NS(x=1.0, y=None, z=['a'])), 1393 ('-3 1 a', NS(x=None, y=1.0, z=['a'])), 1394 ] 1395 1396 1397class TestDefaultSuppress(ParserTestCase): 1398 """Test actions with suppressed defaults""" 1399 1400 argument_signatures = [ 1401 Sig('foo', nargs='?', default=argparse.SUPPRESS), 1402 Sig('bar', nargs='*', default=argparse.SUPPRESS), 1403 Sig('--baz', action='store_true', default=argparse.SUPPRESS), 1404 ] 1405 failures = ['-x'] 1406 successes = [ 1407 ('', NS()), 1408 ('a', NS(foo='a')), 1409 ('a b', NS(foo='a', bar=['b'])), 1410 ('--baz', NS(baz=True)), 1411 ('a --baz', NS(foo='a', baz=True)), 1412 ('--baz a b', NS(foo='a', bar=['b'], baz=True)), 1413 ] 1414 1415 1416class TestParserDefaultSuppress(ParserTestCase): 1417 """Test actions with a parser-level default of SUPPRESS""" 1418 1419 parser_signature = Sig(argument_default=argparse.SUPPRESS) 1420 argument_signatures = [ 1421 Sig('foo', nargs='?'), 1422 Sig('bar', nargs='*'), 1423 Sig('--baz', action='store_true'), 1424 ] 1425 failures = ['-x'] 1426 successes = [ 1427 ('', NS()), 1428 ('a', NS(foo='a')), 1429 ('a b', NS(foo='a', bar=['b'])), 1430 ('--baz', NS(baz=True)), 1431 ('a --baz', NS(foo='a', baz=True)), 1432 ('--baz a b', NS(foo='a', bar=['b'], baz=True)), 1433 ] 1434 1435 1436class TestParserDefault42(ParserTestCase): 1437 """Test actions with a parser-level default of 42""" 1438 1439 parser_signature = Sig(argument_default=42) 1440 argument_signatures = [ 1441 Sig('--version', action='version', version='1.0'), 1442 Sig('foo', nargs='?'), 1443 Sig('bar', nargs='*'), 1444 Sig('--baz', action='store_true'), 1445 ] 1446 failures = ['-x'] 1447 successes = [ 1448 ('', NS(foo=42, bar=42, baz=42, version=42)), 1449 ('a', NS(foo='a', bar=42, baz=42, version=42)), 1450 ('a b', NS(foo='a', bar=['b'], baz=42, version=42)), 1451 ('--baz', NS(foo=42, bar=42, baz=True, version=42)), 1452 ('a --baz', NS(foo='a', bar=42, baz=True, version=42)), 1453 ('--baz a b', NS(foo='a', bar=['b'], baz=True, version=42)), 1454 ] 1455 1456 1457class TestArgumentsFromFile(TempDirMixin, ParserTestCase): 1458 """Test reading arguments from a file""" 1459 1460 def setUp(self): 1461 super(TestArgumentsFromFile, self).setUp() 1462 file_texts = [ 1463 ('hello', 'hello world!\n'), 1464 ('recursive', '-a\n' 1465 'A\n' 1466 '@hello'), 1467 ('invalid', '@no-such-path\n'), 1468 ] 1469 for path, text in file_texts: 1470 with open(path, 'w') as file: 1471 file.write(text) 1472 1473 parser_signature = Sig(fromfile_prefix_chars='@') 1474 argument_signatures = [ 1475 Sig('-a'), 1476 Sig('x'), 1477 Sig('y', nargs='+'), 1478 ] 1479 failures = ['', '-b', 'X', '@invalid', '@missing'] 1480 successes = [ 1481 ('X Y', NS(a=None, x='X', y=['Y'])), 1482 ('X -a A Y Z', NS(a='A', x='X', y=['Y', 'Z'])), 1483 ('@hello X', NS(a=None, x='hello world!', y=['X'])), 1484 ('X @hello', NS(a=None, x='X', y=['hello world!'])), 1485 ('-a B @recursive Y Z', NS(a='A', x='hello world!', y=['Y', 'Z'])), 1486 ('X @recursive Z -a B', NS(a='B', x='X', y=['hello world!', 'Z'])), 1487 (["-a", "", "X", "Y"], NS(a='', x='X', y=['Y'])), 1488 ] 1489 1490 1491class TestArgumentsFromFileConverter(TempDirMixin, ParserTestCase): 1492 """Test reading arguments from a file""" 1493 1494 def setUp(self): 1495 super(TestArgumentsFromFileConverter, self).setUp() 1496 file_texts = [ 1497 ('hello', 'hello world!\n'), 1498 ] 1499 for path, text in file_texts: 1500 with open(path, 'w') as file: 1501 file.write(text) 1502 1503 class FromFileConverterArgumentParser(ErrorRaisingArgumentParser): 1504 1505 def convert_arg_line_to_args(self, arg_line): 1506 for arg in arg_line.split(): 1507 if not arg.strip(): 1508 continue 1509 yield arg 1510 parser_class = FromFileConverterArgumentParser 1511 parser_signature = Sig(fromfile_prefix_chars='@') 1512 argument_signatures = [ 1513 Sig('y', nargs='+'), 1514 ] 1515 failures = [] 1516 successes = [ 1517 ('@hello X', NS(y=['hello', 'world!', 'X'])), 1518 ] 1519 1520 1521# ===================== 1522# Type conversion tests 1523# ===================== 1524 1525class TestFileTypeRepr(TestCase): 1526 1527 def test_r(self): 1528 type = argparse.FileType('r') 1529 self.assertEqual("FileType('r')", repr(type)) 1530 1531 def test_wb_1(self): 1532 type = argparse.FileType('wb', 1) 1533 self.assertEqual("FileType('wb', 1)", repr(type)) 1534 1535 def test_r_latin(self): 1536 type = argparse.FileType('r', encoding='latin_1') 1537 self.assertEqual("FileType('r', encoding='latin_1')", repr(type)) 1538 1539 def test_w_big5_ignore(self): 1540 type = argparse.FileType('w', encoding='big5', errors='ignore') 1541 self.assertEqual("FileType('w', encoding='big5', errors='ignore')", 1542 repr(type)) 1543 1544 def test_r_1_replace(self): 1545 type = argparse.FileType('r', 1, errors='replace') 1546 self.assertEqual("FileType('r', 1, errors='replace')", repr(type)) 1547 1548class StdStreamComparer: 1549 def __init__(self, attr): 1550 self.attr = attr 1551 1552 def __eq__(self, other): 1553 return other == getattr(sys, self.attr) 1554 1555eq_stdin = StdStreamComparer('stdin') 1556eq_stdout = StdStreamComparer('stdout') 1557eq_stderr = StdStreamComparer('stderr') 1558 1559class RFile(object): 1560 seen = {} 1561 1562 def __init__(self, name): 1563 self.name = name 1564 1565 def __eq__(self, other): 1566 if other in self.seen: 1567 text = self.seen[other] 1568 else: 1569 text = self.seen[other] = other.read() 1570 other.close() 1571 if not isinstance(text, str): 1572 text = text.decode('ascii') 1573 return self.name == other.name == text 1574 1575 1576class TestFileTypeR(TempDirMixin, ParserTestCase): 1577 """Test the FileType option/argument type for reading files""" 1578 1579 def setUp(self): 1580 super(TestFileTypeR, self).setUp() 1581 for file_name in ['foo', 'bar']: 1582 with open(os.path.join(self.temp_dir, file_name), 'w') as file: 1583 file.write(file_name) 1584 self.create_readonly_file('readonly') 1585 1586 argument_signatures = [ 1587 Sig('-x', type=argparse.FileType()), 1588 Sig('spam', type=argparse.FileType('r')), 1589 ] 1590 failures = ['-x', '', 'non-existent-file.txt'] 1591 successes = [ 1592 ('foo', NS(x=None, spam=RFile('foo'))), 1593 ('-x foo bar', NS(x=RFile('foo'), spam=RFile('bar'))), 1594 ('bar -x foo', NS(x=RFile('foo'), spam=RFile('bar'))), 1595 ('-x - -', NS(x=eq_stdin, spam=eq_stdin)), 1596 ('readonly', NS(x=None, spam=RFile('readonly'))), 1597 ] 1598 1599class TestFileTypeDefaults(TempDirMixin, ParserTestCase): 1600 """Test that a file is not created unless the default is needed""" 1601 def setUp(self): 1602 super(TestFileTypeDefaults, self).setUp() 1603 file = open(os.path.join(self.temp_dir, 'good'), 'w') 1604 file.write('good') 1605 file.close() 1606 1607 argument_signatures = [ 1608 Sig('-c', type=argparse.FileType('r'), default='no-file.txt'), 1609 ] 1610 # should provoke no such file error 1611 failures = [''] 1612 # should not provoke error because default file is created 1613 successes = [('-c good', NS(c=RFile('good')))] 1614 1615 1616class TestFileTypeRB(TempDirMixin, ParserTestCase): 1617 """Test the FileType option/argument type for reading files""" 1618 1619 def setUp(self): 1620 super(TestFileTypeRB, self).setUp() 1621 for file_name in ['foo', 'bar']: 1622 with open(os.path.join(self.temp_dir, file_name), 'w') as file: 1623 file.write(file_name) 1624 1625 argument_signatures = [ 1626 Sig('-x', type=argparse.FileType('rb')), 1627 Sig('spam', type=argparse.FileType('rb')), 1628 ] 1629 failures = ['-x', ''] 1630 successes = [ 1631 ('foo', NS(x=None, spam=RFile('foo'))), 1632 ('-x foo bar', NS(x=RFile('foo'), spam=RFile('bar'))), 1633 ('bar -x foo', NS(x=RFile('foo'), spam=RFile('bar'))), 1634 ('-x - -', NS(x=eq_stdin, spam=eq_stdin)), 1635 ] 1636 1637 1638class WFile(object): 1639 seen = set() 1640 1641 def __init__(self, name): 1642 self.name = name 1643 1644 def __eq__(self, other): 1645 if other not in self.seen: 1646 text = 'Check that file is writable.' 1647 if 'b' in other.mode: 1648 text = text.encode('ascii') 1649 other.write(text) 1650 other.close() 1651 self.seen.add(other) 1652 return self.name == other.name 1653 1654 1655@unittest.skipIf(hasattr(os, 'geteuid') and os.geteuid() == 0, 1656 "non-root user required") 1657class TestFileTypeW(TempDirMixin, ParserTestCase): 1658 """Test the FileType option/argument type for writing files""" 1659 1660 def setUp(self): 1661 super(TestFileTypeW, self).setUp() 1662 self.create_readonly_file('readonly') 1663 1664 argument_signatures = [ 1665 Sig('-x', type=argparse.FileType('w')), 1666 Sig('spam', type=argparse.FileType('w')), 1667 ] 1668 failures = ['-x', '', 'readonly'] 1669 successes = [ 1670 ('foo', NS(x=None, spam=WFile('foo'))), 1671 ('-x foo bar', NS(x=WFile('foo'), spam=WFile('bar'))), 1672 ('bar -x foo', NS(x=WFile('foo'), spam=WFile('bar'))), 1673 ('-x - -', NS(x=eq_stdout, spam=eq_stdout)), 1674 ] 1675 1676 1677class TestFileTypeWB(TempDirMixin, ParserTestCase): 1678 1679 argument_signatures = [ 1680 Sig('-x', type=argparse.FileType('wb')), 1681 Sig('spam', type=argparse.FileType('wb')), 1682 ] 1683 failures = ['-x', ''] 1684 successes = [ 1685 ('foo', NS(x=None, spam=WFile('foo'))), 1686 ('-x foo bar', NS(x=WFile('foo'), spam=WFile('bar'))), 1687 ('bar -x foo', NS(x=WFile('foo'), spam=WFile('bar'))), 1688 ('-x - -', NS(x=eq_stdout, spam=eq_stdout)), 1689 ] 1690 1691 1692class TestFileTypeOpenArgs(TestCase): 1693 """Test that open (the builtin) is correctly called""" 1694 1695 def test_open_args(self): 1696 FT = argparse.FileType 1697 cases = [ 1698 (FT('rb'), ('rb', -1, None, None)), 1699 (FT('w', 1), ('w', 1, None, None)), 1700 (FT('w', errors='replace'), ('w', -1, None, 'replace')), 1701 (FT('wb', encoding='big5'), ('wb', -1, 'big5', None)), 1702 (FT('w', 0, 'l1', 'strict'), ('w', 0, 'l1', 'strict')), 1703 ] 1704 with mock.patch('builtins.open') as m: 1705 for type, args in cases: 1706 type('foo') 1707 m.assert_called_with('foo', *args) 1708 1709 1710class TestFileTypeMissingInitialization(TestCase): 1711 """ 1712 Test that add_argument throws an error if FileType class 1713 object was passed instead of instance of FileType 1714 """ 1715 1716 def test(self): 1717 parser = argparse.ArgumentParser() 1718 with self.assertRaises(ValueError) as cm: 1719 parser.add_argument('-x', type=argparse.FileType) 1720 1721 self.assertEqual( 1722 '%r is a FileType class object, instance of it must be passed' 1723 % (argparse.FileType,), 1724 str(cm.exception) 1725 ) 1726 1727 1728class TestTypeCallable(ParserTestCase): 1729 """Test some callables as option/argument types""" 1730 1731 argument_signatures = [ 1732 Sig('--eggs', type=complex), 1733 Sig('spam', type=float), 1734 ] 1735 failures = ['a', '42j', '--eggs a', '--eggs 2i'] 1736 successes = [ 1737 ('--eggs=42 42', NS(eggs=42, spam=42.0)), 1738 ('--eggs 2j -- -1.5', NS(eggs=2j, spam=-1.5)), 1739 ('1024.675', NS(eggs=None, spam=1024.675)), 1740 ] 1741 1742 1743class TestTypeUserDefined(ParserTestCase): 1744 """Test a user-defined option/argument type""" 1745 1746 class MyType(TestCase): 1747 1748 def __init__(self, value): 1749 self.value = value 1750 1751 def __eq__(self, other): 1752 return (type(self), self.value) == (type(other), other.value) 1753 1754 argument_signatures = [ 1755 Sig('-x', type=MyType), 1756 Sig('spam', type=MyType), 1757 ] 1758 failures = [] 1759 successes = [ 1760 ('a -x b', NS(x=MyType('b'), spam=MyType('a'))), 1761 ('-xf g', NS(x=MyType('f'), spam=MyType('g'))), 1762 ] 1763 1764 1765class TestTypeClassicClass(ParserTestCase): 1766 """Test a classic class type""" 1767 1768 class C: 1769 1770 def __init__(self, value): 1771 self.value = value 1772 1773 def __eq__(self, other): 1774 return (type(self), self.value) == (type(other), other.value) 1775 1776 argument_signatures = [ 1777 Sig('-x', type=C), 1778 Sig('spam', type=C), 1779 ] 1780 failures = [] 1781 successes = [ 1782 ('a -x b', NS(x=C('b'), spam=C('a'))), 1783 ('-xf g', NS(x=C('f'), spam=C('g'))), 1784 ] 1785 1786 1787class TestTypeRegistration(TestCase): 1788 """Test a user-defined type by registering it""" 1789 1790 def test(self): 1791 1792 def get_my_type(string): 1793 return 'my_type{%s}' % string 1794 1795 parser = argparse.ArgumentParser() 1796 parser.register('type', 'my_type', get_my_type) 1797 parser.add_argument('-x', type='my_type') 1798 parser.add_argument('y', type='my_type') 1799 1800 self.assertEqual(parser.parse_args('1'.split()), 1801 NS(x=None, y='my_type{1}')) 1802 self.assertEqual(parser.parse_args('-x 1 42'.split()), 1803 NS(x='my_type{1}', y='my_type{42}')) 1804 1805 1806# ============ 1807# Action tests 1808# ============ 1809 1810class TestActionUserDefined(ParserTestCase): 1811 """Test a user-defined option/argument action""" 1812 1813 class OptionalAction(argparse.Action): 1814 1815 def __call__(self, parser, namespace, value, option_string=None): 1816 try: 1817 # check destination and option string 1818 assert self.dest == 'spam', 'dest: %s' % self.dest 1819 assert option_string == '-s', 'flag: %s' % option_string 1820 # when option is before argument, badger=2, and when 1821 # option is after argument, badger=<whatever was set> 1822 expected_ns = NS(spam=0.25) 1823 if value in [0.125, 0.625]: 1824 expected_ns.badger = 2 1825 elif value in [2.0]: 1826 expected_ns.badger = 84 1827 else: 1828 raise AssertionError('value: %s' % value) 1829 assert expected_ns == namespace, ('expected %s, got %s' % 1830 (expected_ns, namespace)) 1831 except AssertionError: 1832 e = sys.exc_info()[1] 1833 raise ArgumentParserError('opt_action failed: %s' % e) 1834 setattr(namespace, 'spam', value) 1835 1836 class PositionalAction(argparse.Action): 1837 1838 def __call__(self, parser, namespace, value, option_string=None): 1839 try: 1840 assert option_string is None, ('option_string: %s' % 1841 option_string) 1842 # check destination 1843 assert self.dest == 'badger', 'dest: %s' % self.dest 1844 # when argument is before option, spam=0.25, and when 1845 # option is after argument, spam=<whatever was set> 1846 expected_ns = NS(badger=2) 1847 if value in [42, 84]: 1848 expected_ns.spam = 0.25 1849 elif value in [1]: 1850 expected_ns.spam = 0.625 1851 elif value in [2]: 1852 expected_ns.spam = 0.125 1853 else: 1854 raise AssertionError('value: %s' % value) 1855 assert expected_ns == namespace, ('expected %s, got %s' % 1856 (expected_ns, namespace)) 1857 except AssertionError: 1858 e = sys.exc_info()[1] 1859 raise ArgumentParserError('arg_action failed: %s' % e) 1860 setattr(namespace, 'badger', value) 1861 1862 argument_signatures = [ 1863 Sig('-s', dest='spam', action=OptionalAction, 1864 type=float, default=0.25), 1865 Sig('badger', action=PositionalAction, 1866 type=int, nargs='?', default=2), 1867 ] 1868 failures = [] 1869 successes = [ 1870 ('-s0.125', NS(spam=0.125, badger=2)), 1871 ('42', NS(spam=0.25, badger=42)), 1872 ('-s 0.625 1', NS(spam=0.625, badger=1)), 1873 ('84 -s2', NS(spam=2.0, badger=84)), 1874 ] 1875 1876 1877class TestActionRegistration(TestCase): 1878 """Test a user-defined action supplied by registering it""" 1879 1880 class MyAction(argparse.Action): 1881 1882 def __call__(self, parser, namespace, values, option_string=None): 1883 setattr(namespace, self.dest, 'foo[%s]' % values) 1884 1885 def test(self): 1886 1887 parser = argparse.ArgumentParser() 1888 parser.register('action', 'my_action', self.MyAction) 1889 parser.add_argument('badger', action='my_action') 1890 1891 self.assertEqual(parser.parse_args(['1']), NS(badger='foo[1]')) 1892 self.assertEqual(parser.parse_args(['42']), NS(badger='foo[42]')) 1893 1894 1895class TestActionExtend(ParserTestCase): 1896 argument_signatures = [ 1897 Sig('--foo', action="extend", nargs="+", type=str), 1898 ] 1899 failures = () 1900 successes = [ 1901 ('--foo f1 --foo f2 f3 f4', NS(foo=['f1', 'f2', 'f3', 'f4'])), 1902 ] 1903 1904# ================ 1905# Subparsers tests 1906# ================ 1907 1908class TestAddSubparsers(TestCase): 1909 """Test the add_subparsers method""" 1910 1911 def assertArgumentParserError(self, *args, **kwargs): 1912 self.assertRaises(ArgumentParserError, *args, **kwargs) 1913 1914 def _get_parser(self, subparser_help=False, prefix_chars=None, 1915 aliases=False): 1916 # create a parser with a subparsers argument 1917 if prefix_chars: 1918 parser = ErrorRaisingArgumentParser( 1919 prog='PROG', description='main description', prefix_chars=prefix_chars) 1920 parser.add_argument( 1921 prefix_chars[0] * 2 + 'foo', action='store_true', help='foo help') 1922 else: 1923 parser = ErrorRaisingArgumentParser( 1924 prog='PROG', description='main description') 1925 parser.add_argument( 1926 '--foo', action='store_true', help='foo help') 1927 parser.add_argument( 1928 'bar', type=float, help='bar help') 1929 1930 # check that only one subparsers argument can be added 1931 subparsers_kwargs = {'required': False} 1932 if aliases: 1933 subparsers_kwargs['metavar'] = 'COMMAND' 1934 subparsers_kwargs['title'] = 'commands' 1935 else: 1936 subparsers_kwargs['help'] = 'command help' 1937 subparsers = parser.add_subparsers(**subparsers_kwargs) 1938 self.assertArgumentParserError(parser.add_subparsers) 1939 1940 # add first sub-parser 1941 parser1_kwargs = dict(description='1 description') 1942 if subparser_help: 1943 parser1_kwargs['help'] = '1 help' 1944 if aliases: 1945 parser1_kwargs['aliases'] = ['1alias1', '1alias2'] 1946 parser1 = subparsers.add_parser('1', **parser1_kwargs) 1947 parser1.add_argument('-w', type=int, help='w help') 1948 parser1.add_argument('x', choices='abc', help='x help') 1949 1950 # add second sub-parser 1951 parser2_kwargs = dict(description='2 description') 1952 if subparser_help: 1953 parser2_kwargs['help'] = '2 help' 1954 parser2 = subparsers.add_parser('2', **parser2_kwargs) 1955 parser2.add_argument('-y', choices='123', help='y help') 1956 parser2.add_argument('z', type=complex, nargs='*', help='z help') 1957 1958 # add third sub-parser 1959 parser3_kwargs = dict(description='3 description') 1960 if subparser_help: 1961 parser3_kwargs['help'] = '3 help' 1962 parser3 = subparsers.add_parser('3', **parser3_kwargs) 1963 parser3.add_argument('t', type=int, help='t help') 1964 parser3.add_argument('u', nargs='...', help='u help') 1965 1966 # return the main parser 1967 return parser 1968 1969 def setUp(self): 1970 super().setUp() 1971 self.parser = self._get_parser() 1972 self.command_help_parser = self._get_parser(subparser_help=True) 1973 1974 def test_parse_args_failures(self): 1975 # check some failure cases: 1976 for args_str in ['', 'a', 'a a', '0.5 a', '0.5 1', 1977 '0.5 1 -y', '0.5 2 -w']: 1978 args = args_str.split() 1979 self.assertArgumentParserError(self.parser.parse_args, args) 1980 1981 def test_parse_args(self): 1982 # check some non-failure cases: 1983 self.assertEqual( 1984 self.parser.parse_args('0.5 1 b -w 7'.split()), 1985 NS(foo=False, bar=0.5, w=7, x='b'), 1986 ) 1987 self.assertEqual( 1988 self.parser.parse_args('0.25 --foo 2 -y 2 3j -- -1j'.split()), 1989 NS(foo=True, bar=0.25, y='2', z=[3j, -1j]), 1990 ) 1991 self.assertEqual( 1992 self.parser.parse_args('--foo 0.125 1 c'.split()), 1993 NS(foo=True, bar=0.125, w=None, x='c'), 1994 ) 1995 self.assertEqual( 1996 self.parser.parse_args('-1.5 3 11 -- a --foo 7 -- b'.split()), 1997 NS(foo=False, bar=-1.5, t=11, u=['a', '--foo', '7', '--', 'b']), 1998 ) 1999 2000 def test_parse_known_args(self): 2001 self.assertEqual( 2002 self.parser.parse_known_args('0.5 1 b -w 7'.split()), 2003 (NS(foo=False, bar=0.5, w=7, x='b'), []), 2004 ) 2005 self.assertEqual( 2006 self.parser.parse_known_args('0.5 -p 1 b -w 7'.split()), 2007 (NS(foo=False, bar=0.5, w=7, x='b'), ['-p']), 2008 ) 2009 self.assertEqual( 2010 self.parser.parse_known_args('0.5 1 b -w 7 -p'.split()), 2011 (NS(foo=False, bar=0.5, w=7, x='b'), ['-p']), 2012 ) 2013 self.assertEqual( 2014 self.parser.parse_known_args('0.5 1 b -q -rs -w 7'.split()), 2015 (NS(foo=False, bar=0.5, w=7, x='b'), ['-q', '-rs']), 2016 ) 2017 self.assertEqual( 2018 self.parser.parse_known_args('0.5 -W 1 b -X Y -w 7 Z'.split()), 2019 (NS(foo=False, bar=0.5, w=7, x='b'), ['-W', '-X', 'Y', 'Z']), 2020 ) 2021 2022 def test_dest(self): 2023 parser = ErrorRaisingArgumentParser() 2024 parser.add_argument('--foo', action='store_true') 2025 subparsers = parser.add_subparsers(dest='bar') 2026 parser1 = subparsers.add_parser('1') 2027 parser1.add_argument('baz') 2028 self.assertEqual(NS(foo=False, bar='1', baz='2'), 2029 parser.parse_args('1 2'.split())) 2030 2031 def _test_required_subparsers(self, parser): 2032 # Should parse the sub command 2033 ret = parser.parse_args(['run']) 2034 self.assertEqual(ret.command, 'run') 2035 2036 # Error when the command is missing 2037 self.assertArgumentParserError(parser.parse_args, ()) 2038 2039 def test_required_subparsers_via_attribute(self): 2040 parser = ErrorRaisingArgumentParser() 2041 subparsers = parser.add_subparsers(dest='command') 2042 subparsers.required = True 2043 subparsers.add_parser('run') 2044 self._test_required_subparsers(parser) 2045 2046 def test_required_subparsers_via_kwarg(self): 2047 parser = ErrorRaisingArgumentParser() 2048 subparsers = parser.add_subparsers(dest='command', required=True) 2049 subparsers.add_parser('run') 2050 self._test_required_subparsers(parser) 2051 2052 def test_required_subparsers_default(self): 2053 parser = ErrorRaisingArgumentParser() 2054 subparsers = parser.add_subparsers(dest='command') 2055 subparsers.add_parser('run') 2056 # No error here 2057 ret = parser.parse_args(()) 2058 self.assertIsNone(ret.command) 2059 2060 def test_optional_subparsers(self): 2061 parser = ErrorRaisingArgumentParser() 2062 subparsers = parser.add_subparsers(dest='command', required=False) 2063 subparsers.add_parser('run') 2064 # No error here 2065 ret = parser.parse_args(()) 2066 self.assertIsNone(ret.command) 2067 2068 def test_help(self): 2069 self.assertEqual(self.parser.format_usage(), 2070 'usage: PROG [-h] [--foo] bar {1,2,3} ...\n') 2071 self.assertEqual(self.parser.format_help(), textwrap.dedent('''\ 2072 usage: PROG [-h] [--foo] bar {1,2,3} ... 2073 2074 main description 2075 2076 positional arguments: 2077 bar bar help 2078 {1,2,3} command help 2079 2080 optional arguments: 2081 -h, --help show this help message and exit 2082 --foo foo help 2083 ''')) 2084 2085 def test_help_extra_prefix_chars(self): 2086 # Make sure - is still used for help if it is a non-first prefix char 2087 parser = self._get_parser(prefix_chars='+:-') 2088 self.assertEqual(parser.format_usage(), 2089 'usage: PROG [-h] [++foo] bar {1,2,3} ...\n') 2090 self.assertEqual(parser.format_help(), textwrap.dedent('''\ 2091 usage: PROG [-h] [++foo] bar {1,2,3} ... 2092 2093 main description 2094 2095 positional arguments: 2096 bar bar help 2097 {1,2,3} command help 2098 2099 optional arguments: 2100 -h, --help show this help message and exit 2101 ++foo foo help 2102 ''')) 2103 2104 def test_help_non_breaking_spaces(self): 2105 parser = ErrorRaisingArgumentParser( 2106 prog='PROG', description='main description') 2107 parser.add_argument( 2108 "--non-breaking", action='store_false', 2109 help='help message containing non-breaking spaces shall not ' 2110 'wrap\N{NO-BREAK SPACE}at non-breaking spaces') 2111 self.assertEqual(parser.format_help(), textwrap.dedent('''\ 2112 usage: PROG [-h] [--non-breaking] 2113 2114 main description 2115 2116 optional arguments: 2117 -h, --help show this help message and exit 2118 --non-breaking help message containing non-breaking spaces shall not 2119 wrap\N{NO-BREAK SPACE}at non-breaking spaces 2120 ''')) 2121 2122 def test_help_alternate_prefix_chars(self): 2123 parser = self._get_parser(prefix_chars='+:/') 2124 self.assertEqual(parser.format_usage(), 2125 'usage: PROG [+h] [++foo] bar {1,2,3} ...\n') 2126 self.assertEqual(parser.format_help(), textwrap.dedent('''\ 2127 usage: PROG [+h] [++foo] bar {1,2,3} ... 2128 2129 main description 2130 2131 positional arguments: 2132 bar bar help 2133 {1,2,3} command help 2134 2135 optional arguments: 2136 +h, ++help show this help message and exit 2137 ++foo foo help 2138 ''')) 2139 2140 def test_parser_command_help(self): 2141 self.assertEqual(self.command_help_parser.format_usage(), 2142 'usage: PROG [-h] [--foo] bar {1,2,3} ...\n') 2143 self.assertEqual(self.command_help_parser.format_help(), 2144 textwrap.dedent('''\ 2145 usage: PROG [-h] [--foo] bar {1,2,3} ... 2146 2147 main description 2148 2149 positional arguments: 2150 bar bar help 2151 {1,2,3} command help 2152 1 1 help 2153 2 2 help 2154 3 3 help 2155 2156 optional arguments: 2157 -h, --help show this help message and exit 2158 --foo foo help 2159 ''')) 2160 2161 def test_subparser_title_help(self): 2162 parser = ErrorRaisingArgumentParser(prog='PROG', 2163 description='main description') 2164 parser.add_argument('--foo', action='store_true', help='foo help') 2165 parser.add_argument('bar', help='bar help') 2166 subparsers = parser.add_subparsers(title='subcommands', 2167 description='command help', 2168 help='additional text') 2169 parser1 = subparsers.add_parser('1') 2170 parser2 = subparsers.add_parser('2') 2171 self.assertEqual(parser.format_usage(), 2172 'usage: PROG [-h] [--foo] bar {1,2} ...\n') 2173 self.assertEqual(parser.format_help(), textwrap.dedent('''\ 2174 usage: PROG [-h] [--foo] bar {1,2} ... 2175 2176 main description 2177 2178 positional arguments: 2179 bar bar help 2180 2181 optional arguments: 2182 -h, --help show this help message and exit 2183 --foo foo help 2184 2185 subcommands: 2186 command help 2187 2188 {1,2} additional text 2189 ''')) 2190 2191 def _test_subparser_help(self, args_str, expected_help): 2192 with self.assertRaises(ArgumentParserError) as cm: 2193 self.parser.parse_args(args_str.split()) 2194 self.assertEqual(expected_help, cm.exception.stdout) 2195 2196 def test_subparser1_help(self): 2197 self._test_subparser_help('5.0 1 -h', textwrap.dedent('''\ 2198 usage: PROG bar 1 [-h] [-w W] {a,b,c} 2199 2200 1 description 2201 2202 positional arguments: 2203 {a,b,c} x help 2204 2205 optional arguments: 2206 -h, --help show this help message and exit 2207 -w W w help 2208 ''')) 2209 2210 def test_subparser2_help(self): 2211 self._test_subparser_help('5.0 2 -h', textwrap.dedent('''\ 2212 usage: PROG bar 2 [-h] [-y {1,2,3}] [z ...] 2213 2214 2 description 2215 2216 positional arguments: 2217 z z help 2218 2219 optional arguments: 2220 -h, --help show this help message and exit 2221 -y {1,2,3} y help 2222 ''')) 2223 2224 def test_alias_invocation(self): 2225 parser = self._get_parser(aliases=True) 2226 self.assertEqual( 2227 parser.parse_known_args('0.5 1alias1 b'.split()), 2228 (NS(foo=False, bar=0.5, w=None, x='b'), []), 2229 ) 2230 self.assertEqual( 2231 parser.parse_known_args('0.5 1alias2 b'.split()), 2232 (NS(foo=False, bar=0.5, w=None, x='b'), []), 2233 ) 2234 2235 def test_error_alias_invocation(self): 2236 parser = self._get_parser(aliases=True) 2237 self.assertArgumentParserError(parser.parse_args, 2238 '0.5 1alias3 b'.split()) 2239 2240 def test_alias_help(self): 2241 parser = self._get_parser(aliases=True, subparser_help=True) 2242 self.maxDiff = None 2243 self.assertEqual(parser.format_help(), textwrap.dedent("""\ 2244 usage: PROG [-h] [--foo] bar COMMAND ... 2245 2246 main description 2247 2248 positional arguments: 2249 bar bar help 2250 2251 optional arguments: 2252 -h, --help show this help message and exit 2253 --foo foo help 2254 2255 commands: 2256 COMMAND 2257 1 (1alias1, 1alias2) 2258 1 help 2259 2 2 help 2260 3 3 help 2261 """)) 2262 2263# ============ 2264# Groups tests 2265# ============ 2266 2267class TestPositionalsGroups(TestCase): 2268 """Tests that order of group positionals matches construction order""" 2269 2270 def test_nongroup_first(self): 2271 parser = ErrorRaisingArgumentParser() 2272 parser.add_argument('foo') 2273 group = parser.add_argument_group('g') 2274 group.add_argument('bar') 2275 parser.add_argument('baz') 2276 expected = NS(foo='1', bar='2', baz='3') 2277 result = parser.parse_args('1 2 3'.split()) 2278 self.assertEqual(expected, result) 2279 2280 def test_group_first(self): 2281 parser = ErrorRaisingArgumentParser() 2282 group = parser.add_argument_group('xxx') 2283 group.add_argument('foo') 2284 parser.add_argument('bar') 2285 parser.add_argument('baz') 2286 expected = NS(foo='1', bar='2', baz='3') 2287 result = parser.parse_args('1 2 3'.split()) 2288 self.assertEqual(expected, result) 2289 2290 def test_interleaved_groups(self): 2291 parser = ErrorRaisingArgumentParser() 2292 group = parser.add_argument_group('xxx') 2293 parser.add_argument('foo') 2294 group.add_argument('bar') 2295 parser.add_argument('baz') 2296 group = parser.add_argument_group('yyy') 2297 group.add_argument('frell') 2298 expected = NS(foo='1', bar='2', baz='3', frell='4') 2299 result = parser.parse_args('1 2 3 4'.split()) 2300 self.assertEqual(expected, result) 2301 2302# =================== 2303# Parent parser tests 2304# =================== 2305 2306class TestParentParsers(TestCase): 2307 """Tests that parsers can be created with parent parsers""" 2308 2309 def assertArgumentParserError(self, *args, **kwargs): 2310 self.assertRaises(ArgumentParserError, *args, **kwargs) 2311 2312 def setUp(self): 2313 super().setUp() 2314 self.wxyz_parent = ErrorRaisingArgumentParser(add_help=False) 2315 self.wxyz_parent.add_argument('--w') 2316 x_group = self.wxyz_parent.add_argument_group('x') 2317 x_group.add_argument('-y') 2318 self.wxyz_parent.add_argument('z') 2319 2320 self.abcd_parent = ErrorRaisingArgumentParser(add_help=False) 2321 self.abcd_parent.add_argument('a') 2322 self.abcd_parent.add_argument('-b') 2323 c_group = self.abcd_parent.add_argument_group('c') 2324 c_group.add_argument('--d') 2325 2326 self.w_parent = ErrorRaisingArgumentParser(add_help=False) 2327 self.w_parent.add_argument('--w') 2328 2329 self.z_parent = ErrorRaisingArgumentParser(add_help=False) 2330 self.z_parent.add_argument('z') 2331 2332 # parents with mutually exclusive groups 2333 self.ab_mutex_parent = ErrorRaisingArgumentParser(add_help=False) 2334 group = self.ab_mutex_parent.add_mutually_exclusive_group() 2335 group.add_argument('-a', action='store_true') 2336 group.add_argument('-b', action='store_true') 2337 2338 self.main_program = os.path.basename(sys.argv[0]) 2339 2340 def test_single_parent(self): 2341 parser = ErrorRaisingArgumentParser(parents=[self.wxyz_parent]) 2342 self.assertEqual(parser.parse_args('-y 1 2 --w 3'.split()), 2343 NS(w='3', y='1', z='2')) 2344 2345 def test_single_parent_mutex(self): 2346 self._test_mutex_ab(self.ab_mutex_parent.parse_args) 2347 parser = ErrorRaisingArgumentParser(parents=[self.ab_mutex_parent]) 2348 self._test_mutex_ab(parser.parse_args) 2349 2350 def test_single_granparent_mutex(self): 2351 parents = [self.ab_mutex_parent] 2352 parser = ErrorRaisingArgumentParser(add_help=False, parents=parents) 2353 parser = ErrorRaisingArgumentParser(parents=[parser]) 2354 self._test_mutex_ab(parser.parse_args) 2355 2356 def _test_mutex_ab(self, parse_args): 2357 self.assertEqual(parse_args([]), NS(a=False, b=False)) 2358 self.assertEqual(parse_args(['-a']), NS(a=True, b=False)) 2359 self.assertEqual(parse_args(['-b']), NS(a=False, b=True)) 2360 self.assertArgumentParserError(parse_args, ['-a', '-b']) 2361 self.assertArgumentParserError(parse_args, ['-b', '-a']) 2362 self.assertArgumentParserError(parse_args, ['-c']) 2363 self.assertArgumentParserError(parse_args, ['-a', '-c']) 2364 self.assertArgumentParserError(parse_args, ['-b', '-c']) 2365 2366 def test_multiple_parents(self): 2367 parents = [self.abcd_parent, self.wxyz_parent] 2368 parser = ErrorRaisingArgumentParser(parents=parents) 2369 self.assertEqual(parser.parse_args('--d 1 --w 2 3 4'.split()), 2370 NS(a='3', b=None, d='1', w='2', y=None, z='4')) 2371 2372 def test_multiple_parents_mutex(self): 2373 parents = [self.ab_mutex_parent, self.wxyz_parent] 2374 parser = ErrorRaisingArgumentParser(parents=parents) 2375 self.assertEqual(parser.parse_args('-a --w 2 3'.split()), 2376 NS(a=True, b=False, w='2', y=None, z='3')) 2377 self.assertArgumentParserError( 2378 parser.parse_args, '-a --w 2 3 -b'.split()) 2379 self.assertArgumentParserError( 2380 parser.parse_args, '-a -b --w 2 3'.split()) 2381 2382 def test_conflicting_parents(self): 2383 self.assertRaises( 2384 argparse.ArgumentError, 2385 argparse.ArgumentParser, 2386 parents=[self.w_parent, self.wxyz_parent]) 2387 2388 def test_conflicting_parents_mutex(self): 2389 self.assertRaises( 2390 argparse.ArgumentError, 2391 argparse.ArgumentParser, 2392 parents=[self.abcd_parent, self.ab_mutex_parent]) 2393 2394 def test_same_argument_name_parents(self): 2395 parents = [self.wxyz_parent, self.z_parent] 2396 parser = ErrorRaisingArgumentParser(parents=parents) 2397 self.assertEqual(parser.parse_args('1 2'.split()), 2398 NS(w=None, y=None, z='2')) 2399 2400 def test_subparser_parents(self): 2401 parser = ErrorRaisingArgumentParser() 2402 subparsers = parser.add_subparsers() 2403 abcde_parser = subparsers.add_parser('bar', parents=[self.abcd_parent]) 2404 abcde_parser.add_argument('e') 2405 self.assertEqual(parser.parse_args('bar -b 1 --d 2 3 4'.split()), 2406 NS(a='3', b='1', d='2', e='4')) 2407 2408 def test_subparser_parents_mutex(self): 2409 parser = ErrorRaisingArgumentParser() 2410 subparsers = parser.add_subparsers() 2411 parents = [self.ab_mutex_parent] 2412 abc_parser = subparsers.add_parser('foo', parents=parents) 2413 c_group = abc_parser.add_argument_group('c_group') 2414 c_group.add_argument('c') 2415 parents = [self.wxyz_parent, self.ab_mutex_parent] 2416 wxyzabe_parser = subparsers.add_parser('bar', parents=parents) 2417 wxyzabe_parser.add_argument('e') 2418 self.assertEqual(parser.parse_args('foo -a 4'.split()), 2419 NS(a=True, b=False, c='4')) 2420 self.assertEqual(parser.parse_args('bar -b --w 2 3 4'.split()), 2421 NS(a=False, b=True, w='2', y=None, z='3', e='4')) 2422 self.assertArgumentParserError( 2423 parser.parse_args, 'foo -a -b 4'.split()) 2424 self.assertArgumentParserError( 2425 parser.parse_args, 'bar -b -a 4'.split()) 2426 2427 def test_parent_help(self): 2428 parents = [self.abcd_parent, self.wxyz_parent] 2429 parser = ErrorRaisingArgumentParser(parents=parents) 2430 parser_help = parser.format_help() 2431 progname = self.main_program 2432 self.assertEqual(parser_help, textwrap.dedent('''\ 2433 usage: {}{}[-h] [-b B] [--d D] [--w W] [-y Y] a z 2434 2435 positional arguments: 2436 a 2437 z 2438 2439 optional arguments: 2440 -h, --help show this help message and exit 2441 -b B 2442 --w W 2443 2444 c: 2445 --d D 2446 2447 x: 2448 -y Y 2449 '''.format(progname, ' ' if progname else '' ))) 2450 2451 def test_groups_parents(self): 2452 parent = ErrorRaisingArgumentParser(add_help=False) 2453 g = parent.add_argument_group(title='g', description='gd') 2454 g.add_argument('-w') 2455 g.add_argument('-x') 2456 m = parent.add_mutually_exclusive_group() 2457 m.add_argument('-y') 2458 m.add_argument('-z') 2459 parser = ErrorRaisingArgumentParser(parents=[parent]) 2460 2461 self.assertRaises(ArgumentParserError, parser.parse_args, 2462 ['-y', 'Y', '-z', 'Z']) 2463 2464 parser_help = parser.format_help() 2465 progname = self.main_program 2466 self.assertEqual(parser_help, textwrap.dedent('''\ 2467 usage: {}{}[-h] [-w W] [-x X] [-y Y | -z Z] 2468 2469 optional arguments: 2470 -h, --help show this help message and exit 2471 -y Y 2472 -z Z 2473 2474 g: 2475 gd 2476 2477 -w W 2478 -x X 2479 '''.format(progname, ' ' if progname else '' ))) 2480 2481# ============================== 2482# Mutually exclusive group tests 2483# ============================== 2484 2485class TestMutuallyExclusiveGroupErrors(TestCase): 2486 2487 def test_invalid_add_argument_group(self): 2488 parser = ErrorRaisingArgumentParser() 2489 raises = self.assertRaises 2490 raises(TypeError, parser.add_mutually_exclusive_group, title='foo') 2491 2492 def test_invalid_add_argument(self): 2493 parser = ErrorRaisingArgumentParser() 2494 group = parser.add_mutually_exclusive_group() 2495 add_argument = group.add_argument 2496 raises = self.assertRaises 2497 raises(ValueError, add_argument, '--foo', required=True) 2498 raises(ValueError, add_argument, 'bar') 2499 raises(ValueError, add_argument, 'bar', nargs='+') 2500 raises(ValueError, add_argument, 'bar', nargs=1) 2501 raises(ValueError, add_argument, 'bar', nargs=argparse.PARSER) 2502 2503 def test_help(self): 2504 parser = ErrorRaisingArgumentParser(prog='PROG') 2505 group1 = parser.add_mutually_exclusive_group() 2506 group1.add_argument('--foo', action='store_true') 2507 group1.add_argument('--bar', action='store_false') 2508 group2 = parser.add_mutually_exclusive_group() 2509 group2.add_argument('--soup', action='store_true') 2510 group2.add_argument('--nuts', action='store_false') 2511 expected = '''\ 2512 usage: PROG [-h] [--foo | --bar] [--soup | --nuts] 2513 2514 optional arguments: 2515 -h, --help show this help message and exit 2516 --foo 2517 --bar 2518 --soup 2519 --nuts 2520 ''' 2521 self.assertEqual(parser.format_help(), textwrap.dedent(expected)) 2522 2523class MEMixin(object): 2524 2525 def test_failures_when_not_required(self): 2526 parse_args = self.get_parser(required=False).parse_args 2527 error = ArgumentParserError 2528 for args_string in self.failures: 2529 self.assertRaises(error, parse_args, args_string.split()) 2530 2531 def test_failures_when_required(self): 2532 parse_args = self.get_parser(required=True).parse_args 2533 error = ArgumentParserError 2534 for args_string in self.failures + ['']: 2535 self.assertRaises(error, parse_args, args_string.split()) 2536 2537 def test_successes_when_not_required(self): 2538 parse_args = self.get_parser(required=False).parse_args 2539 successes = self.successes + self.successes_when_not_required 2540 for args_string, expected_ns in successes: 2541 actual_ns = parse_args(args_string.split()) 2542 self.assertEqual(actual_ns, expected_ns) 2543 2544 def test_successes_when_required(self): 2545 parse_args = self.get_parser(required=True).parse_args 2546 for args_string, expected_ns in self.successes: 2547 actual_ns = parse_args(args_string.split()) 2548 self.assertEqual(actual_ns, expected_ns) 2549 2550 def test_usage_when_not_required(self): 2551 format_usage = self.get_parser(required=False).format_usage 2552 expected_usage = self.usage_when_not_required 2553 self.assertEqual(format_usage(), textwrap.dedent(expected_usage)) 2554 2555 def test_usage_when_required(self): 2556 format_usage = self.get_parser(required=True).format_usage 2557 expected_usage = self.usage_when_required 2558 self.assertEqual(format_usage(), textwrap.dedent(expected_usage)) 2559 2560 def test_help_when_not_required(self): 2561 format_help = self.get_parser(required=False).format_help 2562 help = self.usage_when_not_required + self.help 2563 self.assertEqual(format_help(), textwrap.dedent(help)) 2564 2565 def test_help_when_required(self): 2566 format_help = self.get_parser(required=True).format_help 2567 help = self.usage_when_required + self.help 2568 self.assertEqual(format_help(), textwrap.dedent(help)) 2569 2570 2571class TestMutuallyExclusiveSimple(MEMixin, TestCase): 2572 2573 def get_parser(self, required=None): 2574 parser = ErrorRaisingArgumentParser(prog='PROG') 2575 group = parser.add_mutually_exclusive_group(required=required) 2576 group.add_argument('--bar', help='bar help') 2577 group.add_argument('--baz', nargs='?', const='Z', help='baz help') 2578 return parser 2579 2580 failures = ['--bar X --baz Y', '--bar X --baz'] 2581 successes = [ 2582 ('--bar X', NS(bar='X', baz=None)), 2583 ('--bar X --bar Z', NS(bar='Z', baz=None)), 2584 ('--baz Y', NS(bar=None, baz='Y')), 2585 ('--baz', NS(bar=None, baz='Z')), 2586 ] 2587 successes_when_not_required = [ 2588 ('', NS(bar=None, baz=None)), 2589 ] 2590 2591 usage_when_not_required = '''\ 2592 usage: PROG [-h] [--bar BAR | --baz [BAZ]] 2593 ''' 2594 usage_when_required = '''\ 2595 usage: PROG [-h] (--bar BAR | --baz [BAZ]) 2596 ''' 2597 help = '''\ 2598 2599 optional arguments: 2600 -h, --help show this help message and exit 2601 --bar BAR bar help 2602 --baz [BAZ] baz help 2603 ''' 2604 2605 2606class TestMutuallyExclusiveLong(MEMixin, TestCase): 2607 2608 def get_parser(self, required=None): 2609 parser = ErrorRaisingArgumentParser(prog='PROG') 2610 parser.add_argument('--abcde', help='abcde help') 2611 parser.add_argument('--fghij', help='fghij help') 2612 group = parser.add_mutually_exclusive_group(required=required) 2613 group.add_argument('--klmno', help='klmno help') 2614 group.add_argument('--pqrst', help='pqrst help') 2615 return parser 2616 2617 failures = ['--klmno X --pqrst Y'] 2618 successes = [ 2619 ('--klmno X', NS(abcde=None, fghij=None, klmno='X', pqrst=None)), 2620 ('--abcde Y --klmno X', 2621 NS(abcde='Y', fghij=None, klmno='X', pqrst=None)), 2622 ('--pqrst X', NS(abcde=None, fghij=None, klmno=None, pqrst='X')), 2623 ('--pqrst X --fghij Y', 2624 NS(abcde=None, fghij='Y', klmno=None, pqrst='X')), 2625 ] 2626 successes_when_not_required = [ 2627 ('', NS(abcde=None, fghij=None, klmno=None, pqrst=None)), 2628 ] 2629 2630 usage_when_not_required = '''\ 2631 usage: PROG [-h] [--abcde ABCDE] [--fghij FGHIJ] 2632 [--klmno KLMNO | --pqrst PQRST] 2633 ''' 2634 usage_when_required = '''\ 2635 usage: PROG [-h] [--abcde ABCDE] [--fghij FGHIJ] 2636 (--klmno KLMNO | --pqrst PQRST) 2637 ''' 2638 help = '''\ 2639 2640 optional arguments: 2641 -h, --help show this help message and exit 2642 --abcde ABCDE abcde help 2643 --fghij FGHIJ fghij help 2644 --klmno KLMNO klmno help 2645 --pqrst PQRST pqrst help 2646 ''' 2647 2648 2649class TestMutuallyExclusiveFirstSuppressed(MEMixin, TestCase): 2650 2651 def get_parser(self, required): 2652 parser = ErrorRaisingArgumentParser(prog='PROG') 2653 group = parser.add_mutually_exclusive_group(required=required) 2654 group.add_argument('-x', help=argparse.SUPPRESS) 2655 group.add_argument('-y', action='store_false', help='y help') 2656 return parser 2657 2658 failures = ['-x X -y'] 2659 successes = [ 2660 ('-x X', NS(x='X', y=True)), 2661 ('-x X -x Y', NS(x='Y', y=True)), 2662 ('-y', NS(x=None, y=False)), 2663 ] 2664 successes_when_not_required = [ 2665 ('', NS(x=None, y=True)), 2666 ] 2667 2668 usage_when_not_required = '''\ 2669 usage: PROG [-h] [-y] 2670 ''' 2671 usage_when_required = '''\ 2672 usage: PROG [-h] -y 2673 ''' 2674 help = '''\ 2675 2676 optional arguments: 2677 -h, --help show this help message and exit 2678 -y y help 2679 ''' 2680 2681 2682class TestMutuallyExclusiveManySuppressed(MEMixin, TestCase): 2683 2684 def get_parser(self, required): 2685 parser = ErrorRaisingArgumentParser(prog='PROG') 2686 group = parser.add_mutually_exclusive_group(required=required) 2687 add = group.add_argument 2688 add('--spam', action='store_true', help=argparse.SUPPRESS) 2689 add('--badger', action='store_false', help=argparse.SUPPRESS) 2690 add('--bladder', help=argparse.SUPPRESS) 2691 return parser 2692 2693 failures = [ 2694 '--spam --badger', 2695 '--badger --bladder B', 2696 '--bladder B --spam', 2697 ] 2698 successes = [ 2699 ('--spam', NS(spam=True, badger=True, bladder=None)), 2700 ('--badger', NS(spam=False, badger=False, bladder=None)), 2701 ('--bladder B', NS(spam=False, badger=True, bladder='B')), 2702 ('--spam --spam', NS(spam=True, badger=True, bladder=None)), 2703 ] 2704 successes_when_not_required = [ 2705 ('', NS(spam=False, badger=True, bladder=None)), 2706 ] 2707 2708 usage_when_required = usage_when_not_required = '''\ 2709 usage: PROG [-h] 2710 ''' 2711 help = '''\ 2712 2713 optional arguments: 2714 -h, --help show this help message and exit 2715 ''' 2716 2717 2718class TestMutuallyExclusiveOptionalAndPositional(MEMixin, TestCase): 2719 2720 def get_parser(self, required): 2721 parser = ErrorRaisingArgumentParser(prog='PROG') 2722 group = parser.add_mutually_exclusive_group(required=required) 2723 group.add_argument('--foo', action='store_true', help='FOO') 2724 group.add_argument('--spam', help='SPAM') 2725 group.add_argument('badger', nargs='*', default='X', help='BADGER') 2726 return parser 2727 2728 failures = [ 2729 '--foo --spam S', 2730 '--spam S X', 2731 'X --foo', 2732 'X Y Z --spam S', 2733 '--foo X Y', 2734 ] 2735 successes = [ 2736 ('--foo', NS(foo=True, spam=None, badger='X')), 2737 ('--spam S', NS(foo=False, spam='S', badger='X')), 2738 ('X', NS(foo=False, spam=None, badger=['X'])), 2739 ('X Y Z', NS(foo=False, spam=None, badger=['X', 'Y', 'Z'])), 2740 ] 2741 successes_when_not_required = [ 2742 ('', NS(foo=False, spam=None, badger='X')), 2743 ] 2744 2745 usage_when_not_required = '''\ 2746 usage: PROG [-h] [--foo | --spam SPAM | badger ...] 2747 ''' 2748 usage_when_required = '''\ 2749 usage: PROG [-h] (--foo | --spam SPAM | badger ...) 2750 ''' 2751 help = '''\ 2752 2753 positional arguments: 2754 badger BADGER 2755 2756 optional arguments: 2757 -h, --help show this help message and exit 2758 --foo FOO 2759 --spam SPAM SPAM 2760 ''' 2761 2762 2763class TestMutuallyExclusiveOptionalsMixed(MEMixin, TestCase): 2764 2765 def get_parser(self, required): 2766 parser = ErrorRaisingArgumentParser(prog='PROG') 2767 parser.add_argument('-x', action='store_true', help='x help') 2768 group = parser.add_mutually_exclusive_group(required=required) 2769 group.add_argument('-a', action='store_true', help='a help') 2770 group.add_argument('-b', action='store_true', help='b help') 2771 parser.add_argument('-y', action='store_true', help='y help') 2772 group.add_argument('-c', action='store_true', help='c help') 2773 return parser 2774 2775 failures = ['-a -b', '-b -c', '-a -c', '-a -b -c'] 2776 successes = [ 2777 ('-a', NS(a=True, b=False, c=False, x=False, y=False)), 2778 ('-b', NS(a=False, b=True, c=False, x=False, y=False)), 2779 ('-c', NS(a=False, b=False, c=True, x=False, y=False)), 2780 ('-a -x', NS(a=True, b=False, c=False, x=True, y=False)), 2781 ('-y -b', NS(a=False, b=True, c=False, x=False, y=True)), 2782 ('-x -y -c', NS(a=False, b=False, c=True, x=True, y=True)), 2783 ] 2784 successes_when_not_required = [ 2785 ('', NS(a=False, b=False, c=False, x=False, y=False)), 2786 ('-x', NS(a=False, b=False, c=False, x=True, y=False)), 2787 ('-y', NS(a=False, b=False, c=False, x=False, y=True)), 2788 ] 2789 2790 usage_when_required = usage_when_not_required = '''\ 2791 usage: PROG [-h] [-x] [-a] [-b] [-y] [-c] 2792 ''' 2793 help = '''\ 2794 2795 optional arguments: 2796 -h, --help show this help message and exit 2797 -x x help 2798 -a a help 2799 -b b help 2800 -y y help 2801 -c c help 2802 ''' 2803 2804 2805class TestMutuallyExclusiveInGroup(MEMixin, TestCase): 2806 2807 def get_parser(self, required=None): 2808 parser = ErrorRaisingArgumentParser(prog='PROG') 2809 titled_group = parser.add_argument_group( 2810 title='Titled group', description='Group description') 2811 mutex_group = \ 2812 titled_group.add_mutually_exclusive_group(required=required) 2813 mutex_group.add_argument('--bar', help='bar help') 2814 mutex_group.add_argument('--baz', help='baz help') 2815 return parser 2816 2817 failures = ['--bar X --baz Y', '--baz X --bar Y'] 2818 successes = [ 2819 ('--bar X', NS(bar='X', baz=None)), 2820 ('--baz Y', NS(bar=None, baz='Y')), 2821 ] 2822 successes_when_not_required = [ 2823 ('', NS(bar=None, baz=None)), 2824 ] 2825 2826 usage_when_not_required = '''\ 2827 usage: PROG [-h] [--bar BAR | --baz BAZ] 2828 ''' 2829 usage_when_required = '''\ 2830 usage: PROG [-h] (--bar BAR | --baz BAZ) 2831 ''' 2832 help = '''\ 2833 2834 optional arguments: 2835 -h, --help show this help message and exit 2836 2837 Titled group: 2838 Group description 2839 2840 --bar BAR bar help 2841 --baz BAZ baz help 2842 ''' 2843 2844 2845class TestMutuallyExclusiveOptionalsAndPositionalsMixed(MEMixin, TestCase): 2846 2847 def get_parser(self, required): 2848 parser = ErrorRaisingArgumentParser(prog='PROG') 2849 parser.add_argument('x', help='x help') 2850 parser.add_argument('-y', action='store_true', help='y help') 2851 group = parser.add_mutually_exclusive_group(required=required) 2852 group.add_argument('a', nargs='?', help='a help') 2853 group.add_argument('-b', action='store_true', help='b help') 2854 group.add_argument('-c', action='store_true', help='c help') 2855 return parser 2856 2857 failures = ['X A -b', '-b -c', '-c X A'] 2858 successes = [ 2859 ('X A', NS(a='A', b=False, c=False, x='X', y=False)), 2860 ('X -b', NS(a=None, b=True, c=False, x='X', y=False)), 2861 ('X -c', NS(a=None, b=False, c=True, x='X', y=False)), 2862 ('X A -y', NS(a='A', b=False, c=False, x='X', y=True)), 2863 ('X -y -b', NS(a=None, b=True, c=False, x='X', y=True)), 2864 ] 2865 successes_when_not_required = [ 2866 ('X', NS(a=None, b=False, c=False, x='X', y=False)), 2867 ('X -y', NS(a=None, b=False, c=False, x='X', y=True)), 2868 ] 2869 2870 usage_when_required = usage_when_not_required = '''\ 2871 usage: PROG [-h] [-y] [-b] [-c] x [a] 2872 ''' 2873 help = '''\ 2874 2875 positional arguments: 2876 x x help 2877 a a help 2878 2879 optional arguments: 2880 -h, --help show this help message and exit 2881 -y y help 2882 -b b help 2883 -c c help 2884 ''' 2885 2886class TestMutuallyExclusiveNested(MEMixin, TestCase): 2887 2888 def get_parser(self, required): 2889 parser = ErrorRaisingArgumentParser(prog='PROG') 2890 group = parser.add_mutually_exclusive_group(required=required) 2891 group.add_argument('-a') 2892 group.add_argument('-b') 2893 group2 = group.add_mutually_exclusive_group(required=required) 2894 group2.add_argument('-c') 2895 group2.add_argument('-d') 2896 group3 = group2.add_mutually_exclusive_group(required=required) 2897 group3.add_argument('-e') 2898 group3.add_argument('-f') 2899 return parser 2900 2901 usage_when_not_required = '''\ 2902 usage: PROG [-h] [-a A | -b B | [-c C | -d D | [-e E | -f F]]] 2903 ''' 2904 usage_when_required = '''\ 2905 usage: PROG [-h] (-a A | -b B | (-c C | -d D | (-e E | -f F))) 2906 ''' 2907 2908 help = '''\ 2909 2910 optional arguments: 2911 -h, --help show this help message and exit 2912 -a A 2913 -b B 2914 -c C 2915 -d D 2916 -e E 2917 -f F 2918 ''' 2919 2920 # We are only interested in testing the behavior of format_usage(). 2921 test_failures_when_not_required = None 2922 test_failures_when_required = None 2923 test_successes_when_not_required = None 2924 test_successes_when_required = None 2925 2926# ================================================= 2927# Mutually exclusive group in parent parser tests 2928# ================================================= 2929 2930class MEPBase(object): 2931 2932 def get_parser(self, required=None): 2933 parent = super(MEPBase, self).get_parser(required=required) 2934 parser = ErrorRaisingArgumentParser( 2935 prog=parent.prog, add_help=False, parents=[parent]) 2936 return parser 2937 2938 2939class TestMutuallyExclusiveGroupErrorsParent( 2940 MEPBase, TestMutuallyExclusiveGroupErrors): 2941 pass 2942 2943 2944class TestMutuallyExclusiveSimpleParent( 2945 MEPBase, TestMutuallyExclusiveSimple): 2946 pass 2947 2948 2949class TestMutuallyExclusiveLongParent( 2950 MEPBase, TestMutuallyExclusiveLong): 2951 pass 2952 2953 2954class TestMutuallyExclusiveFirstSuppressedParent( 2955 MEPBase, TestMutuallyExclusiveFirstSuppressed): 2956 pass 2957 2958 2959class TestMutuallyExclusiveManySuppressedParent( 2960 MEPBase, TestMutuallyExclusiveManySuppressed): 2961 pass 2962 2963 2964class TestMutuallyExclusiveOptionalAndPositionalParent( 2965 MEPBase, TestMutuallyExclusiveOptionalAndPositional): 2966 pass 2967 2968 2969class TestMutuallyExclusiveOptionalsMixedParent( 2970 MEPBase, TestMutuallyExclusiveOptionalsMixed): 2971 pass 2972 2973 2974class TestMutuallyExclusiveOptionalsAndPositionalsMixedParent( 2975 MEPBase, TestMutuallyExclusiveOptionalsAndPositionalsMixed): 2976 pass 2977 2978# ================= 2979# Set default tests 2980# ================= 2981 2982class TestSetDefaults(TestCase): 2983 2984 def test_set_defaults_no_args(self): 2985 parser = ErrorRaisingArgumentParser() 2986 parser.set_defaults(x='foo') 2987 parser.set_defaults(y='bar', z=1) 2988 self.assertEqual(NS(x='foo', y='bar', z=1), 2989 parser.parse_args([])) 2990 self.assertEqual(NS(x='foo', y='bar', z=1), 2991 parser.parse_args([], NS())) 2992 self.assertEqual(NS(x='baz', y='bar', z=1), 2993 parser.parse_args([], NS(x='baz'))) 2994 self.assertEqual(NS(x='baz', y='bar', z=2), 2995 parser.parse_args([], NS(x='baz', z=2))) 2996 2997 def test_set_defaults_with_args(self): 2998 parser = ErrorRaisingArgumentParser() 2999 parser.set_defaults(x='foo', y='bar') 3000 parser.add_argument('-x', default='xfoox') 3001 self.assertEqual(NS(x='xfoox', y='bar'), 3002 parser.parse_args([])) 3003 self.assertEqual(NS(x='xfoox', y='bar'), 3004 parser.parse_args([], NS())) 3005 self.assertEqual(NS(x='baz', y='bar'), 3006 parser.parse_args([], NS(x='baz'))) 3007 self.assertEqual(NS(x='1', y='bar'), 3008 parser.parse_args('-x 1'.split())) 3009 self.assertEqual(NS(x='1', y='bar'), 3010 parser.parse_args('-x 1'.split(), NS())) 3011 self.assertEqual(NS(x='1', y='bar'), 3012 parser.parse_args('-x 1'.split(), NS(x='baz'))) 3013 3014 def test_set_defaults_subparsers(self): 3015 parser = ErrorRaisingArgumentParser() 3016 parser.set_defaults(x='foo') 3017 subparsers = parser.add_subparsers() 3018 parser_a = subparsers.add_parser('a') 3019 parser_a.set_defaults(y='bar') 3020 self.assertEqual(NS(x='foo', y='bar'), 3021 parser.parse_args('a'.split())) 3022 3023 def test_set_defaults_parents(self): 3024 parent = ErrorRaisingArgumentParser(add_help=False) 3025 parent.set_defaults(x='foo') 3026 parser = ErrorRaisingArgumentParser(parents=[parent]) 3027 self.assertEqual(NS(x='foo'), parser.parse_args([])) 3028 3029 def test_set_defaults_on_parent_and_subparser(self): 3030 parser = argparse.ArgumentParser() 3031 xparser = parser.add_subparsers().add_parser('X') 3032 parser.set_defaults(foo=1) 3033 xparser.set_defaults(foo=2) 3034 self.assertEqual(NS(foo=2), parser.parse_args(['X'])) 3035 3036 def test_set_defaults_same_as_add_argument(self): 3037 parser = ErrorRaisingArgumentParser() 3038 parser.set_defaults(w='W', x='X', y='Y', z='Z') 3039 parser.add_argument('-w') 3040 parser.add_argument('-x', default='XX') 3041 parser.add_argument('y', nargs='?') 3042 parser.add_argument('z', nargs='?', default='ZZ') 3043 3044 # defaults set previously 3045 self.assertEqual(NS(w='W', x='XX', y='Y', z='ZZ'), 3046 parser.parse_args([])) 3047 3048 # reset defaults 3049 parser.set_defaults(w='WW', x='X', y='YY', z='Z') 3050 self.assertEqual(NS(w='WW', x='X', y='YY', z='Z'), 3051 parser.parse_args([])) 3052 3053 def test_set_defaults_same_as_add_argument_group(self): 3054 parser = ErrorRaisingArgumentParser() 3055 parser.set_defaults(w='W', x='X', y='Y', z='Z') 3056 group = parser.add_argument_group('foo') 3057 group.add_argument('-w') 3058 group.add_argument('-x', default='XX') 3059 group.add_argument('y', nargs='?') 3060 group.add_argument('z', nargs='?', default='ZZ') 3061 3062 3063 # defaults set previously 3064 self.assertEqual(NS(w='W', x='XX', y='Y', z='ZZ'), 3065 parser.parse_args([])) 3066 3067 # reset defaults 3068 parser.set_defaults(w='WW', x='X', y='YY', z='Z') 3069 self.assertEqual(NS(w='WW', x='X', y='YY', z='Z'), 3070 parser.parse_args([])) 3071 3072# ================= 3073# Get default tests 3074# ================= 3075 3076class TestGetDefault(TestCase): 3077 3078 def test_get_default(self): 3079 parser = ErrorRaisingArgumentParser() 3080 self.assertIsNone(parser.get_default("foo")) 3081 self.assertIsNone(parser.get_default("bar")) 3082 3083 parser.add_argument("--foo") 3084 self.assertIsNone(parser.get_default("foo")) 3085 self.assertIsNone(parser.get_default("bar")) 3086 3087 parser.add_argument("--bar", type=int, default=42) 3088 self.assertIsNone(parser.get_default("foo")) 3089 self.assertEqual(42, parser.get_default("bar")) 3090 3091 parser.set_defaults(foo="badger") 3092 self.assertEqual("badger", parser.get_default("foo")) 3093 self.assertEqual(42, parser.get_default("bar")) 3094 3095# ========================== 3096# Namespace 'contains' tests 3097# ========================== 3098 3099class TestNamespaceContainsSimple(TestCase): 3100 3101 def test_empty(self): 3102 ns = argparse.Namespace() 3103 self.assertNotIn('', ns) 3104 self.assertNotIn('x', ns) 3105 3106 def test_non_empty(self): 3107 ns = argparse.Namespace(x=1, y=2) 3108 self.assertNotIn('', ns) 3109 self.assertIn('x', ns) 3110 self.assertIn('y', ns) 3111 self.assertNotIn('xx', ns) 3112 self.assertNotIn('z', ns) 3113 3114# ===================== 3115# Help formatting tests 3116# ===================== 3117 3118class TestHelpFormattingMetaclass(type): 3119 3120 def __init__(cls, name, bases, bodydict): 3121 if name == 'HelpTestCase': 3122 return 3123 3124 class AddTests(object): 3125 3126 def __init__(self, test_class, func_suffix, std_name): 3127 self.func_suffix = func_suffix 3128 self.std_name = std_name 3129 3130 for test_func in [self.test_format, 3131 self.test_print, 3132 self.test_print_file]: 3133 test_name = '%s_%s' % (test_func.__name__, func_suffix) 3134 3135 def test_wrapper(self, test_func=test_func): 3136 test_func(self) 3137 try: 3138 test_wrapper.__name__ = test_name 3139 except TypeError: 3140 pass 3141 setattr(test_class, test_name, test_wrapper) 3142 3143 def _get_parser(self, tester): 3144 parser = argparse.ArgumentParser( 3145 *tester.parser_signature.args, 3146 **tester.parser_signature.kwargs) 3147 for argument_sig in getattr(tester, 'argument_signatures', []): 3148 parser.add_argument(*argument_sig.args, 3149 **argument_sig.kwargs) 3150 group_sigs = getattr(tester, 'argument_group_signatures', []) 3151 for group_sig, argument_sigs in group_sigs: 3152 group = parser.add_argument_group(*group_sig.args, 3153 **group_sig.kwargs) 3154 for argument_sig in argument_sigs: 3155 group.add_argument(*argument_sig.args, 3156 **argument_sig.kwargs) 3157 subparsers_sigs = getattr(tester, 'subparsers_signatures', []) 3158 if subparsers_sigs: 3159 subparsers = parser.add_subparsers() 3160 for subparser_sig in subparsers_sigs: 3161 subparsers.add_parser(*subparser_sig.args, 3162 **subparser_sig.kwargs) 3163 return parser 3164 3165 def _test(self, tester, parser_text): 3166 expected_text = getattr(tester, self.func_suffix) 3167 expected_text = textwrap.dedent(expected_text) 3168 tester.assertEqual(expected_text, parser_text) 3169 3170 def test_format(self, tester): 3171 parser = self._get_parser(tester) 3172 format = getattr(parser, 'format_%s' % self.func_suffix) 3173 self._test(tester, format()) 3174 3175 def test_print(self, tester): 3176 parser = self._get_parser(tester) 3177 print_ = getattr(parser, 'print_%s' % self.func_suffix) 3178 old_stream = getattr(sys, self.std_name) 3179 setattr(sys, self.std_name, StdIOBuffer()) 3180 try: 3181 print_() 3182 parser_text = getattr(sys, self.std_name).getvalue() 3183 finally: 3184 setattr(sys, self.std_name, old_stream) 3185 self._test(tester, parser_text) 3186 3187 def test_print_file(self, tester): 3188 parser = self._get_parser(tester) 3189 print_ = getattr(parser, 'print_%s' % self.func_suffix) 3190 sfile = StdIOBuffer() 3191 print_(sfile) 3192 parser_text = sfile.getvalue() 3193 self._test(tester, parser_text) 3194 3195 # add tests for {format,print}_{usage,help} 3196 for func_suffix, std_name in [('usage', 'stdout'), 3197 ('help', 'stdout')]: 3198 AddTests(cls, func_suffix, std_name) 3199 3200bases = TestCase, 3201HelpTestCase = TestHelpFormattingMetaclass('HelpTestCase', bases, {}) 3202 3203 3204class TestHelpBiggerOptionals(HelpTestCase): 3205 """Make sure that argument help aligns when options are longer""" 3206 3207 parser_signature = Sig(prog='PROG', description='DESCRIPTION', 3208 epilog='EPILOG') 3209 argument_signatures = [ 3210 Sig('-v', '--version', action='version', version='0.1'), 3211 Sig('-x', action='store_true', help='X HELP'), 3212 Sig('--y', help='Y HELP'), 3213 Sig('foo', help='FOO HELP'), 3214 Sig('bar', help='BAR HELP'), 3215 ] 3216 argument_group_signatures = [] 3217 usage = '''\ 3218 usage: PROG [-h] [-v] [-x] [--y Y] foo bar 3219 ''' 3220 help = usage + '''\ 3221 3222 DESCRIPTION 3223 3224 positional arguments: 3225 foo FOO HELP 3226 bar BAR HELP 3227 3228 optional arguments: 3229 -h, --help show this help message and exit 3230 -v, --version show program's version number and exit 3231 -x X HELP 3232 --y Y Y HELP 3233 3234 EPILOG 3235 ''' 3236 version = '''\ 3237 0.1 3238 ''' 3239 3240class TestShortColumns(HelpTestCase): 3241 '''Test extremely small number of columns. 3242 3243 TestCase prevents "COLUMNS" from being too small in the tests themselves, 3244 but we don't want any exceptions thrown in such cases. Only ugly representation. 3245 ''' 3246 def setUp(self): 3247 env = support.EnvironmentVarGuard() 3248 env.set("COLUMNS", '15') 3249 self.addCleanup(env.__exit__) 3250 3251 parser_signature = TestHelpBiggerOptionals.parser_signature 3252 argument_signatures = TestHelpBiggerOptionals.argument_signatures 3253 argument_group_signatures = TestHelpBiggerOptionals.argument_group_signatures 3254 usage = '''\ 3255 usage: PROG 3256 [-h] 3257 [-v] 3258 [-x] 3259 [--y Y] 3260 foo 3261 bar 3262 ''' 3263 help = usage + '''\ 3264 3265 DESCRIPTION 3266 3267 positional arguments: 3268 foo 3269 FOO HELP 3270 bar 3271 BAR HELP 3272 3273 optional arguments: 3274 -h, --help 3275 show this 3276 help 3277 message and 3278 exit 3279 -v, --version 3280 show 3281 program's 3282 version 3283 number and 3284 exit 3285 -x 3286 X HELP 3287 --y Y 3288 Y HELP 3289 3290 EPILOG 3291 ''' 3292 version = TestHelpBiggerOptionals.version 3293 3294 3295class TestHelpBiggerOptionalGroups(HelpTestCase): 3296 """Make sure that argument help aligns when options are longer""" 3297 3298 parser_signature = Sig(prog='PROG', description='DESCRIPTION', 3299 epilog='EPILOG') 3300 argument_signatures = [ 3301 Sig('-v', '--version', action='version', version='0.1'), 3302 Sig('-x', action='store_true', help='X HELP'), 3303 Sig('--y', help='Y HELP'), 3304 Sig('foo', help='FOO HELP'), 3305 Sig('bar', help='BAR HELP'), 3306 ] 3307 argument_group_signatures = [ 3308 (Sig('GROUP TITLE', description='GROUP DESCRIPTION'), [ 3309 Sig('baz', help='BAZ HELP'), 3310 Sig('-z', nargs='+', help='Z HELP')]), 3311 ] 3312 usage = '''\ 3313 usage: PROG [-h] [-v] [-x] [--y Y] [-z Z [Z ...]] foo bar baz 3314 ''' 3315 help = usage + '''\ 3316 3317 DESCRIPTION 3318 3319 positional arguments: 3320 foo FOO HELP 3321 bar BAR HELP 3322 3323 optional arguments: 3324 -h, --help show this help message and exit 3325 -v, --version show program's version number and exit 3326 -x X HELP 3327 --y Y Y HELP 3328 3329 GROUP TITLE: 3330 GROUP DESCRIPTION 3331 3332 baz BAZ HELP 3333 -z Z [Z ...] Z HELP 3334 3335 EPILOG 3336 ''' 3337 version = '''\ 3338 0.1 3339 ''' 3340 3341 3342class TestHelpBiggerPositionals(HelpTestCase): 3343 """Make sure that help aligns when arguments are longer""" 3344 3345 parser_signature = Sig(usage='USAGE', description='DESCRIPTION') 3346 argument_signatures = [ 3347 Sig('-x', action='store_true', help='X HELP'), 3348 Sig('--y', help='Y HELP'), 3349 Sig('ekiekiekifekang', help='EKI HELP'), 3350 Sig('bar', help='BAR HELP'), 3351 ] 3352 argument_group_signatures = [] 3353 usage = '''\ 3354 usage: USAGE 3355 ''' 3356 help = usage + '''\ 3357 3358 DESCRIPTION 3359 3360 positional arguments: 3361 ekiekiekifekang EKI HELP 3362 bar BAR HELP 3363 3364 optional arguments: 3365 -h, --help show this help message and exit 3366 -x X HELP 3367 --y Y Y HELP 3368 ''' 3369 3370 version = '' 3371 3372 3373class TestHelpReformatting(HelpTestCase): 3374 """Make sure that text after short names starts on the first line""" 3375 3376 parser_signature = Sig( 3377 prog='PROG', 3378 description=' oddly formatted\n' 3379 'description\n' 3380 '\n' 3381 'that is so long that it should go onto multiple ' 3382 'lines when wrapped') 3383 argument_signatures = [ 3384 Sig('-x', metavar='XX', help='oddly\n' 3385 ' formatted -x help'), 3386 Sig('y', metavar='yyy', help='normal y help'), 3387 ] 3388 argument_group_signatures = [ 3389 (Sig('title', description='\n' 3390 ' oddly formatted group\n' 3391 '\n' 3392 'description'), 3393 [Sig('-a', action='store_true', 3394 help=' oddly \n' 3395 'formatted -a help \n' 3396 ' again, so long that it should be wrapped over ' 3397 'multiple lines')]), 3398 ] 3399 usage = '''\ 3400 usage: PROG [-h] [-x XX] [-a] yyy 3401 ''' 3402 help = usage + '''\ 3403 3404 oddly formatted description that is so long that it should go onto \ 3405multiple 3406 lines when wrapped 3407 3408 positional arguments: 3409 yyy normal y help 3410 3411 optional arguments: 3412 -h, --help show this help message and exit 3413 -x XX oddly formatted -x help 3414 3415 title: 3416 oddly formatted group description 3417 3418 -a oddly formatted -a help again, so long that it should \ 3419be wrapped 3420 over multiple lines 3421 ''' 3422 version = '' 3423 3424 3425class TestHelpWrappingShortNames(HelpTestCase): 3426 """Make sure that text after short names starts on the first line""" 3427 3428 parser_signature = Sig(prog='PROG', description= 'D\nD' * 30) 3429 argument_signatures = [ 3430 Sig('-x', metavar='XX', help='XHH HX' * 20), 3431 Sig('y', metavar='yyy', help='YH YH' * 20), 3432 ] 3433 argument_group_signatures = [ 3434 (Sig('ALPHAS'), [ 3435 Sig('-a', action='store_true', help='AHHH HHA' * 10)]), 3436 ] 3437 usage = '''\ 3438 usage: PROG [-h] [-x XX] [-a] yyy 3439 ''' 3440 help = usage + '''\ 3441 3442 D DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD \ 3443DD DD DD 3444 DD DD DD DD D 3445 3446 positional arguments: 3447 yyy YH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH \ 3448YHYH YHYH 3449 YHYH YHYH YHYH YHYH YHYH YHYH YHYH YH 3450 3451 optional arguments: 3452 -h, --help show this help message and exit 3453 -x XX XHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH \ 3454HXXHH HXXHH 3455 HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HX 3456 3457 ALPHAS: 3458 -a AHHH HHAAHHH HHAAHHH HHAAHHH HHAAHHH HHAAHHH HHAAHHH \ 3459HHAAHHH 3460 HHAAHHH HHAAHHH HHA 3461 ''' 3462 version = '' 3463 3464 3465class TestHelpWrappingLongNames(HelpTestCase): 3466 """Make sure that text after long names starts on the next line""" 3467 3468 parser_signature = Sig(usage='USAGE', description= 'D D' * 30) 3469 argument_signatures = [ 3470 Sig('-v', '--version', action='version', version='V V' * 30), 3471 Sig('-x', metavar='X' * 25, help='XH XH' * 20), 3472 Sig('y', metavar='y' * 25, help='YH YH' * 20), 3473 ] 3474 argument_group_signatures = [ 3475 (Sig('ALPHAS'), [ 3476 Sig('-a', metavar='A' * 25, help='AH AH' * 20), 3477 Sig('z', metavar='z' * 25, help='ZH ZH' * 20)]), 3478 ] 3479 usage = '''\ 3480 usage: USAGE 3481 ''' 3482 help = usage + '''\ 3483 3484 D DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD \ 3485DD DD DD 3486 DD DD DD DD D 3487 3488 positional arguments: 3489 yyyyyyyyyyyyyyyyyyyyyyyyy 3490 YH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH \ 3491YHYH YHYH 3492 YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YH 3493 3494 optional arguments: 3495 -h, --help show this help message and exit 3496 -v, --version show program's version number and exit 3497 -x XXXXXXXXXXXXXXXXXXXXXXXXX 3498 XH XHXH XHXH XHXH XHXH XHXH XHXH XHXH XHXH \ 3499XHXH XHXH 3500 XHXH XHXH XHXH XHXH XHXH XHXH XHXH XHXH XHXH XH 3501 3502 ALPHAS: 3503 -a AAAAAAAAAAAAAAAAAAAAAAAAA 3504 AH AHAH AHAH AHAH AHAH AHAH AHAH AHAH AHAH \ 3505AHAH AHAH 3506 AHAH AHAH AHAH AHAH AHAH AHAH AHAH AHAH AHAH AH 3507 zzzzzzzzzzzzzzzzzzzzzzzzz 3508 ZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH \ 3509ZHZH ZHZH 3510 ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZH 3511 ''' 3512 version = '''\ 3513 V VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV \ 3514VV VV VV 3515 VV VV VV VV V 3516 ''' 3517 3518 3519class TestHelpUsage(HelpTestCase): 3520 """Test basic usage messages""" 3521 3522 parser_signature = Sig(prog='PROG') 3523 argument_signatures = [ 3524 Sig('-w', nargs='+', help='w'), 3525 Sig('-x', nargs='*', help='x'), 3526 Sig('a', help='a'), 3527 Sig('b', help='b', nargs=2), 3528 Sig('c', help='c', nargs='?'), 3529 Sig('--foo', help='Whether to foo', action=argparse.BooleanOptionalAction), 3530 Sig('--bar', help='Whether to bar', default=True, 3531 action=argparse.BooleanOptionalAction), 3532 Sig('-f', '--foobar', '--barfoo', action=argparse.BooleanOptionalAction), 3533 ] 3534 argument_group_signatures = [ 3535 (Sig('group'), [ 3536 Sig('-y', nargs='?', help='y'), 3537 Sig('-z', nargs=3, help='z'), 3538 Sig('d', help='d', nargs='*'), 3539 Sig('e', help='e', nargs='+'), 3540 ]) 3541 ] 3542 usage = '''\ 3543 usage: PROG [-h] [-w W [W ...]] [-x [X ...]] [--foo | --no-foo] 3544 [--bar | --no-bar] 3545 [-f | --foobar | --no-foobar | --barfoo | --no-barfoo] [-y [Y]] 3546 [-z Z Z Z] 3547 a b b [c] [d ...] e [e ...] 3548 ''' 3549 help = usage + '''\ 3550 3551 positional arguments: 3552 a a 3553 b b 3554 c c 3555 3556 optional arguments: 3557 -h, --help show this help message and exit 3558 -w W [W ...] w 3559 -x [X ...] x 3560 --foo, --no-foo Whether to foo 3561 --bar, --no-bar Whether to bar (default: True) 3562 -f, --foobar, --no-foobar, --barfoo, --no-barfoo 3563 3564 group: 3565 -y [Y] y 3566 -z Z Z Z z 3567 d d 3568 e e 3569 ''' 3570 version = '' 3571 3572 3573class TestHelpOnlyUserGroups(HelpTestCase): 3574 """Test basic usage messages""" 3575 3576 parser_signature = Sig(prog='PROG', add_help=False) 3577 argument_signatures = [] 3578 argument_group_signatures = [ 3579 (Sig('xxxx'), [ 3580 Sig('-x', help='x'), 3581 Sig('a', help='a'), 3582 ]), 3583 (Sig('yyyy'), [ 3584 Sig('b', help='b'), 3585 Sig('-y', help='y'), 3586 ]), 3587 ] 3588 usage = '''\ 3589 usage: PROG [-x X] [-y Y] a b 3590 ''' 3591 help = usage + '''\ 3592 3593 xxxx: 3594 -x X x 3595 a a 3596 3597 yyyy: 3598 b b 3599 -y Y y 3600 ''' 3601 version = '' 3602 3603 3604class TestHelpUsageLongProg(HelpTestCase): 3605 """Test usage messages where the prog is long""" 3606 3607 parser_signature = Sig(prog='P' * 60) 3608 argument_signatures = [ 3609 Sig('-w', metavar='W'), 3610 Sig('-x', metavar='X'), 3611 Sig('a'), 3612 Sig('b'), 3613 ] 3614 argument_group_signatures = [] 3615 usage = '''\ 3616 usage: PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP 3617 [-h] [-w W] [-x X] a b 3618 ''' 3619 help = usage + '''\ 3620 3621 positional arguments: 3622 a 3623 b 3624 3625 optional arguments: 3626 -h, --help show this help message and exit 3627 -w W 3628 -x X 3629 ''' 3630 version = '' 3631 3632 3633class TestHelpUsageLongProgOptionsWrap(HelpTestCase): 3634 """Test usage messages where the prog is long and the optionals wrap""" 3635 3636 parser_signature = Sig(prog='P' * 60) 3637 argument_signatures = [ 3638 Sig('-w', metavar='W' * 25), 3639 Sig('-x', metavar='X' * 25), 3640 Sig('-y', metavar='Y' * 25), 3641 Sig('-z', metavar='Z' * 25), 3642 Sig('a'), 3643 Sig('b'), 3644 ] 3645 argument_group_signatures = [] 3646 usage = '''\ 3647 usage: PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP 3648 [-h] [-w WWWWWWWWWWWWWWWWWWWWWWWWW] \ 3649[-x XXXXXXXXXXXXXXXXXXXXXXXXX] 3650 [-y YYYYYYYYYYYYYYYYYYYYYYYYY] [-z ZZZZZZZZZZZZZZZZZZZZZZZZZ] 3651 a b 3652 ''' 3653 help = usage + '''\ 3654 3655 positional arguments: 3656 a 3657 b 3658 3659 optional arguments: 3660 -h, --help show this help message and exit 3661 -w WWWWWWWWWWWWWWWWWWWWWWWWW 3662 -x XXXXXXXXXXXXXXXXXXXXXXXXX 3663 -y YYYYYYYYYYYYYYYYYYYYYYYYY 3664 -z ZZZZZZZZZZZZZZZZZZZZZZZZZ 3665 ''' 3666 version = '' 3667 3668 3669class TestHelpUsageLongProgPositionalsWrap(HelpTestCase): 3670 """Test usage messages where the prog is long and the positionals wrap""" 3671 3672 parser_signature = Sig(prog='P' * 60, add_help=False) 3673 argument_signatures = [ 3674 Sig('a' * 25), 3675 Sig('b' * 25), 3676 Sig('c' * 25), 3677 ] 3678 argument_group_signatures = [] 3679 usage = '''\ 3680 usage: PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP 3681 aaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbb 3682 ccccccccccccccccccccccccc 3683 ''' 3684 help = usage + '''\ 3685 3686 positional arguments: 3687 aaaaaaaaaaaaaaaaaaaaaaaaa 3688 bbbbbbbbbbbbbbbbbbbbbbbbb 3689 ccccccccccccccccccccccccc 3690 ''' 3691 version = '' 3692 3693 3694class TestHelpUsageOptionalsWrap(HelpTestCase): 3695 """Test usage messages where the optionals wrap""" 3696 3697 parser_signature = Sig(prog='PROG') 3698 argument_signatures = [ 3699 Sig('-w', metavar='W' * 25), 3700 Sig('-x', metavar='X' * 25), 3701 Sig('-y', metavar='Y' * 25), 3702 Sig('-z', metavar='Z' * 25), 3703 Sig('a'), 3704 Sig('b'), 3705 Sig('c'), 3706 ] 3707 argument_group_signatures = [] 3708 usage = '''\ 3709 usage: PROG [-h] [-w WWWWWWWWWWWWWWWWWWWWWWWWW] \ 3710[-x XXXXXXXXXXXXXXXXXXXXXXXXX] 3711 [-y YYYYYYYYYYYYYYYYYYYYYYYYY] \ 3712[-z ZZZZZZZZZZZZZZZZZZZZZZZZZ] 3713 a b c 3714 ''' 3715 help = usage + '''\ 3716 3717 positional arguments: 3718 a 3719 b 3720 c 3721 3722 optional arguments: 3723 -h, --help show this help message and exit 3724 -w WWWWWWWWWWWWWWWWWWWWWWWWW 3725 -x XXXXXXXXXXXXXXXXXXXXXXXXX 3726 -y YYYYYYYYYYYYYYYYYYYYYYYYY 3727 -z ZZZZZZZZZZZZZZZZZZZZZZZZZ 3728 ''' 3729 version = '' 3730 3731 3732class TestHelpUsagePositionalsWrap(HelpTestCase): 3733 """Test usage messages where the positionals wrap""" 3734 3735 parser_signature = Sig(prog='PROG') 3736 argument_signatures = [ 3737 Sig('-x'), 3738 Sig('-y'), 3739 Sig('-z'), 3740 Sig('a' * 25), 3741 Sig('b' * 25), 3742 Sig('c' * 25), 3743 ] 3744 argument_group_signatures = [] 3745 usage = '''\ 3746 usage: PROG [-h] [-x X] [-y Y] [-z Z] 3747 aaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbb 3748 ccccccccccccccccccccccccc 3749 ''' 3750 help = usage + '''\ 3751 3752 positional arguments: 3753 aaaaaaaaaaaaaaaaaaaaaaaaa 3754 bbbbbbbbbbbbbbbbbbbbbbbbb 3755 ccccccccccccccccccccccccc 3756 3757 optional arguments: 3758 -h, --help show this help message and exit 3759 -x X 3760 -y Y 3761 -z Z 3762 ''' 3763 version = '' 3764 3765 3766class TestHelpUsageOptionalsPositionalsWrap(HelpTestCase): 3767 """Test usage messages where the optionals and positionals wrap""" 3768 3769 parser_signature = Sig(prog='PROG') 3770 argument_signatures = [ 3771 Sig('-x', metavar='X' * 25), 3772 Sig('-y', metavar='Y' * 25), 3773 Sig('-z', metavar='Z' * 25), 3774 Sig('a' * 25), 3775 Sig('b' * 25), 3776 Sig('c' * 25), 3777 ] 3778 argument_group_signatures = [] 3779 usage = '''\ 3780 usage: PROG [-h] [-x XXXXXXXXXXXXXXXXXXXXXXXXX] \ 3781[-y YYYYYYYYYYYYYYYYYYYYYYYYY] 3782 [-z ZZZZZZZZZZZZZZZZZZZZZZZZZ] 3783 aaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbb 3784 ccccccccccccccccccccccccc 3785 ''' 3786 help = usage + '''\ 3787 3788 positional arguments: 3789 aaaaaaaaaaaaaaaaaaaaaaaaa 3790 bbbbbbbbbbbbbbbbbbbbbbbbb 3791 ccccccccccccccccccccccccc 3792 3793 optional arguments: 3794 -h, --help show this help message and exit 3795 -x XXXXXXXXXXXXXXXXXXXXXXXXX 3796 -y YYYYYYYYYYYYYYYYYYYYYYYYY 3797 -z ZZZZZZZZZZZZZZZZZZZZZZZZZ 3798 ''' 3799 version = '' 3800 3801 3802class TestHelpUsageOptionalsOnlyWrap(HelpTestCase): 3803 """Test usage messages where there are only optionals and they wrap""" 3804 3805 parser_signature = Sig(prog='PROG') 3806 argument_signatures = [ 3807 Sig('-x', metavar='X' * 25), 3808 Sig('-y', metavar='Y' * 25), 3809 Sig('-z', metavar='Z' * 25), 3810 ] 3811 argument_group_signatures = [] 3812 usage = '''\ 3813 usage: PROG [-h] [-x XXXXXXXXXXXXXXXXXXXXXXXXX] \ 3814[-y YYYYYYYYYYYYYYYYYYYYYYYYY] 3815 [-z ZZZZZZZZZZZZZZZZZZZZZZZZZ] 3816 ''' 3817 help = usage + '''\ 3818 3819 optional arguments: 3820 -h, --help show this help message and exit 3821 -x XXXXXXXXXXXXXXXXXXXXXXXXX 3822 -y YYYYYYYYYYYYYYYYYYYYYYYYY 3823 -z ZZZZZZZZZZZZZZZZZZZZZZZZZ 3824 ''' 3825 version = '' 3826 3827 3828class TestHelpUsagePositionalsOnlyWrap(HelpTestCase): 3829 """Test usage messages where there are only positionals and they wrap""" 3830 3831 parser_signature = Sig(prog='PROG', add_help=False) 3832 argument_signatures = [ 3833 Sig('a' * 25), 3834 Sig('b' * 25), 3835 Sig('c' * 25), 3836 ] 3837 argument_group_signatures = [] 3838 usage = '''\ 3839 usage: PROG aaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbb 3840 ccccccccccccccccccccccccc 3841 ''' 3842 help = usage + '''\ 3843 3844 positional arguments: 3845 aaaaaaaaaaaaaaaaaaaaaaaaa 3846 bbbbbbbbbbbbbbbbbbbbbbbbb 3847 ccccccccccccccccccccccccc 3848 ''' 3849 version = '' 3850 3851 3852class TestHelpVariableExpansion(HelpTestCase): 3853 """Test that variables are expanded properly in help messages""" 3854 3855 parser_signature = Sig(prog='PROG') 3856 argument_signatures = [ 3857 Sig('-x', type=int, 3858 help='x %(prog)s %(default)s %(type)s %%'), 3859 Sig('-y', action='store_const', default=42, const='XXX', 3860 help='y %(prog)s %(default)s %(const)s'), 3861 Sig('--foo', choices='abc', 3862 help='foo %(prog)s %(default)s %(choices)s'), 3863 Sig('--bar', default='baz', choices=[1, 2], metavar='BBB', 3864 help='bar %(prog)s %(default)s %(dest)s'), 3865 Sig('spam', help='spam %(prog)s %(default)s'), 3866 Sig('badger', default=0.5, help='badger %(prog)s %(default)s'), 3867 ] 3868 argument_group_signatures = [ 3869 (Sig('group'), [ 3870 Sig('-a', help='a %(prog)s %(default)s'), 3871 Sig('-b', default=-1, help='b %(prog)s %(default)s'), 3872 ]) 3873 ] 3874 usage = ('''\ 3875 usage: PROG [-h] [-x X] [-y] [--foo {a,b,c}] [--bar BBB] [-a A] [-b B] 3876 spam badger 3877 ''') 3878 help = usage + '''\ 3879 3880 positional arguments: 3881 spam spam PROG None 3882 badger badger PROG 0.5 3883 3884 optional arguments: 3885 -h, --help show this help message and exit 3886 -x X x PROG None int % 3887 -y y PROG 42 XXX 3888 --foo {a,b,c} foo PROG None a, b, c 3889 --bar BBB bar PROG baz bar 3890 3891 group: 3892 -a A a PROG None 3893 -b B b PROG -1 3894 ''' 3895 version = '' 3896 3897 3898class TestHelpVariableExpansionUsageSupplied(HelpTestCase): 3899 """Test that variables are expanded properly when usage= is present""" 3900 3901 parser_signature = Sig(prog='PROG', usage='%(prog)s FOO') 3902 argument_signatures = [] 3903 argument_group_signatures = [] 3904 usage = ('''\ 3905 usage: PROG FOO 3906 ''') 3907 help = usage + '''\ 3908 3909 optional arguments: 3910 -h, --help show this help message and exit 3911 ''' 3912 version = '' 3913 3914 3915class TestHelpVariableExpansionNoArguments(HelpTestCase): 3916 """Test that variables are expanded properly with no arguments""" 3917 3918 parser_signature = Sig(prog='PROG', add_help=False) 3919 argument_signatures = [] 3920 argument_group_signatures = [] 3921 usage = ('''\ 3922 usage: PROG 3923 ''') 3924 help = usage 3925 version = '' 3926 3927 3928class TestHelpSuppressUsage(HelpTestCase): 3929 """Test that items can be suppressed in usage messages""" 3930 3931 parser_signature = Sig(prog='PROG', usage=argparse.SUPPRESS) 3932 argument_signatures = [ 3933 Sig('--foo', help='foo help'), 3934 Sig('spam', help='spam help'), 3935 ] 3936 argument_group_signatures = [] 3937 help = '''\ 3938 positional arguments: 3939 spam spam help 3940 3941 optional arguments: 3942 -h, --help show this help message and exit 3943 --foo FOO foo help 3944 ''' 3945 usage = '' 3946 version = '' 3947 3948 3949class TestHelpSuppressOptional(HelpTestCase): 3950 """Test that optional arguments can be suppressed in help messages""" 3951 3952 parser_signature = Sig(prog='PROG', add_help=False) 3953 argument_signatures = [ 3954 Sig('--foo', help=argparse.SUPPRESS), 3955 Sig('spam', help='spam help'), 3956 ] 3957 argument_group_signatures = [] 3958 usage = '''\ 3959 usage: PROG spam 3960 ''' 3961 help = usage + '''\ 3962 3963 positional arguments: 3964 spam spam help 3965 ''' 3966 version = '' 3967 3968 3969class TestHelpSuppressOptionalGroup(HelpTestCase): 3970 """Test that optional groups can be suppressed in help messages""" 3971 3972 parser_signature = Sig(prog='PROG') 3973 argument_signatures = [ 3974 Sig('--foo', help='foo help'), 3975 Sig('spam', help='spam help'), 3976 ] 3977 argument_group_signatures = [ 3978 (Sig('group'), [Sig('--bar', help=argparse.SUPPRESS)]), 3979 ] 3980 usage = '''\ 3981 usage: PROG [-h] [--foo FOO] spam 3982 ''' 3983 help = usage + '''\ 3984 3985 positional arguments: 3986 spam spam help 3987 3988 optional arguments: 3989 -h, --help show this help message and exit 3990 --foo FOO foo help 3991 ''' 3992 version = '' 3993 3994 3995class TestHelpSuppressPositional(HelpTestCase): 3996 """Test that positional arguments can be suppressed in help messages""" 3997 3998 parser_signature = Sig(prog='PROG') 3999 argument_signatures = [ 4000 Sig('--foo', help='foo help'), 4001 Sig('spam', help=argparse.SUPPRESS), 4002 ] 4003 argument_group_signatures = [] 4004 usage = '''\ 4005 usage: PROG [-h] [--foo FOO] 4006 ''' 4007 help = usage + '''\ 4008 4009 optional arguments: 4010 -h, --help show this help message and exit 4011 --foo FOO foo help 4012 ''' 4013 version = '' 4014 4015 4016class TestHelpRequiredOptional(HelpTestCase): 4017 """Test that required options don't look optional""" 4018 4019 parser_signature = Sig(prog='PROG') 4020 argument_signatures = [ 4021 Sig('--foo', required=True, help='foo help'), 4022 ] 4023 argument_group_signatures = [] 4024 usage = '''\ 4025 usage: PROG [-h] --foo FOO 4026 ''' 4027 help = usage + '''\ 4028 4029 optional arguments: 4030 -h, --help show this help message and exit 4031 --foo FOO foo help 4032 ''' 4033 version = '' 4034 4035 4036class TestHelpAlternatePrefixChars(HelpTestCase): 4037 """Test that options display with different prefix characters""" 4038 4039 parser_signature = Sig(prog='PROG', prefix_chars='^;', add_help=False) 4040 argument_signatures = [ 4041 Sig('^^foo', action='store_true', help='foo help'), 4042 Sig(';b', ';;bar', help='bar help'), 4043 ] 4044 argument_group_signatures = [] 4045 usage = '''\ 4046 usage: PROG [^^foo] [;b BAR] 4047 ''' 4048 help = usage + '''\ 4049 4050 optional arguments: 4051 ^^foo foo help 4052 ;b BAR, ;;bar BAR bar help 4053 ''' 4054 version = '' 4055 4056 4057class TestHelpNoHelpOptional(HelpTestCase): 4058 """Test that the --help argument can be suppressed help messages""" 4059 4060 parser_signature = Sig(prog='PROG', add_help=False) 4061 argument_signatures = [ 4062 Sig('--foo', help='foo help'), 4063 Sig('spam', help='spam help'), 4064 ] 4065 argument_group_signatures = [] 4066 usage = '''\ 4067 usage: PROG [--foo FOO] spam 4068 ''' 4069 help = usage + '''\ 4070 4071 positional arguments: 4072 spam spam help 4073 4074 optional arguments: 4075 --foo FOO foo help 4076 ''' 4077 version = '' 4078 4079 4080class TestHelpNone(HelpTestCase): 4081 """Test that no errors occur if no help is specified""" 4082 4083 parser_signature = Sig(prog='PROG') 4084 argument_signatures = [ 4085 Sig('--foo'), 4086 Sig('spam'), 4087 ] 4088 argument_group_signatures = [] 4089 usage = '''\ 4090 usage: PROG [-h] [--foo FOO] spam 4091 ''' 4092 help = usage + '''\ 4093 4094 positional arguments: 4095 spam 4096 4097 optional arguments: 4098 -h, --help show this help message and exit 4099 --foo FOO 4100 ''' 4101 version = '' 4102 4103 4104class TestHelpTupleMetavar(HelpTestCase): 4105 """Test specifying metavar as a tuple""" 4106 4107 parser_signature = Sig(prog='PROG') 4108 argument_signatures = [ 4109 Sig('-w', help='w', nargs='+', metavar=('W1', 'W2')), 4110 Sig('-x', help='x', nargs='*', metavar=('X1', 'X2')), 4111 Sig('-y', help='y', nargs=3, metavar=('Y1', 'Y2', 'Y3')), 4112 Sig('-z', help='z', nargs='?', metavar=('Z1', )), 4113 ] 4114 argument_group_signatures = [] 4115 usage = '''\ 4116 usage: PROG [-h] [-w W1 [W2 ...]] [-x [X1 [X2 ...]]] [-y Y1 Y2 Y3] \ 4117[-z [Z1]] 4118 ''' 4119 help = usage + '''\ 4120 4121 optional arguments: 4122 -h, --help show this help message and exit 4123 -w W1 [W2 ...] w 4124 -x [X1 [X2 ...]] x 4125 -y Y1 Y2 Y3 y 4126 -z [Z1] z 4127 ''' 4128 version = '' 4129 4130 4131class TestHelpRawText(HelpTestCase): 4132 """Test the RawTextHelpFormatter""" 4133 4134 parser_signature = Sig( 4135 prog='PROG', formatter_class=argparse.RawTextHelpFormatter, 4136 description='Keep the formatting\n' 4137 ' exactly as it is written\n' 4138 '\n' 4139 'here\n') 4140 4141 argument_signatures = [ 4142 Sig('--foo', help=' foo help should also\n' 4143 'appear as given here'), 4144 Sig('spam', help='spam help'), 4145 ] 4146 argument_group_signatures = [ 4147 (Sig('title', description=' This text\n' 4148 ' should be indented\n' 4149 ' exactly like it is here\n'), 4150 [Sig('--bar', help='bar help')]), 4151 ] 4152 usage = '''\ 4153 usage: PROG [-h] [--foo FOO] [--bar BAR] spam 4154 ''' 4155 help = usage + '''\ 4156 4157 Keep the formatting 4158 exactly as it is written 4159 4160 here 4161 4162 positional arguments: 4163 spam spam help 4164 4165 optional arguments: 4166 -h, --help show this help message and exit 4167 --foo FOO foo help should also 4168 appear as given here 4169 4170 title: 4171 This text 4172 should be indented 4173 exactly like it is here 4174 4175 --bar BAR bar help 4176 ''' 4177 version = '' 4178 4179 4180class TestHelpRawDescription(HelpTestCase): 4181 """Test the RawTextHelpFormatter""" 4182 4183 parser_signature = Sig( 4184 prog='PROG', formatter_class=argparse.RawDescriptionHelpFormatter, 4185 description='Keep the formatting\n' 4186 ' exactly as it is written\n' 4187 '\n' 4188 'here\n') 4189 4190 argument_signatures = [ 4191 Sig('--foo', help=' foo help should not\n' 4192 ' retain this odd formatting'), 4193 Sig('spam', help='spam help'), 4194 ] 4195 argument_group_signatures = [ 4196 (Sig('title', description=' This text\n' 4197 ' should be indented\n' 4198 ' exactly like it is here\n'), 4199 [Sig('--bar', help='bar help')]), 4200 ] 4201 usage = '''\ 4202 usage: PROG [-h] [--foo FOO] [--bar BAR] spam 4203 ''' 4204 help = usage + '''\ 4205 4206 Keep the formatting 4207 exactly as it is written 4208 4209 here 4210 4211 positional arguments: 4212 spam spam help 4213 4214 optional arguments: 4215 -h, --help show this help message and exit 4216 --foo FOO foo help should not retain this odd formatting 4217 4218 title: 4219 This text 4220 should be indented 4221 exactly like it is here 4222 4223 --bar BAR bar help 4224 ''' 4225 version = '' 4226 4227 4228class TestHelpArgumentDefaults(HelpTestCase): 4229 """Test the ArgumentDefaultsHelpFormatter""" 4230 4231 parser_signature = Sig( 4232 prog='PROG', formatter_class=argparse.ArgumentDefaultsHelpFormatter, 4233 description='description') 4234 4235 argument_signatures = [ 4236 Sig('--foo', help='foo help - oh and by the way, %(default)s'), 4237 Sig('--bar', action='store_true', help='bar help'), 4238 Sig('spam', help='spam help'), 4239 Sig('badger', nargs='?', default='wooden', help='badger help'), 4240 ] 4241 argument_group_signatures = [ 4242 (Sig('title', description='description'), 4243 [Sig('--baz', type=int, default=42, help='baz help')]), 4244 ] 4245 usage = '''\ 4246 usage: PROG [-h] [--foo FOO] [--bar] [--baz BAZ] spam [badger] 4247 ''' 4248 help = usage + '''\ 4249 4250 description 4251 4252 positional arguments: 4253 spam spam help 4254 badger badger help (default: wooden) 4255 4256 optional arguments: 4257 -h, --help show this help message and exit 4258 --foo FOO foo help - oh and by the way, None 4259 --bar bar help (default: False) 4260 4261 title: 4262 description 4263 4264 --baz BAZ baz help (default: 42) 4265 ''' 4266 version = '' 4267 4268class TestHelpVersionAction(HelpTestCase): 4269 """Test the default help for the version action""" 4270 4271 parser_signature = Sig(prog='PROG', description='description') 4272 argument_signatures = [Sig('-V', '--version', action='version', version='3.6')] 4273 argument_group_signatures = [] 4274 usage = '''\ 4275 usage: PROG [-h] [-V] 4276 ''' 4277 help = usage + '''\ 4278 4279 description 4280 4281 optional arguments: 4282 -h, --help show this help message and exit 4283 -V, --version show program's version number and exit 4284 ''' 4285 version = '' 4286 4287 4288class TestHelpVersionActionSuppress(HelpTestCase): 4289 """Test that the --version argument can be suppressed in help messages""" 4290 4291 parser_signature = Sig(prog='PROG') 4292 argument_signatures = [ 4293 Sig('-v', '--version', action='version', version='1.0', 4294 help=argparse.SUPPRESS), 4295 Sig('--foo', help='foo help'), 4296 Sig('spam', help='spam help'), 4297 ] 4298 argument_group_signatures = [] 4299 usage = '''\ 4300 usage: PROG [-h] [--foo FOO] spam 4301 ''' 4302 help = usage + '''\ 4303 4304 positional arguments: 4305 spam spam help 4306 4307 optional arguments: 4308 -h, --help show this help message and exit 4309 --foo FOO foo help 4310 ''' 4311 4312 4313class TestHelpSubparsersOrdering(HelpTestCase): 4314 """Test ordering of subcommands in help matches the code""" 4315 parser_signature = Sig(prog='PROG', 4316 description='display some subcommands') 4317 argument_signatures = [Sig('-v', '--version', action='version', version='0.1')] 4318 4319 subparsers_signatures = [Sig(name=name) 4320 for name in ('a', 'b', 'c', 'd', 'e')] 4321 4322 usage = '''\ 4323 usage: PROG [-h] [-v] {a,b,c,d,e} ... 4324 ''' 4325 4326 help = usage + '''\ 4327 4328 display some subcommands 4329 4330 positional arguments: 4331 {a,b,c,d,e} 4332 4333 optional arguments: 4334 -h, --help show this help message and exit 4335 -v, --version show program's version number and exit 4336 ''' 4337 4338 version = '''\ 4339 0.1 4340 ''' 4341 4342class TestHelpSubparsersWithHelpOrdering(HelpTestCase): 4343 """Test ordering of subcommands in help matches the code""" 4344 parser_signature = Sig(prog='PROG', 4345 description='display some subcommands') 4346 argument_signatures = [Sig('-v', '--version', action='version', version='0.1')] 4347 4348 subcommand_data = (('a', 'a subcommand help'), 4349 ('b', 'b subcommand help'), 4350 ('c', 'c subcommand help'), 4351 ('d', 'd subcommand help'), 4352 ('e', 'e subcommand help'), 4353 ) 4354 4355 subparsers_signatures = [Sig(name=name, help=help) 4356 for name, help in subcommand_data] 4357 4358 usage = '''\ 4359 usage: PROG [-h] [-v] {a,b,c,d,e} ... 4360 ''' 4361 4362 help = usage + '''\ 4363 4364 display some subcommands 4365 4366 positional arguments: 4367 {a,b,c,d,e} 4368 a a subcommand help 4369 b b subcommand help 4370 c c subcommand help 4371 d d subcommand help 4372 e e subcommand help 4373 4374 optional arguments: 4375 -h, --help show this help message and exit 4376 -v, --version show program's version number and exit 4377 ''' 4378 4379 version = '''\ 4380 0.1 4381 ''' 4382 4383 4384 4385class TestHelpMetavarTypeFormatter(HelpTestCase): 4386 4387 def custom_type(string): 4388 return string 4389 4390 parser_signature = Sig(prog='PROG', description='description', 4391 formatter_class=argparse.MetavarTypeHelpFormatter) 4392 argument_signatures = [Sig('a', type=int), 4393 Sig('-b', type=custom_type), 4394 Sig('-c', type=float, metavar='SOME FLOAT')] 4395 argument_group_signatures = [] 4396 usage = '''\ 4397 usage: PROG [-h] [-b custom_type] [-c SOME FLOAT] int 4398 ''' 4399 help = usage + '''\ 4400 4401 description 4402 4403 positional arguments: 4404 int 4405 4406 optional arguments: 4407 -h, --help show this help message and exit 4408 -b custom_type 4409 -c SOME FLOAT 4410 ''' 4411 version = '' 4412 4413 4414# ===================================== 4415# Optional/Positional constructor tests 4416# ===================================== 4417 4418class TestInvalidArgumentConstructors(TestCase): 4419 """Test a bunch of invalid Argument constructors""" 4420 4421 def assertTypeError(self, *args, **kwargs): 4422 parser = argparse.ArgumentParser() 4423 self.assertRaises(TypeError, parser.add_argument, 4424 *args, **kwargs) 4425 4426 def assertValueError(self, *args, **kwargs): 4427 parser = argparse.ArgumentParser() 4428 self.assertRaises(ValueError, parser.add_argument, 4429 *args, **kwargs) 4430 4431 def test_invalid_keyword_arguments(self): 4432 self.assertTypeError('-x', bar=None) 4433 self.assertTypeError('-y', callback='foo') 4434 self.assertTypeError('-y', callback_args=()) 4435 self.assertTypeError('-y', callback_kwargs={}) 4436 4437 def test_missing_destination(self): 4438 self.assertTypeError() 4439 for action in ['append', 'store']: 4440 self.assertTypeError(action=action) 4441 4442 def test_invalid_option_strings(self): 4443 self.assertValueError('--') 4444 self.assertValueError('---') 4445 4446 def test_invalid_type(self): 4447 self.assertValueError('--foo', type='int') 4448 self.assertValueError('--foo', type=(int, float)) 4449 4450 def test_invalid_action(self): 4451 self.assertValueError('-x', action='foo') 4452 self.assertValueError('foo', action='baz') 4453 self.assertValueError('--foo', action=('store', 'append')) 4454 parser = argparse.ArgumentParser() 4455 with self.assertRaises(ValueError) as cm: 4456 parser.add_argument("--foo", action="store-true") 4457 self.assertIn('unknown action', str(cm.exception)) 4458 4459 def test_multiple_dest(self): 4460 parser = argparse.ArgumentParser() 4461 parser.add_argument(dest='foo') 4462 with self.assertRaises(ValueError) as cm: 4463 parser.add_argument('bar', dest='baz') 4464 self.assertIn('dest supplied twice for positional argument', 4465 str(cm.exception)) 4466 4467 def test_no_argument_actions(self): 4468 for action in ['store_const', 'store_true', 'store_false', 4469 'append_const', 'count']: 4470 for attrs in [dict(type=int), dict(nargs='+'), 4471 dict(choices='ab')]: 4472 self.assertTypeError('-x', action=action, **attrs) 4473 4474 def test_no_argument_no_const_actions(self): 4475 # options with zero arguments 4476 for action in ['store_true', 'store_false', 'count']: 4477 4478 # const is always disallowed 4479 self.assertTypeError('-x', const='foo', action=action) 4480 4481 # nargs is always disallowed 4482 self.assertTypeError('-x', nargs='*', action=action) 4483 4484 def test_more_than_one_argument_actions(self): 4485 for action in ['store', 'append']: 4486 4487 # nargs=0 is disallowed 4488 self.assertValueError('-x', nargs=0, action=action) 4489 self.assertValueError('spam', nargs=0, action=action) 4490 4491 # const is disallowed with non-optional arguments 4492 for nargs in [1, '*', '+']: 4493 self.assertValueError('-x', const='foo', 4494 nargs=nargs, action=action) 4495 self.assertValueError('spam', const='foo', 4496 nargs=nargs, action=action) 4497 4498 def test_required_const_actions(self): 4499 for action in ['store_const', 'append_const']: 4500 4501 # nargs is always disallowed 4502 self.assertTypeError('-x', nargs='+', action=action) 4503 4504 def test_parsers_action_missing_params(self): 4505 self.assertTypeError('command', action='parsers') 4506 self.assertTypeError('command', action='parsers', prog='PROG') 4507 self.assertTypeError('command', action='parsers', 4508 parser_class=argparse.ArgumentParser) 4509 4510 def test_required_positional(self): 4511 self.assertTypeError('foo', required=True) 4512 4513 def test_user_defined_action(self): 4514 4515 class Success(Exception): 4516 pass 4517 4518 class Action(object): 4519 4520 def __init__(self, 4521 option_strings, 4522 dest, 4523 const, 4524 default, 4525 required=False): 4526 if dest == 'spam': 4527 if const is Success: 4528 if default is Success: 4529 raise Success() 4530 4531 def __call__(self, *args, **kwargs): 4532 pass 4533 4534 parser = argparse.ArgumentParser() 4535 self.assertRaises(Success, parser.add_argument, '--spam', 4536 action=Action, default=Success, const=Success) 4537 self.assertRaises(Success, parser.add_argument, 'spam', 4538 action=Action, default=Success, const=Success) 4539 4540# ================================ 4541# Actions returned by add_argument 4542# ================================ 4543 4544class TestActionsReturned(TestCase): 4545 4546 def test_dest(self): 4547 parser = argparse.ArgumentParser() 4548 action = parser.add_argument('--foo') 4549 self.assertEqual(action.dest, 'foo') 4550 action = parser.add_argument('-b', '--bar') 4551 self.assertEqual(action.dest, 'bar') 4552 action = parser.add_argument('-x', '-y') 4553 self.assertEqual(action.dest, 'x') 4554 4555 def test_misc(self): 4556 parser = argparse.ArgumentParser() 4557 action = parser.add_argument('--foo', nargs='?', const=42, 4558 default=84, type=int, choices=[1, 2], 4559 help='FOO', metavar='BAR', dest='baz') 4560 self.assertEqual(action.nargs, '?') 4561 self.assertEqual(action.const, 42) 4562 self.assertEqual(action.default, 84) 4563 self.assertEqual(action.type, int) 4564 self.assertEqual(action.choices, [1, 2]) 4565 self.assertEqual(action.help, 'FOO') 4566 self.assertEqual(action.metavar, 'BAR') 4567 self.assertEqual(action.dest, 'baz') 4568 4569 4570# ================================ 4571# Argument conflict handling tests 4572# ================================ 4573 4574class TestConflictHandling(TestCase): 4575 4576 def test_bad_type(self): 4577 self.assertRaises(ValueError, argparse.ArgumentParser, 4578 conflict_handler='foo') 4579 4580 def test_conflict_error(self): 4581 parser = argparse.ArgumentParser() 4582 parser.add_argument('-x') 4583 self.assertRaises(argparse.ArgumentError, 4584 parser.add_argument, '-x') 4585 parser.add_argument('--spam') 4586 self.assertRaises(argparse.ArgumentError, 4587 parser.add_argument, '--spam') 4588 4589 def test_resolve_error(self): 4590 get_parser = argparse.ArgumentParser 4591 parser = get_parser(prog='PROG', conflict_handler='resolve') 4592 4593 parser.add_argument('-x', help='OLD X') 4594 parser.add_argument('-x', help='NEW X') 4595 self.assertEqual(parser.format_help(), textwrap.dedent('''\ 4596 usage: PROG [-h] [-x X] 4597 4598 optional arguments: 4599 -h, --help show this help message and exit 4600 -x X NEW X 4601 ''')) 4602 4603 parser.add_argument('--spam', metavar='OLD_SPAM') 4604 parser.add_argument('--spam', metavar='NEW_SPAM') 4605 self.assertEqual(parser.format_help(), textwrap.dedent('''\ 4606 usage: PROG [-h] [-x X] [--spam NEW_SPAM] 4607 4608 optional arguments: 4609 -h, --help show this help message and exit 4610 -x X NEW X 4611 --spam NEW_SPAM 4612 ''')) 4613 4614 4615# ============================= 4616# Help and Version option tests 4617# ============================= 4618 4619class TestOptionalsHelpVersionActions(TestCase): 4620 """Test the help and version actions""" 4621 4622 def assertPrintHelpExit(self, parser, args_str): 4623 with self.assertRaises(ArgumentParserError) as cm: 4624 parser.parse_args(args_str.split()) 4625 self.assertEqual(parser.format_help(), cm.exception.stdout) 4626 4627 def assertArgumentParserError(self, parser, *args): 4628 self.assertRaises(ArgumentParserError, parser.parse_args, args) 4629 4630 def test_version(self): 4631 parser = ErrorRaisingArgumentParser() 4632 parser.add_argument('-v', '--version', action='version', version='1.0') 4633 self.assertPrintHelpExit(parser, '-h') 4634 self.assertPrintHelpExit(parser, '--help') 4635 self.assertRaises(AttributeError, getattr, parser, 'format_version') 4636 4637 def test_version_format(self): 4638 parser = ErrorRaisingArgumentParser(prog='PPP') 4639 parser.add_argument('-v', '--version', action='version', version='%(prog)s 3.5') 4640 with self.assertRaises(ArgumentParserError) as cm: 4641 parser.parse_args(['-v']) 4642 self.assertEqual('PPP 3.5\n', cm.exception.stdout) 4643 4644 def test_version_no_help(self): 4645 parser = ErrorRaisingArgumentParser(add_help=False) 4646 parser.add_argument('-v', '--version', action='version', version='1.0') 4647 self.assertArgumentParserError(parser, '-h') 4648 self.assertArgumentParserError(parser, '--help') 4649 self.assertRaises(AttributeError, getattr, parser, 'format_version') 4650 4651 def test_version_action(self): 4652 parser = ErrorRaisingArgumentParser(prog='XXX') 4653 parser.add_argument('-V', action='version', version='%(prog)s 3.7') 4654 with self.assertRaises(ArgumentParserError) as cm: 4655 parser.parse_args(['-V']) 4656 self.assertEqual('XXX 3.7\n', cm.exception.stdout) 4657 4658 def test_no_help(self): 4659 parser = ErrorRaisingArgumentParser(add_help=False) 4660 self.assertArgumentParserError(parser, '-h') 4661 self.assertArgumentParserError(parser, '--help') 4662 self.assertArgumentParserError(parser, '-v') 4663 self.assertArgumentParserError(parser, '--version') 4664 4665 def test_alternate_help_version(self): 4666 parser = ErrorRaisingArgumentParser() 4667 parser.add_argument('-x', action='help') 4668 parser.add_argument('-y', action='version') 4669 self.assertPrintHelpExit(parser, '-x') 4670 self.assertArgumentParserError(parser, '-v') 4671 self.assertArgumentParserError(parser, '--version') 4672 self.assertRaises(AttributeError, getattr, parser, 'format_version') 4673 4674 def test_help_version_extra_arguments(self): 4675 parser = ErrorRaisingArgumentParser() 4676 parser.add_argument('--version', action='version', version='1.0') 4677 parser.add_argument('-x', action='store_true') 4678 parser.add_argument('y') 4679 4680 # try all combinations of valid prefixes and suffixes 4681 valid_prefixes = ['', '-x', 'foo', '-x bar', 'baz -x'] 4682 valid_suffixes = valid_prefixes + ['--bad-option', 'foo bar baz'] 4683 for prefix in valid_prefixes: 4684 for suffix in valid_suffixes: 4685 format = '%s %%s %s' % (prefix, suffix) 4686 self.assertPrintHelpExit(parser, format % '-h') 4687 self.assertPrintHelpExit(parser, format % '--help') 4688 self.assertRaises(AttributeError, getattr, parser, 'format_version') 4689 4690 4691# ====================== 4692# str() and repr() tests 4693# ====================== 4694 4695class TestStrings(TestCase): 4696 """Test str() and repr() on Optionals and Positionals""" 4697 4698 def assertStringEqual(self, obj, result_string): 4699 for func in [str, repr]: 4700 self.assertEqual(func(obj), result_string) 4701 4702 def test_optional(self): 4703 option = argparse.Action( 4704 option_strings=['--foo', '-a', '-b'], 4705 dest='b', 4706 type='int', 4707 nargs='+', 4708 default=42, 4709 choices=[1, 2, 3], 4710 help='HELP', 4711 metavar='METAVAR') 4712 string = ( 4713 "Action(option_strings=['--foo', '-a', '-b'], dest='b', " 4714 "nargs='+', const=None, default=42, type='int', " 4715 "choices=[1, 2, 3], help='HELP', metavar='METAVAR')") 4716 self.assertStringEqual(option, string) 4717 4718 def test_argument(self): 4719 argument = argparse.Action( 4720 option_strings=[], 4721 dest='x', 4722 type=float, 4723 nargs='?', 4724 default=2.5, 4725 choices=[0.5, 1.5, 2.5], 4726 help='H HH H', 4727 metavar='MV MV MV') 4728 string = ( 4729 "Action(option_strings=[], dest='x', nargs='?', " 4730 "const=None, default=2.5, type=%r, choices=[0.5, 1.5, 2.5], " 4731 "help='H HH H', metavar='MV MV MV')" % float) 4732 self.assertStringEqual(argument, string) 4733 4734 def test_namespace(self): 4735 ns = argparse.Namespace(foo=42, bar='spam') 4736 string = "Namespace(foo=42, bar='spam')" 4737 self.assertStringEqual(ns, string) 4738 4739 def test_namespace_starkwargs_notidentifier(self): 4740 ns = argparse.Namespace(**{'"': 'quote'}) 4741 string = """Namespace(**{'"': 'quote'})""" 4742 self.assertStringEqual(ns, string) 4743 4744 def test_namespace_kwargs_and_starkwargs_notidentifier(self): 4745 ns = argparse.Namespace(a=1, **{'"': 'quote'}) 4746 string = """Namespace(a=1, **{'"': 'quote'})""" 4747 self.assertStringEqual(ns, string) 4748 4749 def test_namespace_starkwargs_identifier(self): 4750 ns = argparse.Namespace(**{'valid': True}) 4751 string = "Namespace(valid=True)" 4752 self.assertStringEqual(ns, string) 4753 4754 def test_parser(self): 4755 parser = argparse.ArgumentParser(prog='PROG') 4756 string = ( 4757 "ArgumentParser(prog='PROG', usage=None, description=None, " 4758 "formatter_class=%r, conflict_handler='error', " 4759 "add_help=True)" % argparse.HelpFormatter) 4760 self.assertStringEqual(parser, string) 4761 4762# =============== 4763# Namespace tests 4764# =============== 4765 4766class TestNamespace(TestCase): 4767 4768 def test_constructor(self): 4769 ns = argparse.Namespace() 4770 self.assertRaises(AttributeError, getattr, ns, 'x') 4771 4772 ns = argparse.Namespace(a=42, b='spam') 4773 self.assertEqual(ns.a, 42) 4774 self.assertEqual(ns.b, 'spam') 4775 4776 def test_equality(self): 4777 ns1 = argparse.Namespace(a=1, b=2) 4778 ns2 = argparse.Namespace(b=2, a=1) 4779 ns3 = argparse.Namespace(a=1) 4780 ns4 = argparse.Namespace(b=2) 4781 4782 self.assertEqual(ns1, ns2) 4783 self.assertNotEqual(ns1, ns3) 4784 self.assertNotEqual(ns1, ns4) 4785 self.assertNotEqual(ns2, ns3) 4786 self.assertNotEqual(ns2, ns4) 4787 self.assertTrue(ns1 != ns3) 4788 self.assertTrue(ns1 != ns4) 4789 self.assertTrue(ns2 != ns3) 4790 self.assertTrue(ns2 != ns4) 4791 4792 def test_equality_returns_notimplemented(self): 4793 # See issue 21481 4794 ns = argparse.Namespace(a=1, b=2) 4795 self.assertIs(ns.__eq__(None), NotImplemented) 4796 self.assertIs(ns.__ne__(None), NotImplemented) 4797 4798 4799# =================== 4800# File encoding tests 4801# =================== 4802 4803class TestEncoding(TestCase): 4804 4805 def _test_module_encoding(self, path): 4806 path, _ = os.path.splitext(path) 4807 path += ".py" 4808 with open(path, 'r', encoding='utf-8') as f: 4809 f.read() 4810 4811 def test_argparse_module_encoding(self): 4812 self._test_module_encoding(argparse.__file__) 4813 4814 def test_test_argparse_module_encoding(self): 4815 self._test_module_encoding(__file__) 4816 4817# =================== 4818# ArgumentError tests 4819# =================== 4820 4821class TestArgumentError(TestCase): 4822 4823 def test_argument_error(self): 4824 msg = "my error here" 4825 error = argparse.ArgumentError(None, msg) 4826 self.assertEqual(str(error), msg) 4827 4828# ======================= 4829# ArgumentTypeError tests 4830# ======================= 4831 4832class TestArgumentTypeError(TestCase): 4833 4834 def test_argument_type_error(self): 4835 4836 def spam(string): 4837 raise argparse.ArgumentTypeError('spam!') 4838 4839 parser = ErrorRaisingArgumentParser(prog='PROG', add_help=False) 4840 parser.add_argument('x', type=spam) 4841 with self.assertRaises(ArgumentParserError) as cm: 4842 parser.parse_args(['XXX']) 4843 self.assertEqual('usage: PROG x\nPROG: error: argument x: spam!\n', 4844 cm.exception.stderr) 4845 4846# ========================= 4847# MessageContentError tests 4848# ========================= 4849 4850class TestMessageContentError(TestCase): 4851 4852 def test_missing_argument_name_in_message(self): 4853 parser = ErrorRaisingArgumentParser(prog='PROG', usage='') 4854 parser.add_argument('req_pos', type=str) 4855 parser.add_argument('-req_opt', type=int, required=True) 4856 parser.add_argument('need_one', type=str, nargs='+') 4857 4858 with self.assertRaises(ArgumentParserError) as cm: 4859 parser.parse_args([]) 4860 msg = str(cm.exception) 4861 self.assertRegex(msg, 'req_pos') 4862 self.assertRegex(msg, 'req_opt') 4863 self.assertRegex(msg, 'need_one') 4864 with self.assertRaises(ArgumentParserError) as cm: 4865 parser.parse_args(['myXargument']) 4866 msg = str(cm.exception) 4867 self.assertNotIn(msg, 'req_pos') 4868 self.assertRegex(msg, 'req_opt') 4869 self.assertRegex(msg, 'need_one') 4870 with self.assertRaises(ArgumentParserError) as cm: 4871 parser.parse_args(['myXargument', '-req_opt=1']) 4872 msg = str(cm.exception) 4873 self.assertNotIn(msg, 'req_pos') 4874 self.assertNotIn(msg, 'req_opt') 4875 self.assertRegex(msg, 'need_one') 4876 4877 def test_optional_optional_not_in_message(self): 4878 parser = ErrorRaisingArgumentParser(prog='PROG', usage='') 4879 parser.add_argument('req_pos', type=str) 4880 parser.add_argument('--req_opt', type=int, required=True) 4881 parser.add_argument('--opt_opt', type=bool, nargs='?', 4882 default=True) 4883 with self.assertRaises(ArgumentParserError) as cm: 4884 parser.parse_args([]) 4885 msg = str(cm.exception) 4886 self.assertRegex(msg, 'req_pos') 4887 self.assertRegex(msg, 'req_opt') 4888 self.assertNotIn(msg, 'opt_opt') 4889 with self.assertRaises(ArgumentParserError) as cm: 4890 parser.parse_args(['--req_opt=1']) 4891 msg = str(cm.exception) 4892 self.assertRegex(msg, 'req_pos') 4893 self.assertNotIn(msg, 'req_opt') 4894 self.assertNotIn(msg, 'opt_opt') 4895 4896 def test_optional_positional_not_in_message(self): 4897 parser = ErrorRaisingArgumentParser(prog='PROG', usage='') 4898 parser.add_argument('req_pos') 4899 parser.add_argument('optional_positional', nargs='?', default='eggs') 4900 with self.assertRaises(ArgumentParserError) as cm: 4901 parser.parse_args([]) 4902 msg = str(cm.exception) 4903 self.assertRegex(msg, 'req_pos') 4904 self.assertNotIn(msg, 'optional_positional') 4905 4906 4907# ================================================ 4908# Check that the type function is called only once 4909# ================================================ 4910 4911class TestTypeFunctionCallOnlyOnce(TestCase): 4912 4913 def test_type_function_call_only_once(self): 4914 def spam(string_to_convert): 4915 self.assertEqual(string_to_convert, 'spam!') 4916 return 'foo_converted' 4917 4918 parser = argparse.ArgumentParser() 4919 parser.add_argument('--foo', type=spam, default='bar') 4920 args = parser.parse_args('--foo spam!'.split()) 4921 self.assertEqual(NS(foo='foo_converted'), args) 4922 4923# ================================================================== 4924# Check semantics regarding the default argument and type conversion 4925# ================================================================== 4926 4927class TestTypeFunctionCalledOnDefault(TestCase): 4928 4929 def test_type_function_call_with_non_string_default(self): 4930 def spam(int_to_convert): 4931 self.assertEqual(int_to_convert, 0) 4932 return 'foo_converted' 4933 4934 parser = argparse.ArgumentParser() 4935 parser.add_argument('--foo', type=spam, default=0) 4936 args = parser.parse_args([]) 4937 # foo should *not* be converted because its default is not a string. 4938 self.assertEqual(NS(foo=0), args) 4939 4940 def test_type_function_call_with_string_default(self): 4941 def spam(int_to_convert): 4942 return 'foo_converted' 4943 4944 parser = argparse.ArgumentParser() 4945 parser.add_argument('--foo', type=spam, default='0') 4946 args = parser.parse_args([]) 4947 # foo is converted because its default is a string. 4948 self.assertEqual(NS(foo='foo_converted'), args) 4949 4950 def test_no_double_type_conversion_of_default(self): 4951 def extend(str_to_convert): 4952 return str_to_convert + '*' 4953 4954 parser = argparse.ArgumentParser() 4955 parser.add_argument('--test', type=extend, default='*') 4956 args = parser.parse_args([]) 4957 # The test argument will be two stars, one coming from the default 4958 # value and one coming from the type conversion being called exactly 4959 # once. 4960 self.assertEqual(NS(test='**'), args) 4961 4962 def test_issue_15906(self): 4963 # Issue #15906: When action='append', type=str, default=[] are 4964 # providing, the dest value was the string representation "[]" when it 4965 # should have been an empty list. 4966 parser = argparse.ArgumentParser() 4967 parser.add_argument('--test', dest='test', type=str, 4968 default=[], action='append') 4969 args = parser.parse_args([]) 4970 self.assertEqual(args.test, []) 4971 4972# ====================== 4973# parse_known_args tests 4974# ====================== 4975 4976class TestParseKnownArgs(TestCase): 4977 4978 def test_arguments_tuple(self): 4979 parser = argparse.ArgumentParser() 4980 parser.parse_args(()) 4981 4982 def test_arguments_list(self): 4983 parser = argparse.ArgumentParser() 4984 parser.parse_args([]) 4985 4986 def test_arguments_tuple_positional(self): 4987 parser = argparse.ArgumentParser() 4988 parser.add_argument('x') 4989 parser.parse_args(('x',)) 4990 4991 def test_arguments_list_positional(self): 4992 parser = argparse.ArgumentParser() 4993 parser.add_argument('x') 4994 parser.parse_args(['x']) 4995 4996 def test_optionals(self): 4997 parser = argparse.ArgumentParser() 4998 parser.add_argument('--foo') 4999 args, extras = parser.parse_known_args('--foo F --bar --baz'.split()) 5000 self.assertEqual(NS(foo='F'), args) 5001 self.assertEqual(['--bar', '--baz'], extras) 5002 5003 def test_mixed(self): 5004 parser = argparse.ArgumentParser() 5005 parser.add_argument('-v', nargs='?', const=1, type=int) 5006 parser.add_argument('--spam', action='store_false') 5007 parser.add_argument('badger') 5008 5009 argv = ["B", "C", "--foo", "-v", "3", "4"] 5010 args, extras = parser.parse_known_args(argv) 5011 self.assertEqual(NS(v=3, spam=True, badger="B"), args) 5012 self.assertEqual(["C", "--foo", "4"], extras) 5013 5014# =========================== 5015# parse_intermixed_args tests 5016# =========================== 5017 5018class TestIntermixedArgs(TestCase): 5019 def test_basic(self): 5020 # test parsing intermixed optionals and positionals 5021 parser = argparse.ArgumentParser(prog='PROG') 5022 parser.add_argument('--foo', dest='foo') 5023 bar = parser.add_argument('--bar', dest='bar', required=True) 5024 parser.add_argument('cmd') 5025 parser.add_argument('rest', nargs='*', type=int) 5026 argv = 'cmd --foo x 1 --bar y 2 3'.split() 5027 args = parser.parse_intermixed_args(argv) 5028 # rest gets [1,2,3] despite the foo and bar strings 5029 self.assertEqual(NS(bar='y', cmd='cmd', foo='x', rest=[1, 2, 3]), args) 5030 5031 args, extras = parser.parse_known_args(argv) 5032 # cannot parse the '1,2,3' 5033 self.assertEqual(NS(bar='y', cmd='cmd', foo='x', rest=[]), args) 5034 self.assertEqual(["1", "2", "3"], extras) 5035 5036 argv = 'cmd --foo x 1 --error 2 --bar y 3'.split() 5037 args, extras = parser.parse_known_intermixed_args(argv) 5038 # unknown optionals go into extras 5039 self.assertEqual(NS(bar='y', cmd='cmd', foo='x', rest=[1]), args) 5040 self.assertEqual(['--error', '2', '3'], extras) 5041 5042 # restores attributes that were temporarily changed 5043 self.assertIsNone(parser.usage) 5044 self.assertEqual(bar.required, True) 5045 5046 def test_remainder(self): 5047 # Intermixed and remainder are incompatible 5048 parser = ErrorRaisingArgumentParser(prog='PROG') 5049 parser.add_argument('-z') 5050 parser.add_argument('x') 5051 parser.add_argument('y', nargs='...') 5052 argv = 'X A B -z Z'.split() 5053 # intermixed fails with '...' (also 'A...') 5054 # self.assertRaises(TypeError, parser.parse_intermixed_args, argv) 5055 with self.assertRaises(TypeError) as cm: 5056 parser.parse_intermixed_args(argv) 5057 self.assertRegex(str(cm.exception), r'\.\.\.') 5058 5059 def test_exclusive(self): 5060 # mutually exclusive group; intermixed works fine 5061 parser = ErrorRaisingArgumentParser(prog='PROG') 5062 group = parser.add_mutually_exclusive_group(required=True) 5063 group.add_argument('--foo', action='store_true', help='FOO') 5064 group.add_argument('--spam', help='SPAM') 5065 parser.add_argument('badger', nargs='*', default='X', help='BADGER') 5066 args = parser.parse_intermixed_args('1 --foo 2'.split()) 5067 self.assertEqual(NS(badger=['1', '2'], foo=True, spam=None), args) 5068 self.assertRaises(ArgumentParserError, parser.parse_intermixed_args, '1 2'.split()) 5069 self.assertEqual(group.required, True) 5070 5071 def test_exclusive_incompatible(self): 5072 # mutually exclusive group including positional - fail 5073 parser = ErrorRaisingArgumentParser(prog='PROG') 5074 group = parser.add_mutually_exclusive_group(required=True) 5075 group.add_argument('--foo', action='store_true', help='FOO') 5076 group.add_argument('--spam', help='SPAM') 5077 group.add_argument('badger', nargs='*', default='X', help='BADGER') 5078 self.assertRaises(TypeError, parser.parse_intermixed_args, []) 5079 self.assertEqual(group.required, True) 5080 5081class TestIntermixedMessageContentError(TestCase): 5082 # case where Intermixed gives different error message 5083 # error is raised by 1st parsing step 5084 def test_missing_argument_name_in_message(self): 5085 parser = ErrorRaisingArgumentParser(prog='PROG', usage='') 5086 parser.add_argument('req_pos', type=str) 5087 parser.add_argument('-req_opt', type=int, required=True) 5088 5089 with self.assertRaises(ArgumentParserError) as cm: 5090 parser.parse_args([]) 5091 msg = str(cm.exception) 5092 self.assertRegex(msg, 'req_pos') 5093 self.assertRegex(msg, 'req_opt') 5094 5095 with self.assertRaises(ArgumentParserError) as cm: 5096 parser.parse_intermixed_args([]) 5097 msg = str(cm.exception) 5098 self.assertNotRegex(msg, 'req_pos') 5099 self.assertRegex(msg, 'req_opt') 5100 5101# ========================== 5102# add_argument metavar tests 5103# ========================== 5104 5105class TestAddArgumentMetavar(TestCase): 5106 5107 EXPECTED_MESSAGE = "length of metavar tuple does not match nargs" 5108 5109 def do_test_no_exception(self, nargs, metavar): 5110 parser = argparse.ArgumentParser() 5111 parser.add_argument("--foo", nargs=nargs, metavar=metavar) 5112 5113 def do_test_exception(self, nargs, metavar): 5114 parser = argparse.ArgumentParser() 5115 with self.assertRaises(ValueError) as cm: 5116 parser.add_argument("--foo", nargs=nargs, metavar=metavar) 5117 self.assertEqual(cm.exception.args[0], self.EXPECTED_MESSAGE) 5118 5119 # Unit tests for different values of metavar when nargs=None 5120 5121 def test_nargs_None_metavar_string(self): 5122 self.do_test_no_exception(nargs=None, metavar="1") 5123 5124 def test_nargs_None_metavar_length0(self): 5125 self.do_test_exception(nargs=None, metavar=tuple()) 5126 5127 def test_nargs_None_metavar_length1(self): 5128 self.do_test_no_exception(nargs=None, metavar=("1",)) 5129 5130 def test_nargs_None_metavar_length2(self): 5131 self.do_test_exception(nargs=None, metavar=("1", "2")) 5132 5133 def test_nargs_None_metavar_length3(self): 5134 self.do_test_exception(nargs=None, metavar=("1", "2", "3")) 5135 5136 # Unit tests for different values of metavar when nargs=? 5137 5138 def test_nargs_optional_metavar_string(self): 5139 self.do_test_no_exception(nargs="?", metavar="1") 5140 5141 def test_nargs_optional_metavar_length0(self): 5142 self.do_test_exception(nargs="?", metavar=tuple()) 5143 5144 def test_nargs_optional_metavar_length1(self): 5145 self.do_test_no_exception(nargs="?", metavar=("1",)) 5146 5147 def test_nargs_optional_metavar_length2(self): 5148 self.do_test_exception(nargs="?", metavar=("1", "2")) 5149 5150 def test_nargs_optional_metavar_length3(self): 5151 self.do_test_exception(nargs="?", metavar=("1", "2", "3")) 5152 5153 # Unit tests for different values of metavar when nargs=* 5154 5155 def test_nargs_zeroormore_metavar_string(self): 5156 self.do_test_no_exception(nargs="*", metavar="1") 5157 5158 def test_nargs_zeroormore_metavar_length0(self): 5159 self.do_test_exception(nargs="*", metavar=tuple()) 5160 5161 def test_nargs_zeroormore_metavar_length1(self): 5162 self.do_test_no_exception(nargs="*", metavar=("1",)) 5163 5164 def test_nargs_zeroormore_metavar_length2(self): 5165 self.do_test_no_exception(nargs="*", metavar=("1", "2")) 5166 5167 def test_nargs_zeroormore_metavar_length3(self): 5168 self.do_test_exception(nargs="*", metavar=("1", "2", "3")) 5169 5170 # Unit tests for different values of metavar when nargs=+ 5171 5172 def test_nargs_oneormore_metavar_string(self): 5173 self.do_test_no_exception(nargs="+", metavar="1") 5174 5175 def test_nargs_oneormore_metavar_length0(self): 5176 self.do_test_exception(nargs="+", metavar=tuple()) 5177 5178 def test_nargs_oneormore_metavar_length1(self): 5179 self.do_test_exception(nargs="+", metavar=("1",)) 5180 5181 def test_nargs_oneormore_metavar_length2(self): 5182 self.do_test_no_exception(nargs="+", metavar=("1", "2")) 5183 5184 def test_nargs_oneormore_metavar_length3(self): 5185 self.do_test_exception(nargs="+", metavar=("1", "2", "3")) 5186 5187 # Unit tests for different values of metavar when nargs=... 5188 5189 def test_nargs_remainder_metavar_string(self): 5190 self.do_test_no_exception(nargs="...", metavar="1") 5191 5192 def test_nargs_remainder_metavar_length0(self): 5193 self.do_test_no_exception(nargs="...", metavar=tuple()) 5194 5195 def test_nargs_remainder_metavar_length1(self): 5196 self.do_test_no_exception(nargs="...", metavar=("1",)) 5197 5198 def test_nargs_remainder_metavar_length2(self): 5199 self.do_test_no_exception(nargs="...", metavar=("1", "2")) 5200 5201 def test_nargs_remainder_metavar_length3(self): 5202 self.do_test_no_exception(nargs="...", metavar=("1", "2", "3")) 5203 5204 # Unit tests for different values of metavar when nargs=A... 5205 5206 def test_nargs_parser_metavar_string(self): 5207 self.do_test_no_exception(nargs="A...", metavar="1") 5208 5209 def test_nargs_parser_metavar_length0(self): 5210 self.do_test_exception(nargs="A...", metavar=tuple()) 5211 5212 def test_nargs_parser_metavar_length1(self): 5213 self.do_test_no_exception(nargs="A...", metavar=("1",)) 5214 5215 def test_nargs_parser_metavar_length2(self): 5216 self.do_test_exception(nargs="A...", metavar=("1", "2")) 5217 5218 def test_nargs_parser_metavar_length3(self): 5219 self.do_test_exception(nargs="A...", metavar=("1", "2", "3")) 5220 5221 # Unit tests for different values of metavar when nargs=1 5222 5223 def test_nargs_1_metavar_string(self): 5224 self.do_test_no_exception(nargs=1, metavar="1") 5225 5226 def test_nargs_1_metavar_length0(self): 5227 self.do_test_exception(nargs=1, metavar=tuple()) 5228 5229 def test_nargs_1_metavar_length1(self): 5230 self.do_test_no_exception(nargs=1, metavar=("1",)) 5231 5232 def test_nargs_1_metavar_length2(self): 5233 self.do_test_exception(nargs=1, metavar=("1", "2")) 5234 5235 def test_nargs_1_metavar_length3(self): 5236 self.do_test_exception(nargs=1, metavar=("1", "2", "3")) 5237 5238 # Unit tests for different values of metavar when nargs=2 5239 5240 def test_nargs_2_metavar_string(self): 5241 self.do_test_no_exception(nargs=2, metavar="1") 5242 5243 def test_nargs_2_metavar_length0(self): 5244 self.do_test_exception(nargs=2, metavar=tuple()) 5245 5246 def test_nargs_2_metavar_length1(self): 5247 self.do_test_exception(nargs=2, metavar=("1",)) 5248 5249 def test_nargs_2_metavar_length2(self): 5250 self.do_test_no_exception(nargs=2, metavar=("1", "2")) 5251 5252 def test_nargs_2_metavar_length3(self): 5253 self.do_test_exception(nargs=2, metavar=("1", "2", "3")) 5254 5255 # Unit tests for different values of metavar when nargs=3 5256 5257 def test_nargs_3_metavar_string(self): 5258 self.do_test_no_exception(nargs=3, metavar="1") 5259 5260 def test_nargs_3_metavar_length0(self): 5261 self.do_test_exception(nargs=3, metavar=tuple()) 5262 5263 def test_nargs_3_metavar_length1(self): 5264 self.do_test_exception(nargs=3, metavar=("1",)) 5265 5266 def test_nargs_3_metavar_length2(self): 5267 self.do_test_exception(nargs=3, metavar=("1", "2")) 5268 5269 def test_nargs_3_metavar_length3(self): 5270 self.do_test_no_exception(nargs=3, metavar=("1", "2", "3")) 5271 5272 5273class TestInvalidNargs(TestCase): 5274 5275 EXPECTED_INVALID_MESSAGE = "invalid nargs value" 5276 EXPECTED_RANGE_MESSAGE = ("nargs for store actions must be != 0; if you " 5277 "have nothing to store, actions such as store " 5278 "true or store const may be more appropriate") 5279 5280 def do_test_range_exception(self, nargs): 5281 parser = argparse.ArgumentParser() 5282 with self.assertRaises(ValueError) as cm: 5283 parser.add_argument("--foo", nargs=nargs) 5284 self.assertEqual(cm.exception.args[0], self.EXPECTED_RANGE_MESSAGE) 5285 5286 def do_test_invalid_exception(self, nargs): 5287 parser = argparse.ArgumentParser() 5288 with self.assertRaises(ValueError) as cm: 5289 parser.add_argument("--foo", nargs=nargs) 5290 self.assertEqual(cm.exception.args[0], self.EXPECTED_INVALID_MESSAGE) 5291 5292 # Unit tests for different values of nargs 5293 5294 def test_nargs_alphabetic(self): 5295 self.do_test_invalid_exception(nargs='a') 5296 self.do_test_invalid_exception(nargs="abcd") 5297 5298 def test_nargs_zero(self): 5299 self.do_test_range_exception(nargs=0) 5300 5301# ============================ 5302# from argparse import * tests 5303# ============================ 5304 5305class TestImportStar(TestCase): 5306 5307 def test(self): 5308 for name in argparse.__all__: 5309 self.assertTrue(hasattr(argparse, name)) 5310 5311 def test_all_exports_everything_but_modules(self): 5312 items = [ 5313 name 5314 for name, value in vars(argparse).items() 5315 if not (name.startswith("_") or name == 'ngettext') 5316 if not inspect.ismodule(value) 5317 ] 5318 self.assertEqual(sorted(items), sorted(argparse.__all__)) 5319 5320 5321class TestWrappingMetavar(TestCase): 5322 5323 def setUp(self): 5324 super().setUp() 5325 self.parser = ErrorRaisingArgumentParser( 5326 'this_is_spammy_prog_with_a_long_name_sorry_about_the_name' 5327 ) 5328 # this metavar was triggering library assertion errors due to usage 5329 # message formatting incorrectly splitting on the ] chars within 5330 metavar = '<http[s]://example:1234>' 5331 self.parser.add_argument('--proxy', metavar=metavar) 5332 5333 def test_help_with_metavar(self): 5334 help_text = self.parser.format_help() 5335 self.assertEqual(help_text, textwrap.dedent('''\ 5336 usage: this_is_spammy_prog_with_a_long_name_sorry_about_the_name 5337 [-h] [--proxy <http[s]://example:1234>] 5338 5339 optional arguments: 5340 -h, --help show this help message and exit 5341 --proxy <http[s]://example:1234> 5342 ''')) 5343 5344 5345class TestExitOnError(TestCase): 5346 5347 def setUp(self): 5348 self.parser = argparse.ArgumentParser(exit_on_error=False) 5349 self.parser.add_argument('--integers', metavar='N', type=int) 5350 5351 def test_exit_on_error_with_good_args(self): 5352 ns = self.parser.parse_args('--integers 4'.split()) 5353 self.assertEqual(ns, argparse.Namespace(integers=4)) 5354 5355 def test_exit_on_error_with_bad_args(self): 5356 with self.assertRaises(argparse.ArgumentError): 5357 self.parser.parse_args('--integers a'.split()) 5358 5359 5360def test_main(): 5361 support.run_unittest(__name__) 5362 # Remove global references to avoid looking like we have refleaks. 5363 RFile.seen = {} 5364 WFile.seen = set() 5365 5366 5367 5368if __name__ == '__main__': 5369 test_main() 5370