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.support import os_helper 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 = os_helper.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', encoding="utf-8") 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', encoding="utf-8") 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', encoding="utf-8") 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), 1583 'w', encoding="utf-8") as file: 1584 file.write(file_name) 1585 self.create_readonly_file('readonly') 1586 1587 argument_signatures = [ 1588 Sig('-x', type=argparse.FileType()), 1589 Sig('spam', type=argparse.FileType('r')), 1590 ] 1591 failures = ['-x', '', 'non-existent-file.txt'] 1592 successes = [ 1593 ('foo', NS(x=None, spam=RFile('foo'))), 1594 ('-x foo bar', NS(x=RFile('foo'), spam=RFile('bar'))), 1595 ('bar -x foo', NS(x=RFile('foo'), spam=RFile('bar'))), 1596 ('-x - -', NS(x=eq_stdin, spam=eq_stdin)), 1597 ('readonly', NS(x=None, spam=RFile('readonly'))), 1598 ] 1599 1600class TestFileTypeDefaults(TempDirMixin, ParserTestCase): 1601 """Test that a file is not created unless the default is needed""" 1602 def setUp(self): 1603 super(TestFileTypeDefaults, self).setUp() 1604 file = open(os.path.join(self.temp_dir, 'good'), 'w', encoding="utf-8") 1605 file.write('good') 1606 file.close() 1607 1608 argument_signatures = [ 1609 Sig('-c', type=argparse.FileType('r'), default='no-file.txt'), 1610 ] 1611 # should provoke no such file error 1612 failures = [''] 1613 # should not provoke error because default file is created 1614 successes = [('-c good', NS(c=RFile('good')))] 1615 1616 1617class TestFileTypeRB(TempDirMixin, ParserTestCase): 1618 """Test the FileType option/argument type for reading files""" 1619 1620 def setUp(self): 1621 super(TestFileTypeRB, self).setUp() 1622 for file_name in ['foo', 'bar']: 1623 with open(os.path.join(self.temp_dir, file_name), 1624 'w', encoding="utf-8") as file: 1625 file.write(file_name) 1626 1627 argument_signatures = [ 1628 Sig('-x', type=argparse.FileType('rb')), 1629 Sig('spam', type=argparse.FileType('rb')), 1630 ] 1631 failures = ['-x', ''] 1632 successes = [ 1633 ('foo', NS(x=None, spam=RFile('foo'))), 1634 ('-x foo bar', NS(x=RFile('foo'), spam=RFile('bar'))), 1635 ('bar -x foo', NS(x=RFile('foo'), spam=RFile('bar'))), 1636 ('-x - -', NS(x=eq_stdin, spam=eq_stdin)), 1637 ] 1638 1639 1640class WFile(object): 1641 seen = set() 1642 1643 def __init__(self, name): 1644 self.name = name 1645 1646 def __eq__(self, other): 1647 if other not in self.seen: 1648 text = 'Check that file is writable.' 1649 if 'b' in other.mode: 1650 text = text.encode('ascii') 1651 other.write(text) 1652 other.close() 1653 self.seen.add(other) 1654 return self.name == other.name 1655 1656 1657@unittest.skipIf(hasattr(os, 'geteuid') and os.geteuid() == 0, 1658 "non-root user required") 1659class TestFileTypeW(TempDirMixin, ParserTestCase): 1660 """Test the FileType option/argument type for writing files""" 1661 1662 def setUp(self): 1663 super(TestFileTypeW, self).setUp() 1664 self.create_readonly_file('readonly') 1665 1666 argument_signatures = [ 1667 Sig('-x', type=argparse.FileType('w')), 1668 Sig('spam', type=argparse.FileType('w')), 1669 ] 1670 failures = ['-x', '', 'readonly'] 1671 successes = [ 1672 ('foo', NS(x=None, spam=WFile('foo'))), 1673 ('-x foo bar', NS(x=WFile('foo'), spam=WFile('bar'))), 1674 ('bar -x foo', NS(x=WFile('foo'), spam=WFile('bar'))), 1675 ('-x - -', NS(x=eq_stdout, spam=eq_stdout)), 1676 ] 1677 1678 1679class TestFileTypeWB(TempDirMixin, ParserTestCase): 1680 1681 argument_signatures = [ 1682 Sig('-x', type=argparse.FileType('wb')), 1683 Sig('spam', type=argparse.FileType('wb')), 1684 ] 1685 failures = ['-x', ''] 1686 successes = [ 1687 ('foo', NS(x=None, spam=WFile('foo'))), 1688 ('-x foo bar', NS(x=WFile('foo'), spam=WFile('bar'))), 1689 ('bar -x foo', NS(x=WFile('foo'), spam=WFile('bar'))), 1690 ('-x - -', NS(x=eq_stdout, spam=eq_stdout)), 1691 ] 1692 1693 1694class TestFileTypeOpenArgs(TestCase): 1695 """Test that open (the builtin) is correctly called""" 1696 1697 def test_open_args(self): 1698 FT = argparse.FileType 1699 cases = [ 1700 (FT('rb'), ('rb', -1, None, None)), 1701 (FT('w', 1), ('w', 1, None, None)), 1702 (FT('w', errors='replace'), ('w', -1, None, 'replace')), 1703 (FT('wb', encoding='big5'), ('wb', -1, 'big5', None)), 1704 (FT('w', 0, 'l1', 'strict'), ('w', 0, 'l1', 'strict')), 1705 ] 1706 with mock.patch('builtins.open') as m: 1707 for type, args in cases: 1708 type('foo') 1709 m.assert_called_with('foo', *args) 1710 1711 1712class TestFileTypeMissingInitialization(TestCase): 1713 """ 1714 Test that add_argument throws an error if FileType class 1715 object was passed instead of instance of FileType 1716 """ 1717 1718 def test(self): 1719 parser = argparse.ArgumentParser() 1720 with self.assertRaises(ValueError) as cm: 1721 parser.add_argument('-x', type=argparse.FileType) 1722 1723 self.assertEqual( 1724 '%r is a FileType class object, instance of it must be passed' 1725 % (argparse.FileType,), 1726 str(cm.exception) 1727 ) 1728 1729 1730class TestTypeCallable(ParserTestCase): 1731 """Test some callables as option/argument types""" 1732 1733 argument_signatures = [ 1734 Sig('--eggs', type=complex), 1735 Sig('spam', type=float), 1736 ] 1737 failures = ['a', '42j', '--eggs a', '--eggs 2i'] 1738 successes = [ 1739 ('--eggs=42 42', NS(eggs=42, spam=42.0)), 1740 ('--eggs 2j -- -1.5', NS(eggs=2j, spam=-1.5)), 1741 ('1024.675', NS(eggs=None, spam=1024.675)), 1742 ] 1743 1744 1745class TestTypeUserDefined(ParserTestCase): 1746 """Test a user-defined option/argument type""" 1747 1748 class MyType(TestCase): 1749 1750 def __init__(self, value): 1751 self.value = value 1752 1753 def __eq__(self, other): 1754 return (type(self), self.value) == (type(other), other.value) 1755 1756 argument_signatures = [ 1757 Sig('-x', type=MyType), 1758 Sig('spam', type=MyType), 1759 ] 1760 failures = [] 1761 successes = [ 1762 ('a -x b', NS(x=MyType('b'), spam=MyType('a'))), 1763 ('-xf g', NS(x=MyType('f'), spam=MyType('g'))), 1764 ] 1765 1766 1767class TestTypeClassicClass(ParserTestCase): 1768 """Test a classic class type""" 1769 1770 class C: 1771 1772 def __init__(self, value): 1773 self.value = value 1774 1775 def __eq__(self, other): 1776 return (type(self), self.value) == (type(other), other.value) 1777 1778 argument_signatures = [ 1779 Sig('-x', type=C), 1780 Sig('spam', type=C), 1781 ] 1782 failures = [] 1783 successes = [ 1784 ('a -x b', NS(x=C('b'), spam=C('a'))), 1785 ('-xf g', NS(x=C('f'), spam=C('g'))), 1786 ] 1787 1788 1789class TestTypeRegistration(TestCase): 1790 """Test a user-defined type by registering it""" 1791 1792 def test(self): 1793 1794 def get_my_type(string): 1795 return 'my_type{%s}' % string 1796 1797 parser = argparse.ArgumentParser() 1798 parser.register('type', 'my_type', get_my_type) 1799 parser.add_argument('-x', type='my_type') 1800 parser.add_argument('y', type='my_type') 1801 1802 self.assertEqual(parser.parse_args('1'.split()), 1803 NS(x=None, y='my_type{1}')) 1804 self.assertEqual(parser.parse_args('-x 1 42'.split()), 1805 NS(x='my_type{1}', y='my_type{42}')) 1806 1807 1808# ============ 1809# Action tests 1810# ============ 1811 1812class TestActionUserDefined(ParserTestCase): 1813 """Test a user-defined option/argument action""" 1814 1815 class OptionalAction(argparse.Action): 1816 1817 def __call__(self, parser, namespace, value, option_string=None): 1818 try: 1819 # check destination and option string 1820 assert self.dest == 'spam', 'dest: %s' % self.dest 1821 assert option_string == '-s', 'flag: %s' % option_string 1822 # when option is before argument, badger=2, and when 1823 # option is after argument, badger=<whatever was set> 1824 expected_ns = NS(spam=0.25) 1825 if value in [0.125, 0.625]: 1826 expected_ns.badger = 2 1827 elif value in [2.0]: 1828 expected_ns.badger = 84 1829 else: 1830 raise AssertionError('value: %s' % value) 1831 assert expected_ns == namespace, ('expected %s, got %s' % 1832 (expected_ns, namespace)) 1833 except AssertionError: 1834 e = sys.exc_info()[1] 1835 raise ArgumentParserError('opt_action failed: %s' % e) 1836 setattr(namespace, 'spam', value) 1837 1838 class PositionalAction(argparse.Action): 1839 1840 def __call__(self, parser, namespace, value, option_string=None): 1841 try: 1842 assert option_string is None, ('option_string: %s' % 1843 option_string) 1844 # check destination 1845 assert self.dest == 'badger', 'dest: %s' % self.dest 1846 # when argument is before option, spam=0.25, and when 1847 # option is after argument, spam=<whatever was set> 1848 expected_ns = NS(badger=2) 1849 if value in [42, 84]: 1850 expected_ns.spam = 0.25 1851 elif value in [1]: 1852 expected_ns.spam = 0.625 1853 elif value in [2]: 1854 expected_ns.spam = 0.125 1855 else: 1856 raise AssertionError('value: %s' % value) 1857 assert expected_ns == namespace, ('expected %s, got %s' % 1858 (expected_ns, namespace)) 1859 except AssertionError: 1860 e = sys.exc_info()[1] 1861 raise ArgumentParserError('arg_action failed: %s' % e) 1862 setattr(namespace, 'badger', value) 1863 1864 argument_signatures = [ 1865 Sig('-s', dest='spam', action=OptionalAction, 1866 type=float, default=0.25), 1867 Sig('badger', action=PositionalAction, 1868 type=int, nargs='?', default=2), 1869 ] 1870 failures = [] 1871 successes = [ 1872 ('-s0.125', NS(spam=0.125, badger=2)), 1873 ('42', NS(spam=0.25, badger=42)), 1874 ('-s 0.625 1', NS(spam=0.625, badger=1)), 1875 ('84 -s2', NS(spam=2.0, badger=84)), 1876 ] 1877 1878 1879class TestActionRegistration(TestCase): 1880 """Test a user-defined action supplied by registering it""" 1881 1882 class MyAction(argparse.Action): 1883 1884 def __call__(self, parser, namespace, values, option_string=None): 1885 setattr(namespace, self.dest, 'foo[%s]' % values) 1886 1887 def test(self): 1888 1889 parser = argparse.ArgumentParser() 1890 parser.register('action', 'my_action', self.MyAction) 1891 parser.add_argument('badger', action='my_action') 1892 1893 self.assertEqual(parser.parse_args(['1']), NS(badger='foo[1]')) 1894 self.assertEqual(parser.parse_args(['42']), NS(badger='foo[42]')) 1895 1896 1897class TestActionExtend(ParserTestCase): 1898 argument_signatures = [ 1899 Sig('--foo', action="extend", nargs="+", type=str), 1900 ] 1901 failures = () 1902 successes = [ 1903 ('--foo f1 --foo f2 f3 f4', NS(foo=['f1', 'f2', 'f3', 'f4'])), 1904 ] 1905 1906# ================ 1907# Subparsers tests 1908# ================ 1909 1910class TestAddSubparsers(TestCase): 1911 """Test the add_subparsers method""" 1912 1913 def assertArgumentParserError(self, *args, **kwargs): 1914 self.assertRaises(ArgumentParserError, *args, **kwargs) 1915 1916 def _get_parser(self, subparser_help=False, prefix_chars=None, 1917 aliases=False): 1918 # create a parser with a subparsers argument 1919 if prefix_chars: 1920 parser = ErrorRaisingArgumentParser( 1921 prog='PROG', description='main description', prefix_chars=prefix_chars) 1922 parser.add_argument( 1923 prefix_chars[0] * 2 + 'foo', action='store_true', help='foo help') 1924 else: 1925 parser = ErrorRaisingArgumentParser( 1926 prog='PROG', description='main description') 1927 parser.add_argument( 1928 '--foo', action='store_true', help='foo help') 1929 parser.add_argument( 1930 'bar', type=float, help='bar help') 1931 1932 # check that only one subparsers argument can be added 1933 subparsers_kwargs = {'required': False} 1934 if aliases: 1935 subparsers_kwargs['metavar'] = 'COMMAND' 1936 subparsers_kwargs['title'] = 'commands' 1937 else: 1938 subparsers_kwargs['help'] = 'command help' 1939 subparsers = parser.add_subparsers(**subparsers_kwargs) 1940 self.assertArgumentParserError(parser.add_subparsers) 1941 1942 # add first sub-parser 1943 parser1_kwargs = dict(description='1 description') 1944 if subparser_help: 1945 parser1_kwargs['help'] = '1 help' 1946 if aliases: 1947 parser1_kwargs['aliases'] = ['1alias1', '1alias2'] 1948 parser1 = subparsers.add_parser('1', **parser1_kwargs) 1949 parser1.add_argument('-w', type=int, help='w help') 1950 parser1.add_argument('x', choices='abc', help='x help') 1951 1952 # add second sub-parser 1953 parser2_kwargs = dict(description='2 description') 1954 if subparser_help: 1955 parser2_kwargs['help'] = '2 help' 1956 parser2 = subparsers.add_parser('2', **parser2_kwargs) 1957 parser2.add_argument('-y', choices='123', help='y help') 1958 parser2.add_argument('z', type=complex, nargs='*', help='z help') 1959 1960 # add third sub-parser 1961 parser3_kwargs = dict(description='3 description') 1962 if subparser_help: 1963 parser3_kwargs['help'] = '3 help' 1964 parser3 = subparsers.add_parser('3', **parser3_kwargs) 1965 parser3.add_argument('t', type=int, help='t help') 1966 parser3.add_argument('u', nargs='...', help='u help') 1967 1968 # return the main parser 1969 return parser 1970 1971 def setUp(self): 1972 super().setUp() 1973 self.parser = self._get_parser() 1974 self.command_help_parser = self._get_parser(subparser_help=True) 1975 1976 def test_parse_args_failures(self): 1977 # check some failure cases: 1978 for args_str in ['', 'a', 'a a', '0.5 a', '0.5 1', 1979 '0.5 1 -y', '0.5 2 -w']: 1980 args = args_str.split() 1981 self.assertArgumentParserError(self.parser.parse_args, args) 1982 1983 def test_parse_args(self): 1984 # check some non-failure cases: 1985 self.assertEqual( 1986 self.parser.parse_args('0.5 1 b -w 7'.split()), 1987 NS(foo=False, bar=0.5, w=7, x='b'), 1988 ) 1989 self.assertEqual( 1990 self.parser.parse_args('0.25 --foo 2 -y 2 3j -- -1j'.split()), 1991 NS(foo=True, bar=0.25, y='2', z=[3j, -1j]), 1992 ) 1993 self.assertEqual( 1994 self.parser.parse_args('--foo 0.125 1 c'.split()), 1995 NS(foo=True, bar=0.125, w=None, x='c'), 1996 ) 1997 self.assertEqual( 1998 self.parser.parse_args('-1.5 3 11 -- a --foo 7 -- b'.split()), 1999 NS(foo=False, bar=-1.5, t=11, u=['a', '--foo', '7', '--', 'b']), 2000 ) 2001 2002 def test_parse_known_args(self): 2003 self.assertEqual( 2004 self.parser.parse_known_args('0.5 1 b -w 7'.split()), 2005 (NS(foo=False, bar=0.5, w=7, x='b'), []), 2006 ) 2007 self.assertEqual( 2008 self.parser.parse_known_args('0.5 -p 1 b -w 7'.split()), 2009 (NS(foo=False, bar=0.5, w=7, x='b'), ['-p']), 2010 ) 2011 self.assertEqual( 2012 self.parser.parse_known_args('0.5 1 b -w 7 -p'.split()), 2013 (NS(foo=False, bar=0.5, w=7, x='b'), ['-p']), 2014 ) 2015 self.assertEqual( 2016 self.parser.parse_known_args('0.5 1 b -q -rs -w 7'.split()), 2017 (NS(foo=False, bar=0.5, w=7, x='b'), ['-q', '-rs']), 2018 ) 2019 self.assertEqual( 2020 self.parser.parse_known_args('0.5 -W 1 b -X Y -w 7 Z'.split()), 2021 (NS(foo=False, bar=0.5, w=7, x='b'), ['-W', '-X', 'Y', 'Z']), 2022 ) 2023 2024 def test_dest(self): 2025 parser = ErrorRaisingArgumentParser() 2026 parser.add_argument('--foo', action='store_true') 2027 subparsers = parser.add_subparsers(dest='bar') 2028 parser1 = subparsers.add_parser('1') 2029 parser1.add_argument('baz') 2030 self.assertEqual(NS(foo=False, bar='1', baz='2'), 2031 parser.parse_args('1 2'.split())) 2032 2033 def _test_required_subparsers(self, parser): 2034 # Should parse the sub command 2035 ret = parser.parse_args(['run']) 2036 self.assertEqual(ret.command, 'run') 2037 2038 # Error when the command is missing 2039 self.assertArgumentParserError(parser.parse_args, ()) 2040 2041 def test_required_subparsers_via_attribute(self): 2042 parser = ErrorRaisingArgumentParser() 2043 subparsers = parser.add_subparsers(dest='command') 2044 subparsers.required = True 2045 subparsers.add_parser('run') 2046 self._test_required_subparsers(parser) 2047 2048 def test_required_subparsers_via_kwarg(self): 2049 parser = ErrorRaisingArgumentParser() 2050 subparsers = parser.add_subparsers(dest='command', required=True) 2051 subparsers.add_parser('run') 2052 self._test_required_subparsers(parser) 2053 2054 def test_required_subparsers_default(self): 2055 parser = ErrorRaisingArgumentParser() 2056 subparsers = parser.add_subparsers(dest='command') 2057 subparsers.add_parser('run') 2058 # No error here 2059 ret = parser.parse_args(()) 2060 self.assertIsNone(ret.command) 2061 2062 def test_required_subparsers_no_destination_error(self): 2063 parser = ErrorRaisingArgumentParser() 2064 subparsers = parser.add_subparsers(required=True) 2065 subparsers.add_parser('foo') 2066 subparsers.add_parser('bar') 2067 with self.assertRaises(ArgumentParserError) as excinfo: 2068 parser.parse_args(()) 2069 self.assertRegex( 2070 excinfo.exception.stderr, 2071 'error: the following arguments are required: {foo,bar}\n$' 2072 ) 2073 2074 def test_wrong_argument_subparsers_no_destination_error(self): 2075 parser = ErrorRaisingArgumentParser() 2076 subparsers = parser.add_subparsers(required=True) 2077 subparsers.add_parser('foo') 2078 subparsers.add_parser('bar') 2079 with self.assertRaises(ArgumentParserError) as excinfo: 2080 parser.parse_args(('baz',)) 2081 self.assertRegex( 2082 excinfo.exception.stderr, 2083 r"error: argument {foo,bar}: invalid choice: 'baz' \(choose from 'foo', 'bar'\)\n$" 2084 ) 2085 2086 def test_optional_subparsers(self): 2087 parser = ErrorRaisingArgumentParser() 2088 subparsers = parser.add_subparsers(dest='command', required=False) 2089 subparsers.add_parser('run') 2090 # No error here 2091 ret = parser.parse_args(()) 2092 self.assertIsNone(ret.command) 2093 2094 def test_help(self): 2095 self.assertEqual(self.parser.format_usage(), 2096 'usage: PROG [-h] [--foo] bar {1,2,3} ...\n') 2097 self.assertEqual(self.parser.format_help(), textwrap.dedent('''\ 2098 usage: PROG [-h] [--foo] bar {1,2,3} ... 2099 2100 main description 2101 2102 positional arguments: 2103 bar bar help 2104 {1,2,3} command help 2105 2106 options: 2107 -h, --help show this help message and exit 2108 --foo foo help 2109 ''')) 2110 2111 def test_help_extra_prefix_chars(self): 2112 # Make sure - is still used for help if it is a non-first prefix char 2113 parser = self._get_parser(prefix_chars='+:-') 2114 self.assertEqual(parser.format_usage(), 2115 'usage: PROG [-h] [++foo] bar {1,2,3} ...\n') 2116 self.assertEqual(parser.format_help(), textwrap.dedent('''\ 2117 usage: PROG [-h] [++foo] bar {1,2,3} ... 2118 2119 main description 2120 2121 positional arguments: 2122 bar bar help 2123 {1,2,3} command help 2124 2125 options: 2126 -h, --help show this help message and exit 2127 ++foo foo help 2128 ''')) 2129 2130 def test_help_non_breaking_spaces(self): 2131 parser = ErrorRaisingArgumentParser( 2132 prog='PROG', description='main description') 2133 parser.add_argument( 2134 "--non-breaking", action='store_false', 2135 help='help message containing non-breaking spaces shall not ' 2136 'wrap\N{NO-BREAK SPACE}at non-breaking spaces') 2137 self.assertEqual(parser.format_help(), textwrap.dedent('''\ 2138 usage: PROG [-h] [--non-breaking] 2139 2140 main description 2141 2142 options: 2143 -h, --help show this help message and exit 2144 --non-breaking help message containing non-breaking spaces shall not 2145 wrap\N{NO-BREAK SPACE}at non-breaking spaces 2146 ''')) 2147 2148 def test_help_blank(self): 2149 # Issue 24444 2150 parser = ErrorRaisingArgumentParser( 2151 prog='PROG', description='main description') 2152 parser.add_argument( 2153 'foo', 2154 help=' ') 2155 self.assertEqual(parser.format_help(), textwrap.dedent('''\ 2156 usage: PROG [-h] foo 2157 2158 main description 2159 2160 positional arguments: 2161 foo 2162 2163 options: 2164 -h, --help show this help message and exit 2165 ''')) 2166 2167 parser = ErrorRaisingArgumentParser( 2168 prog='PROG', description='main description') 2169 parser.add_argument( 2170 'foo', choices=[], 2171 help='%(choices)s') 2172 self.assertEqual(parser.format_help(), textwrap.dedent('''\ 2173 usage: PROG [-h] {} 2174 2175 main description 2176 2177 positional arguments: 2178 {} 2179 2180 options: 2181 -h, --help show this help message and exit 2182 ''')) 2183 2184 def test_help_alternate_prefix_chars(self): 2185 parser = self._get_parser(prefix_chars='+:/') 2186 self.assertEqual(parser.format_usage(), 2187 'usage: PROG [+h] [++foo] bar {1,2,3} ...\n') 2188 self.assertEqual(parser.format_help(), textwrap.dedent('''\ 2189 usage: PROG [+h] [++foo] bar {1,2,3} ... 2190 2191 main description 2192 2193 positional arguments: 2194 bar bar help 2195 {1,2,3} command help 2196 2197 options: 2198 +h, ++help show this help message and exit 2199 ++foo foo help 2200 ''')) 2201 2202 def test_parser_command_help(self): 2203 self.assertEqual(self.command_help_parser.format_usage(), 2204 'usage: PROG [-h] [--foo] bar {1,2,3} ...\n') 2205 self.assertEqual(self.command_help_parser.format_help(), 2206 textwrap.dedent('''\ 2207 usage: PROG [-h] [--foo] bar {1,2,3} ... 2208 2209 main description 2210 2211 positional arguments: 2212 bar bar help 2213 {1,2,3} command help 2214 1 1 help 2215 2 2 help 2216 3 3 help 2217 2218 options: 2219 -h, --help show this help message and exit 2220 --foo foo help 2221 ''')) 2222 2223 def test_subparser_title_help(self): 2224 parser = ErrorRaisingArgumentParser(prog='PROG', 2225 description='main description') 2226 parser.add_argument('--foo', action='store_true', help='foo help') 2227 parser.add_argument('bar', help='bar help') 2228 subparsers = parser.add_subparsers(title='subcommands', 2229 description='command help', 2230 help='additional text') 2231 parser1 = subparsers.add_parser('1') 2232 parser2 = subparsers.add_parser('2') 2233 self.assertEqual(parser.format_usage(), 2234 'usage: PROG [-h] [--foo] bar {1,2} ...\n') 2235 self.assertEqual(parser.format_help(), textwrap.dedent('''\ 2236 usage: PROG [-h] [--foo] bar {1,2} ... 2237 2238 main description 2239 2240 positional arguments: 2241 bar bar help 2242 2243 options: 2244 -h, --help show this help message and exit 2245 --foo foo help 2246 2247 subcommands: 2248 command help 2249 2250 {1,2} additional text 2251 ''')) 2252 2253 def _test_subparser_help(self, args_str, expected_help): 2254 with self.assertRaises(ArgumentParserError) as cm: 2255 self.parser.parse_args(args_str.split()) 2256 self.assertEqual(expected_help, cm.exception.stdout) 2257 2258 def test_subparser1_help(self): 2259 self._test_subparser_help('5.0 1 -h', textwrap.dedent('''\ 2260 usage: PROG bar 1 [-h] [-w W] {a,b,c} 2261 2262 1 description 2263 2264 positional arguments: 2265 {a,b,c} x help 2266 2267 options: 2268 -h, --help show this help message and exit 2269 -w W w help 2270 ''')) 2271 2272 def test_subparser2_help(self): 2273 self._test_subparser_help('5.0 2 -h', textwrap.dedent('''\ 2274 usage: PROG bar 2 [-h] [-y {1,2,3}] [z ...] 2275 2276 2 description 2277 2278 positional arguments: 2279 z z help 2280 2281 options: 2282 -h, --help show this help message and exit 2283 -y {1,2,3} y help 2284 ''')) 2285 2286 def test_alias_invocation(self): 2287 parser = self._get_parser(aliases=True) 2288 self.assertEqual( 2289 parser.parse_known_args('0.5 1alias1 b'.split()), 2290 (NS(foo=False, bar=0.5, w=None, x='b'), []), 2291 ) 2292 self.assertEqual( 2293 parser.parse_known_args('0.5 1alias2 b'.split()), 2294 (NS(foo=False, bar=0.5, w=None, x='b'), []), 2295 ) 2296 2297 def test_error_alias_invocation(self): 2298 parser = self._get_parser(aliases=True) 2299 self.assertArgumentParserError(parser.parse_args, 2300 '0.5 1alias3 b'.split()) 2301 2302 def test_alias_help(self): 2303 parser = self._get_parser(aliases=True, subparser_help=True) 2304 self.maxDiff = None 2305 self.assertEqual(parser.format_help(), textwrap.dedent("""\ 2306 usage: PROG [-h] [--foo] bar COMMAND ... 2307 2308 main description 2309 2310 positional arguments: 2311 bar bar help 2312 2313 options: 2314 -h, --help show this help message and exit 2315 --foo foo help 2316 2317 commands: 2318 COMMAND 2319 1 (1alias1, 1alias2) 2320 1 help 2321 2 2 help 2322 3 3 help 2323 """)) 2324 2325# ============ 2326# Groups tests 2327# ============ 2328 2329class TestPositionalsGroups(TestCase): 2330 """Tests that order of group positionals matches construction order""" 2331 2332 def test_nongroup_first(self): 2333 parser = ErrorRaisingArgumentParser() 2334 parser.add_argument('foo') 2335 group = parser.add_argument_group('g') 2336 group.add_argument('bar') 2337 parser.add_argument('baz') 2338 expected = NS(foo='1', bar='2', baz='3') 2339 result = parser.parse_args('1 2 3'.split()) 2340 self.assertEqual(expected, result) 2341 2342 def test_group_first(self): 2343 parser = ErrorRaisingArgumentParser() 2344 group = parser.add_argument_group('xxx') 2345 group.add_argument('foo') 2346 parser.add_argument('bar') 2347 parser.add_argument('baz') 2348 expected = NS(foo='1', bar='2', baz='3') 2349 result = parser.parse_args('1 2 3'.split()) 2350 self.assertEqual(expected, result) 2351 2352 def test_interleaved_groups(self): 2353 parser = ErrorRaisingArgumentParser() 2354 group = parser.add_argument_group('xxx') 2355 parser.add_argument('foo') 2356 group.add_argument('bar') 2357 parser.add_argument('baz') 2358 group = parser.add_argument_group('yyy') 2359 group.add_argument('frell') 2360 expected = NS(foo='1', bar='2', baz='3', frell='4') 2361 result = parser.parse_args('1 2 3 4'.split()) 2362 self.assertEqual(expected, result) 2363 2364# =================== 2365# Parent parser tests 2366# =================== 2367 2368class TestParentParsers(TestCase): 2369 """Tests that parsers can be created with parent parsers""" 2370 2371 def assertArgumentParserError(self, *args, **kwargs): 2372 self.assertRaises(ArgumentParserError, *args, **kwargs) 2373 2374 def setUp(self): 2375 super().setUp() 2376 self.wxyz_parent = ErrorRaisingArgumentParser(add_help=False) 2377 self.wxyz_parent.add_argument('--w') 2378 x_group = self.wxyz_parent.add_argument_group('x') 2379 x_group.add_argument('-y') 2380 self.wxyz_parent.add_argument('z') 2381 2382 self.abcd_parent = ErrorRaisingArgumentParser(add_help=False) 2383 self.abcd_parent.add_argument('a') 2384 self.abcd_parent.add_argument('-b') 2385 c_group = self.abcd_parent.add_argument_group('c') 2386 c_group.add_argument('--d') 2387 2388 self.w_parent = ErrorRaisingArgumentParser(add_help=False) 2389 self.w_parent.add_argument('--w') 2390 2391 self.z_parent = ErrorRaisingArgumentParser(add_help=False) 2392 self.z_parent.add_argument('z') 2393 2394 # parents with mutually exclusive groups 2395 self.ab_mutex_parent = ErrorRaisingArgumentParser(add_help=False) 2396 group = self.ab_mutex_parent.add_mutually_exclusive_group() 2397 group.add_argument('-a', action='store_true') 2398 group.add_argument('-b', action='store_true') 2399 2400 self.main_program = os.path.basename(sys.argv[0]) 2401 2402 def test_single_parent(self): 2403 parser = ErrorRaisingArgumentParser(parents=[self.wxyz_parent]) 2404 self.assertEqual(parser.parse_args('-y 1 2 --w 3'.split()), 2405 NS(w='3', y='1', z='2')) 2406 2407 def test_single_parent_mutex(self): 2408 self._test_mutex_ab(self.ab_mutex_parent.parse_args) 2409 parser = ErrorRaisingArgumentParser(parents=[self.ab_mutex_parent]) 2410 self._test_mutex_ab(parser.parse_args) 2411 2412 def test_single_granparent_mutex(self): 2413 parents = [self.ab_mutex_parent] 2414 parser = ErrorRaisingArgumentParser(add_help=False, parents=parents) 2415 parser = ErrorRaisingArgumentParser(parents=[parser]) 2416 self._test_mutex_ab(parser.parse_args) 2417 2418 def _test_mutex_ab(self, parse_args): 2419 self.assertEqual(parse_args([]), NS(a=False, b=False)) 2420 self.assertEqual(parse_args(['-a']), NS(a=True, b=False)) 2421 self.assertEqual(parse_args(['-b']), NS(a=False, b=True)) 2422 self.assertArgumentParserError(parse_args, ['-a', '-b']) 2423 self.assertArgumentParserError(parse_args, ['-b', '-a']) 2424 self.assertArgumentParserError(parse_args, ['-c']) 2425 self.assertArgumentParserError(parse_args, ['-a', '-c']) 2426 self.assertArgumentParserError(parse_args, ['-b', '-c']) 2427 2428 def test_multiple_parents(self): 2429 parents = [self.abcd_parent, self.wxyz_parent] 2430 parser = ErrorRaisingArgumentParser(parents=parents) 2431 self.assertEqual(parser.parse_args('--d 1 --w 2 3 4'.split()), 2432 NS(a='3', b=None, d='1', w='2', y=None, z='4')) 2433 2434 def test_multiple_parents_mutex(self): 2435 parents = [self.ab_mutex_parent, self.wxyz_parent] 2436 parser = ErrorRaisingArgumentParser(parents=parents) 2437 self.assertEqual(parser.parse_args('-a --w 2 3'.split()), 2438 NS(a=True, b=False, w='2', y=None, z='3')) 2439 self.assertArgumentParserError( 2440 parser.parse_args, '-a --w 2 3 -b'.split()) 2441 self.assertArgumentParserError( 2442 parser.parse_args, '-a -b --w 2 3'.split()) 2443 2444 def test_conflicting_parents(self): 2445 self.assertRaises( 2446 argparse.ArgumentError, 2447 argparse.ArgumentParser, 2448 parents=[self.w_parent, self.wxyz_parent]) 2449 2450 def test_conflicting_parents_mutex(self): 2451 self.assertRaises( 2452 argparse.ArgumentError, 2453 argparse.ArgumentParser, 2454 parents=[self.abcd_parent, self.ab_mutex_parent]) 2455 2456 def test_same_argument_name_parents(self): 2457 parents = [self.wxyz_parent, self.z_parent] 2458 parser = ErrorRaisingArgumentParser(parents=parents) 2459 self.assertEqual(parser.parse_args('1 2'.split()), 2460 NS(w=None, y=None, z='2')) 2461 2462 def test_subparser_parents(self): 2463 parser = ErrorRaisingArgumentParser() 2464 subparsers = parser.add_subparsers() 2465 abcde_parser = subparsers.add_parser('bar', parents=[self.abcd_parent]) 2466 abcde_parser.add_argument('e') 2467 self.assertEqual(parser.parse_args('bar -b 1 --d 2 3 4'.split()), 2468 NS(a='3', b='1', d='2', e='4')) 2469 2470 def test_subparser_parents_mutex(self): 2471 parser = ErrorRaisingArgumentParser() 2472 subparsers = parser.add_subparsers() 2473 parents = [self.ab_mutex_parent] 2474 abc_parser = subparsers.add_parser('foo', parents=parents) 2475 c_group = abc_parser.add_argument_group('c_group') 2476 c_group.add_argument('c') 2477 parents = [self.wxyz_parent, self.ab_mutex_parent] 2478 wxyzabe_parser = subparsers.add_parser('bar', parents=parents) 2479 wxyzabe_parser.add_argument('e') 2480 self.assertEqual(parser.parse_args('foo -a 4'.split()), 2481 NS(a=True, b=False, c='4')) 2482 self.assertEqual(parser.parse_args('bar -b --w 2 3 4'.split()), 2483 NS(a=False, b=True, w='2', y=None, z='3', e='4')) 2484 self.assertArgumentParserError( 2485 parser.parse_args, 'foo -a -b 4'.split()) 2486 self.assertArgumentParserError( 2487 parser.parse_args, 'bar -b -a 4'.split()) 2488 2489 def test_parent_help(self): 2490 parents = [self.abcd_parent, self.wxyz_parent] 2491 parser = ErrorRaisingArgumentParser(parents=parents) 2492 parser_help = parser.format_help() 2493 progname = self.main_program 2494 self.assertEqual(parser_help, textwrap.dedent('''\ 2495 usage: {}{}[-h] [-b B] [--d D] [--w W] [-y Y] a z 2496 2497 positional arguments: 2498 a 2499 z 2500 2501 options: 2502 -h, --help show this help message and exit 2503 -b B 2504 --w W 2505 2506 c: 2507 --d D 2508 2509 x: 2510 -y Y 2511 '''.format(progname, ' ' if progname else '' ))) 2512 2513 def test_groups_parents(self): 2514 parent = ErrorRaisingArgumentParser(add_help=False) 2515 g = parent.add_argument_group(title='g', description='gd') 2516 g.add_argument('-w') 2517 g.add_argument('-x') 2518 m = parent.add_mutually_exclusive_group() 2519 m.add_argument('-y') 2520 m.add_argument('-z') 2521 parser = ErrorRaisingArgumentParser(parents=[parent]) 2522 2523 self.assertRaises(ArgumentParserError, parser.parse_args, 2524 ['-y', 'Y', '-z', 'Z']) 2525 2526 parser_help = parser.format_help() 2527 progname = self.main_program 2528 self.assertEqual(parser_help, textwrap.dedent('''\ 2529 usage: {}{}[-h] [-w W] [-x X] [-y Y | -z Z] 2530 2531 options: 2532 -h, --help show this help message and exit 2533 -y Y 2534 -z Z 2535 2536 g: 2537 gd 2538 2539 -w W 2540 -x X 2541 '''.format(progname, ' ' if progname else '' ))) 2542 2543# ============================== 2544# Mutually exclusive group tests 2545# ============================== 2546 2547class TestMutuallyExclusiveGroupErrors(TestCase): 2548 2549 def test_invalid_add_argument_group(self): 2550 parser = ErrorRaisingArgumentParser() 2551 raises = self.assertRaises 2552 raises(TypeError, parser.add_mutually_exclusive_group, title='foo') 2553 2554 def test_invalid_add_argument(self): 2555 parser = ErrorRaisingArgumentParser() 2556 group = parser.add_mutually_exclusive_group() 2557 add_argument = group.add_argument 2558 raises = self.assertRaises 2559 raises(ValueError, add_argument, '--foo', required=True) 2560 raises(ValueError, add_argument, 'bar') 2561 raises(ValueError, add_argument, 'bar', nargs='+') 2562 raises(ValueError, add_argument, 'bar', nargs=1) 2563 raises(ValueError, add_argument, 'bar', nargs=argparse.PARSER) 2564 2565 def test_help(self): 2566 parser = ErrorRaisingArgumentParser(prog='PROG') 2567 group1 = parser.add_mutually_exclusive_group() 2568 group1.add_argument('--foo', action='store_true') 2569 group1.add_argument('--bar', action='store_false') 2570 group2 = parser.add_mutually_exclusive_group() 2571 group2.add_argument('--soup', action='store_true') 2572 group2.add_argument('--nuts', action='store_false') 2573 expected = '''\ 2574 usage: PROG [-h] [--foo | --bar] [--soup | --nuts] 2575 2576 options: 2577 -h, --help show this help message and exit 2578 --foo 2579 --bar 2580 --soup 2581 --nuts 2582 ''' 2583 self.assertEqual(parser.format_help(), textwrap.dedent(expected)) 2584 2585 def test_empty_group(self): 2586 # See issue 26952 2587 parser = argparse.ArgumentParser() 2588 group = parser.add_mutually_exclusive_group() 2589 with self.assertRaises(ValueError): 2590 parser.parse_args(['-h']) 2591 2592class MEMixin(object): 2593 2594 def test_failures_when_not_required(self): 2595 parse_args = self.get_parser(required=False).parse_args 2596 error = ArgumentParserError 2597 for args_string in self.failures: 2598 self.assertRaises(error, parse_args, args_string.split()) 2599 2600 def test_failures_when_required(self): 2601 parse_args = self.get_parser(required=True).parse_args 2602 error = ArgumentParserError 2603 for args_string in self.failures + ['']: 2604 self.assertRaises(error, parse_args, args_string.split()) 2605 2606 def test_successes_when_not_required(self): 2607 parse_args = self.get_parser(required=False).parse_args 2608 successes = self.successes + self.successes_when_not_required 2609 for args_string, expected_ns in successes: 2610 actual_ns = parse_args(args_string.split()) 2611 self.assertEqual(actual_ns, expected_ns) 2612 2613 def test_successes_when_required(self): 2614 parse_args = self.get_parser(required=True).parse_args 2615 for args_string, expected_ns in self.successes: 2616 actual_ns = parse_args(args_string.split()) 2617 self.assertEqual(actual_ns, expected_ns) 2618 2619 def test_usage_when_not_required(self): 2620 format_usage = self.get_parser(required=False).format_usage 2621 expected_usage = self.usage_when_not_required 2622 self.assertEqual(format_usage(), textwrap.dedent(expected_usage)) 2623 2624 def test_usage_when_required(self): 2625 format_usage = self.get_parser(required=True).format_usage 2626 expected_usage = self.usage_when_required 2627 self.assertEqual(format_usage(), textwrap.dedent(expected_usage)) 2628 2629 def test_help_when_not_required(self): 2630 format_help = self.get_parser(required=False).format_help 2631 help = self.usage_when_not_required + self.help 2632 self.assertEqual(format_help(), textwrap.dedent(help)) 2633 2634 def test_help_when_required(self): 2635 format_help = self.get_parser(required=True).format_help 2636 help = self.usage_when_required + self.help 2637 self.assertEqual(format_help(), textwrap.dedent(help)) 2638 2639 2640class TestMutuallyExclusiveSimple(MEMixin, TestCase): 2641 2642 def get_parser(self, required=None): 2643 parser = ErrorRaisingArgumentParser(prog='PROG') 2644 group = parser.add_mutually_exclusive_group(required=required) 2645 group.add_argument('--bar', help='bar help') 2646 group.add_argument('--baz', nargs='?', const='Z', help='baz help') 2647 return parser 2648 2649 failures = ['--bar X --baz Y', '--bar X --baz'] 2650 successes = [ 2651 ('--bar X', NS(bar='X', baz=None)), 2652 ('--bar X --bar Z', NS(bar='Z', baz=None)), 2653 ('--baz Y', NS(bar=None, baz='Y')), 2654 ('--baz', NS(bar=None, baz='Z')), 2655 ] 2656 successes_when_not_required = [ 2657 ('', NS(bar=None, baz=None)), 2658 ] 2659 2660 usage_when_not_required = '''\ 2661 usage: PROG [-h] [--bar BAR | --baz [BAZ]] 2662 ''' 2663 usage_when_required = '''\ 2664 usage: PROG [-h] (--bar BAR | --baz [BAZ]) 2665 ''' 2666 help = '''\ 2667 2668 options: 2669 -h, --help show this help message and exit 2670 --bar BAR bar help 2671 --baz [BAZ] baz help 2672 ''' 2673 2674 2675class TestMutuallyExclusiveLong(MEMixin, TestCase): 2676 2677 def get_parser(self, required=None): 2678 parser = ErrorRaisingArgumentParser(prog='PROG') 2679 parser.add_argument('--abcde', help='abcde help') 2680 parser.add_argument('--fghij', help='fghij help') 2681 group = parser.add_mutually_exclusive_group(required=required) 2682 group.add_argument('--klmno', help='klmno help') 2683 group.add_argument('--pqrst', help='pqrst help') 2684 return parser 2685 2686 failures = ['--klmno X --pqrst Y'] 2687 successes = [ 2688 ('--klmno X', NS(abcde=None, fghij=None, klmno='X', pqrst=None)), 2689 ('--abcde Y --klmno X', 2690 NS(abcde='Y', fghij=None, klmno='X', pqrst=None)), 2691 ('--pqrst X', NS(abcde=None, fghij=None, klmno=None, pqrst='X')), 2692 ('--pqrst X --fghij Y', 2693 NS(abcde=None, fghij='Y', klmno=None, pqrst='X')), 2694 ] 2695 successes_when_not_required = [ 2696 ('', NS(abcde=None, fghij=None, klmno=None, pqrst=None)), 2697 ] 2698 2699 usage_when_not_required = '''\ 2700 usage: PROG [-h] [--abcde ABCDE] [--fghij FGHIJ] 2701 [--klmno KLMNO | --pqrst PQRST] 2702 ''' 2703 usage_when_required = '''\ 2704 usage: PROG [-h] [--abcde ABCDE] [--fghij FGHIJ] 2705 (--klmno KLMNO | --pqrst PQRST) 2706 ''' 2707 help = '''\ 2708 2709 options: 2710 -h, --help show this help message and exit 2711 --abcde ABCDE abcde help 2712 --fghij FGHIJ fghij help 2713 --klmno KLMNO klmno help 2714 --pqrst PQRST pqrst help 2715 ''' 2716 2717 2718class TestMutuallyExclusiveFirstSuppressed(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('-x', help=argparse.SUPPRESS) 2724 group.add_argument('-y', action='store_false', help='y help') 2725 return parser 2726 2727 failures = ['-x X -y'] 2728 successes = [ 2729 ('-x X', NS(x='X', y=True)), 2730 ('-x X -x Y', NS(x='Y', y=True)), 2731 ('-y', NS(x=None, y=False)), 2732 ] 2733 successes_when_not_required = [ 2734 ('', NS(x=None, y=True)), 2735 ] 2736 2737 usage_when_not_required = '''\ 2738 usage: PROG [-h] [-y] 2739 ''' 2740 usage_when_required = '''\ 2741 usage: PROG [-h] -y 2742 ''' 2743 help = '''\ 2744 2745 options: 2746 -h, --help show this help message and exit 2747 -y y help 2748 ''' 2749 2750 2751class TestMutuallyExclusiveManySuppressed(MEMixin, TestCase): 2752 2753 def get_parser(self, required): 2754 parser = ErrorRaisingArgumentParser(prog='PROG') 2755 group = parser.add_mutually_exclusive_group(required=required) 2756 add = group.add_argument 2757 add('--spam', action='store_true', help=argparse.SUPPRESS) 2758 add('--badger', action='store_false', help=argparse.SUPPRESS) 2759 add('--bladder', help=argparse.SUPPRESS) 2760 return parser 2761 2762 failures = [ 2763 '--spam --badger', 2764 '--badger --bladder B', 2765 '--bladder B --spam', 2766 ] 2767 successes = [ 2768 ('--spam', NS(spam=True, badger=True, bladder=None)), 2769 ('--badger', NS(spam=False, badger=False, bladder=None)), 2770 ('--bladder B', NS(spam=False, badger=True, bladder='B')), 2771 ('--spam --spam', NS(spam=True, badger=True, bladder=None)), 2772 ] 2773 successes_when_not_required = [ 2774 ('', NS(spam=False, badger=True, bladder=None)), 2775 ] 2776 2777 usage_when_required = usage_when_not_required = '''\ 2778 usage: PROG [-h] 2779 ''' 2780 help = '''\ 2781 2782 options: 2783 -h, --help show this help message and exit 2784 ''' 2785 2786 2787class TestMutuallyExclusiveOptionalAndPositional(MEMixin, TestCase): 2788 2789 def get_parser(self, required): 2790 parser = ErrorRaisingArgumentParser(prog='PROG') 2791 group = parser.add_mutually_exclusive_group(required=required) 2792 group.add_argument('--foo', action='store_true', help='FOO') 2793 group.add_argument('--spam', help='SPAM') 2794 group.add_argument('badger', nargs='*', default='X', help='BADGER') 2795 return parser 2796 2797 failures = [ 2798 '--foo --spam S', 2799 '--spam S X', 2800 'X --foo', 2801 'X Y Z --spam S', 2802 '--foo X Y', 2803 ] 2804 successes = [ 2805 ('--foo', NS(foo=True, spam=None, badger='X')), 2806 ('--spam S', NS(foo=False, spam='S', badger='X')), 2807 ('X', NS(foo=False, spam=None, badger=['X'])), 2808 ('X Y Z', NS(foo=False, spam=None, badger=['X', 'Y', 'Z'])), 2809 ] 2810 successes_when_not_required = [ 2811 ('', NS(foo=False, spam=None, badger='X')), 2812 ] 2813 2814 usage_when_not_required = '''\ 2815 usage: PROG [-h] [--foo | --spam SPAM | badger ...] 2816 ''' 2817 usage_when_required = '''\ 2818 usage: PROG [-h] (--foo | --spam SPAM | badger ...) 2819 ''' 2820 help = '''\ 2821 2822 positional arguments: 2823 badger BADGER 2824 2825 options: 2826 -h, --help show this help message and exit 2827 --foo FOO 2828 --spam SPAM SPAM 2829 ''' 2830 2831 2832class TestMutuallyExclusiveOptionalsMixed(MEMixin, TestCase): 2833 2834 def get_parser(self, required): 2835 parser = ErrorRaisingArgumentParser(prog='PROG') 2836 parser.add_argument('-x', action='store_true', help='x help') 2837 group = parser.add_mutually_exclusive_group(required=required) 2838 group.add_argument('-a', action='store_true', help='a help') 2839 group.add_argument('-b', action='store_true', help='b help') 2840 parser.add_argument('-y', action='store_true', help='y help') 2841 group.add_argument('-c', action='store_true', help='c help') 2842 return parser 2843 2844 failures = ['-a -b', '-b -c', '-a -c', '-a -b -c'] 2845 successes = [ 2846 ('-a', NS(a=True, b=False, c=False, x=False, y=False)), 2847 ('-b', NS(a=False, b=True, c=False, x=False, y=False)), 2848 ('-c', NS(a=False, b=False, c=True, x=False, y=False)), 2849 ('-a -x', NS(a=True, b=False, c=False, x=True, y=False)), 2850 ('-y -b', NS(a=False, b=True, c=False, x=False, y=True)), 2851 ('-x -y -c', NS(a=False, b=False, c=True, x=True, y=True)), 2852 ] 2853 successes_when_not_required = [ 2854 ('', NS(a=False, b=False, c=False, x=False, y=False)), 2855 ('-x', NS(a=False, b=False, c=False, x=True, y=False)), 2856 ('-y', NS(a=False, b=False, c=False, x=False, y=True)), 2857 ] 2858 2859 usage_when_required = usage_when_not_required = '''\ 2860 usage: PROG [-h] [-x] [-a] [-b] [-y] [-c] 2861 ''' 2862 help = '''\ 2863 2864 options: 2865 -h, --help show this help message and exit 2866 -x x help 2867 -a a help 2868 -b b help 2869 -y y help 2870 -c c help 2871 ''' 2872 2873 2874class TestMutuallyExclusiveInGroup(MEMixin, TestCase): 2875 2876 def get_parser(self, required=None): 2877 parser = ErrorRaisingArgumentParser(prog='PROG') 2878 titled_group = parser.add_argument_group( 2879 title='Titled group', description='Group description') 2880 mutex_group = \ 2881 titled_group.add_mutually_exclusive_group(required=required) 2882 mutex_group.add_argument('--bar', help='bar help') 2883 mutex_group.add_argument('--baz', help='baz help') 2884 return parser 2885 2886 failures = ['--bar X --baz Y', '--baz X --bar Y'] 2887 successes = [ 2888 ('--bar X', NS(bar='X', baz=None)), 2889 ('--baz Y', NS(bar=None, baz='Y')), 2890 ] 2891 successes_when_not_required = [ 2892 ('', NS(bar=None, baz=None)), 2893 ] 2894 2895 usage_when_not_required = '''\ 2896 usage: PROG [-h] [--bar BAR | --baz BAZ] 2897 ''' 2898 usage_when_required = '''\ 2899 usage: PROG [-h] (--bar BAR | --baz BAZ) 2900 ''' 2901 help = '''\ 2902 2903 options: 2904 -h, --help show this help message and exit 2905 2906 Titled group: 2907 Group description 2908 2909 --bar BAR bar help 2910 --baz BAZ baz help 2911 ''' 2912 2913 2914class TestMutuallyExclusiveOptionalsAndPositionalsMixed(MEMixin, TestCase): 2915 2916 def get_parser(self, required): 2917 parser = ErrorRaisingArgumentParser(prog='PROG') 2918 parser.add_argument('x', help='x help') 2919 parser.add_argument('-y', action='store_true', help='y help') 2920 group = parser.add_mutually_exclusive_group(required=required) 2921 group.add_argument('a', nargs='?', help='a help') 2922 group.add_argument('-b', action='store_true', help='b help') 2923 group.add_argument('-c', action='store_true', help='c help') 2924 return parser 2925 2926 failures = ['X A -b', '-b -c', '-c X A'] 2927 successes = [ 2928 ('X A', NS(a='A', b=False, c=False, x='X', y=False)), 2929 ('X -b', NS(a=None, b=True, c=False, x='X', y=False)), 2930 ('X -c', NS(a=None, b=False, c=True, x='X', y=False)), 2931 ('X A -y', NS(a='A', b=False, c=False, x='X', y=True)), 2932 ('X -y -b', NS(a=None, b=True, c=False, x='X', y=True)), 2933 ] 2934 successes_when_not_required = [ 2935 ('X', NS(a=None, b=False, c=False, x='X', y=False)), 2936 ('X -y', NS(a=None, b=False, c=False, x='X', y=True)), 2937 ] 2938 2939 usage_when_required = usage_when_not_required = '''\ 2940 usage: PROG [-h] [-y] [-b] [-c] x [a] 2941 ''' 2942 help = '''\ 2943 2944 positional arguments: 2945 x x help 2946 a a help 2947 2948 options: 2949 -h, --help show this help message and exit 2950 -y y help 2951 -b b help 2952 -c c help 2953 ''' 2954 2955class TestMutuallyExclusiveNested(MEMixin, TestCase): 2956 2957 def get_parser(self, required): 2958 parser = ErrorRaisingArgumentParser(prog='PROG') 2959 group = parser.add_mutually_exclusive_group(required=required) 2960 group.add_argument('-a') 2961 group.add_argument('-b') 2962 group2 = group.add_mutually_exclusive_group(required=required) 2963 group2.add_argument('-c') 2964 group2.add_argument('-d') 2965 group3 = group2.add_mutually_exclusive_group(required=required) 2966 group3.add_argument('-e') 2967 group3.add_argument('-f') 2968 return parser 2969 2970 usage_when_not_required = '''\ 2971 usage: PROG [-h] [-a A | -b B | [-c C | -d D | [-e E | -f F]]] 2972 ''' 2973 usage_when_required = '''\ 2974 usage: PROG [-h] (-a A | -b B | (-c C | -d D | (-e E | -f F))) 2975 ''' 2976 2977 help = '''\ 2978 2979 options: 2980 -h, --help show this help message and exit 2981 -a A 2982 -b B 2983 -c C 2984 -d D 2985 -e E 2986 -f F 2987 ''' 2988 2989 # We are only interested in testing the behavior of format_usage(). 2990 test_failures_when_not_required = None 2991 test_failures_when_required = None 2992 test_successes_when_not_required = None 2993 test_successes_when_required = None 2994 2995# ================================================= 2996# Mutually exclusive group in parent parser tests 2997# ================================================= 2998 2999class MEPBase(object): 3000 3001 def get_parser(self, required=None): 3002 parent = super(MEPBase, self).get_parser(required=required) 3003 parser = ErrorRaisingArgumentParser( 3004 prog=parent.prog, add_help=False, parents=[parent]) 3005 return parser 3006 3007 3008class TestMutuallyExclusiveGroupErrorsParent( 3009 MEPBase, TestMutuallyExclusiveGroupErrors): 3010 pass 3011 3012 3013class TestMutuallyExclusiveSimpleParent( 3014 MEPBase, TestMutuallyExclusiveSimple): 3015 pass 3016 3017 3018class TestMutuallyExclusiveLongParent( 3019 MEPBase, TestMutuallyExclusiveLong): 3020 pass 3021 3022 3023class TestMutuallyExclusiveFirstSuppressedParent( 3024 MEPBase, TestMutuallyExclusiveFirstSuppressed): 3025 pass 3026 3027 3028class TestMutuallyExclusiveManySuppressedParent( 3029 MEPBase, TestMutuallyExclusiveManySuppressed): 3030 pass 3031 3032 3033class TestMutuallyExclusiveOptionalAndPositionalParent( 3034 MEPBase, TestMutuallyExclusiveOptionalAndPositional): 3035 pass 3036 3037 3038class TestMutuallyExclusiveOptionalsMixedParent( 3039 MEPBase, TestMutuallyExclusiveOptionalsMixed): 3040 pass 3041 3042 3043class TestMutuallyExclusiveOptionalsAndPositionalsMixedParent( 3044 MEPBase, TestMutuallyExclusiveOptionalsAndPositionalsMixed): 3045 pass 3046 3047# ================= 3048# Set default tests 3049# ================= 3050 3051class TestSetDefaults(TestCase): 3052 3053 def test_set_defaults_no_args(self): 3054 parser = ErrorRaisingArgumentParser() 3055 parser.set_defaults(x='foo') 3056 parser.set_defaults(y='bar', z=1) 3057 self.assertEqual(NS(x='foo', y='bar', z=1), 3058 parser.parse_args([])) 3059 self.assertEqual(NS(x='foo', y='bar', z=1), 3060 parser.parse_args([], NS())) 3061 self.assertEqual(NS(x='baz', y='bar', z=1), 3062 parser.parse_args([], NS(x='baz'))) 3063 self.assertEqual(NS(x='baz', y='bar', z=2), 3064 parser.parse_args([], NS(x='baz', z=2))) 3065 3066 def test_set_defaults_with_args(self): 3067 parser = ErrorRaisingArgumentParser() 3068 parser.set_defaults(x='foo', y='bar') 3069 parser.add_argument('-x', default='xfoox') 3070 self.assertEqual(NS(x='xfoox', y='bar'), 3071 parser.parse_args([])) 3072 self.assertEqual(NS(x='xfoox', y='bar'), 3073 parser.parse_args([], NS())) 3074 self.assertEqual(NS(x='baz', y='bar'), 3075 parser.parse_args([], NS(x='baz'))) 3076 self.assertEqual(NS(x='1', y='bar'), 3077 parser.parse_args('-x 1'.split())) 3078 self.assertEqual(NS(x='1', y='bar'), 3079 parser.parse_args('-x 1'.split(), NS())) 3080 self.assertEqual(NS(x='1', y='bar'), 3081 parser.parse_args('-x 1'.split(), NS(x='baz'))) 3082 3083 def test_set_defaults_subparsers(self): 3084 parser = ErrorRaisingArgumentParser() 3085 parser.set_defaults(x='foo') 3086 subparsers = parser.add_subparsers() 3087 parser_a = subparsers.add_parser('a') 3088 parser_a.set_defaults(y='bar') 3089 self.assertEqual(NS(x='foo', y='bar'), 3090 parser.parse_args('a'.split())) 3091 3092 def test_set_defaults_parents(self): 3093 parent = ErrorRaisingArgumentParser(add_help=False) 3094 parent.set_defaults(x='foo') 3095 parser = ErrorRaisingArgumentParser(parents=[parent]) 3096 self.assertEqual(NS(x='foo'), parser.parse_args([])) 3097 3098 def test_set_defaults_on_parent_and_subparser(self): 3099 parser = argparse.ArgumentParser() 3100 xparser = parser.add_subparsers().add_parser('X') 3101 parser.set_defaults(foo=1) 3102 xparser.set_defaults(foo=2) 3103 self.assertEqual(NS(foo=2), parser.parse_args(['X'])) 3104 3105 def test_set_defaults_same_as_add_argument(self): 3106 parser = ErrorRaisingArgumentParser() 3107 parser.set_defaults(w='W', x='X', y='Y', z='Z') 3108 parser.add_argument('-w') 3109 parser.add_argument('-x', default='XX') 3110 parser.add_argument('y', nargs='?') 3111 parser.add_argument('z', nargs='?', default='ZZ') 3112 3113 # defaults set previously 3114 self.assertEqual(NS(w='W', x='XX', y='Y', z='ZZ'), 3115 parser.parse_args([])) 3116 3117 # reset defaults 3118 parser.set_defaults(w='WW', x='X', y='YY', z='Z') 3119 self.assertEqual(NS(w='WW', x='X', y='YY', z='Z'), 3120 parser.parse_args([])) 3121 3122 def test_set_defaults_same_as_add_argument_group(self): 3123 parser = ErrorRaisingArgumentParser() 3124 parser.set_defaults(w='W', x='X', y='Y', z='Z') 3125 group = parser.add_argument_group('foo') 3126 group.add_argument('-w') 3127 group.add_argument('-x', default='XX') 3128 group.add_argument('y', nargs='?') 3129 group.add_argument('z', nargs='?', default='ZZ') 3130 3131 3132 # defaults set previously 3133 self.assertEqual(NS(w='W', x='XX', y='Y', z='ZZ'), 3134 parser.parse_args([])) 3135 3136 # reset defaults 3137 parser.set_defaults(w='WW', x='X', y='YY', z='Z') 3138 self.assertEqual(NS(w='WW', x='X', y='YY', z='Z'), 3139 parser.parse_args([])) 3140 3141# ================= 3142# Get default tests 3143# ================= 3144 3145class TestGetDefault(TestCase): 3146 3147 def test_get_default(self): 3148 parser = ErrorRaisingArgumentParser() 3149 self.assertIsNone(parser.get_default("foo")) 3150 self.assertIsNone(parser.get_default("bar")) 3151 3152 parser.add_argument("--foo") 3153 self.assertIsNone(parser.get_default("foo")) 3154 self.assertIsNone(parser.get_default("bar")) 3155 3156 parser.add_argument("--bar", type=int, default=42) 3157 self.assertIsNone(parser.get_default("foo")) 3158 self.assertEqual(42, parser.get_default("bar")) 3159 3160 parser.set_defaults(foo="badger") 3161 self.assertEqual("badger", parser.get_default("foo")) 3162 self.assertEqual(42, parser.get_default("bar")) 3163 3164# ========================== 3165# Namespace 'contains' tests 3166# ========================== 3167 3168class TestNamespaceContainsSimple(TestCase): 3169 3170 def test_empty(self): 3171 ns = argparse.Namespace() 3172 self.assertNotIn('', ns) 3173 self.assertNotIn('x', ns) 3174 3175 def test_non_empty(self): 3176 ns = argparse.Namespace(x=1, y=2) 3177 self.assertNotIn('', ns) 3178 self.assertIn('x', ns) 3179 self.assertIn('y', ns) 3180 self.assertNotIn('xx', ns) 3181 self.assertNotIn('z', ns) 3182 3183# ===================== 3184# Help formatting tests 3185# ===================== 3186 3187class TestHelpFormattingMetaclass(type): 3188 3189 def __init__(cls, name, bases, bodydict): 3190 if name == 'HelpTestCase': 3191 return 3192 3193 class AddTests(object): 3194 3195 def __init__(self, test_class, func_suffix, std_name): 3196 self.func_suffix = func_suffix 3197 self.std_name = std_name 3198 3199 for test_func in [self.test_format, 3200 self.test_print, 3201 self.test_print_file]: 3202 test_name = '%s_%s' % (test_func.__name__, func_suffix) 3203 3204 def test_wrapper(self, test_func=test_func): 3205 test_func(self) 3206 try: 3207 test_wrapper.__name__ = test_name 3208 except TypeError: 3209 pass 3210 setattr(test_class, test_name, test_wrapper) 3211 3212 def _get_parser(self, tester): 3213 parser = argparse.ArgumentParser( 3214 *tester.parser_signature.args, 3215 **tester.parser_signature.kwargs) 3216 for argument_sig in getattr(tester, 'argument_signatures', []): 3217 parser.add_argument(*argument_sig.args, 3218 **argument_sig.kwargs) 3219 group_sigs = getattr(tester, 'argument_group_signatures', []) 3220 for group_sig, argument_sigs in group_sigs: 3221 group = parser.add_argument_group(*group_sig.args, 3222 **group_sig.kwargs) 3223 for argument_sig in argument_sigs: 3224 group.add_argument(*argument_sig.args, 3225 **argument_sig.kwargs) 3226 subparsers_sigs = getattr(tester, 'subparsers_signatures', []) 3227 if subparsers_sigs: 3228 subparsers = parser.add_subparsers() 3229 for subparser_sig in subparsers_sigs: 3230 subparsers.add_parser(*subparser_sig.args, 3231 **subparser_sig.kwargs) 3232 return parser 3233 3234 def _test(self, tester, parser_text): 3235 expected_text = getattr(tester, self.func_suffix) 3236 expected_text = textwrap.dedent(expected_text) 3237 tester.assertEqual(expected_text, parser_text) 3238 3239 def test_format(self, tester): 3240 parser = self._get_parser(tester) 3241 format = getattr(parser, 'format_%s' % self.func_suffix) 3242 self._test(tester, format()) 3243 3244 def test_print(self, tester): 3245 parser = self._get_parser(tester) 3246 print_ = getattr(parser, 'print_%s' % self.func_suffix) 3247 old_stream = getattr(sys, self.std_name) 3248 setattr(sys, self.std_name, StdIOBuffer()) 3249 try: 3250 print_() 3251 parser_text = getattr(sys, self.std_name).getvalue() 3252 finally: 3253 setattr(sys, self.std_name, old_stream) 3254 self._test(tester, parser_text) 3255 3256 def test_print_file(self, tester): 3257 parser = self._get_parser(tester) 3258 print_ = getattr(parser, 'print_%s' % self.func_suffix) 3259 sfile = StdIOBuffer() 3260 print_(sfile) 3261 parser_text = sfile.getvalue() 3262 self._test(tester, parser_text) 3263 3264 # add tests for {format,print}_{usage,help} 3265 for func_suffix, std_name in [('usage', 'stdout'), 3266 ('help', 'stdout')]: 3267 AddTests(cls, func_suffix, std_name) 3268 3269bases = TestCase, 3270HelpTestCase = TestHelpFormattingMetaclass('HelpTestCase', bases, {}) 3271 3272 3273class TestHelpBiggerOptionals(HelpTestCase): 3274 """Make sure that argument help aligns when options are longer""" 3275 3276 parser_signature = Sig(prog='PROG', description='DESCRIPTION', 3277 epilog='EPILOG') 3278 argument_signatures = [ 3279 Sig('-v', '--version', action='version', version='0.1'), 3280 Sig('-x', action='store_true', help='X HELP'), 3281 Sig('--y', help='Y HELP'), 3282 Sig('foo', help='FOO HELP'), 3283 Sig('bar', help='BAR HELP'), 3284 ] 3285 argument_group_signatures = [] 3286 usage = '''\ 3287 usage: PROG [-h] [-v] [-x] [--y Y] foo bar 3288 ''' 3289 help = usage + '''\ 3290 3291 DESCRIPTION 3292 3293 positional arguments: 3294 foo FOO HELP 3295 bar BAR HELP 3296 3297 options: 3298 -h, --help show this help message and exit 3299 -v, --version show program's version number and exit 3300 -x X HELP 3301 --y Y Y HELP 3302 3303 EPILOG 3304 ''' 3305 version = '''\ 3306 0.1 3307 ''' 3308 3309class TestShortColumns(HelpTestCase): 3310 '''Test extremely small number of columns. 3311 3312 TestCase prevents "COLUMNS" from being too small in the tests themselves, 3313 but we don't want any exceptions thrown in such cases. Only ugly representation. 3314 ''' 3315 def setUp(self): 3316 env = os_helper.EnvironmentVarGuard() 3317 env.set("COLUMNS", '15') 3318 self.addCleanup(env.__exit__) 3319 3320 parser_signature = TestHelpBiggerOptionals.parser_signature 3321 argument_signatures = TestHelpBiggerOptionals.argument_signatures 3322 argument_group_signatures = TestHelpBiggerOptionals.argument_group_signatures 3323 usage = '''\ 3324 usage: PROG 3325 [-h] 3326 [-v] 3327 [-x] 3328 [--y Y] 3329 foo 3330 bar 3331 ''' 3332 help = usage + '''\ 3333 3334 DESCRIPTION 3335 3336 positional arguments: 3337 foo 3338 FOO HELP 3339 bar 3340 BAR HELP 3341 3342 options: 3343 -h, --help 3344 show this 3345 help 3346 message and 3347 exit 3348 -v, --version 3349 show 3350 program's 3351 version 3352 number and 3353 exit 3354 -x 3355 X HELP 3356 --y Y 3357 Y HELP 3358 3359 EPILOG 3360 ''' 3361 version = TestHelpBiggerOptionals.version 3362 3363 3364class TestHelpBiggerOptionalGroups(HelpTestCase): 3365 """Make sure that argument help aligns when options are longer""" 3366 3367 parser_signature = Sig(prog='PROG', description='DESCRIPTION', 3368 epilog='EPILOG') 3369 argument_signatures = [ 3370 Sig('-v', '--version', action='version', version='0.1'), 3371 Sig('-x', action='store_true', help='X HELP'), 3372 Sig('--y', help='Y HELP'), 3373 Sig('foo', help='FOO HELP'), 3374 Sig('bar', help='BAR HELP'), 3375 ] 3376 argument_group_signatures = [ 3377 (Sig('GROUP TITLE', description='GROUP DESCRIPTION'), [ 3378 Sig('baz', help='BAZ HELP'), 3379 Sig('-z', nargs='+', help='Z HELP')]), 3380 ] 3381 usage = '''\ 3382 usage: PROG [-h] [-v] [-x] [--y Y] [-z Z [Z ...]] foo bar baz 3383 ''' 3384 help = usage + '''\ 3385 3386 DESCRIPTION 3387 3388 positional arguments: 3389 foo FOO HELP 3390 bar BAR HELP 3391 3392 options: 3393 -h, --help show this help message and exit 3394 -v, --version show program's version number and exit 3395 -x X HELP 3396 --y Y Y HELP 3397 3398 GROUP TITLE: 3399 GROUP DESCRIPTION 3400 3401 baz BAZ HELP 3402 -z Z [Z ...] Z HELP 3403 3404 EPILOG 3405 ''' 3406 version = '''\ 3407 0.1 3408 ''' 3409 3410 3411class TestHelpBiggerPositionals(HelpTestCase): 3412 """Make sure that help aligns when arguments are longer""" 3413 3414 parser_signature = Sig(usage='USAGE', description='DESCRIPTION') 3415 argument_signatures = [ 3416 Sig('-x', action='store_true', help='X HELP'), 3417 Sig('--y', help='Y HELP'), 3418 Sig('ekiekiekifekang', help='EKI HELP'), 3419 Sig('bar', help='BAR HELP'), 3420 ] 3421 argument_group_signatures = [] 3422 usage = '''\ 3423 usage: USAGE 3424 ''' 3425 help = usage + '''\ 3426 3427 DESCRIPTION 3428 3429 positional arguments: 3430 ekiekiekifekang EKI HELP 3431 bar BAR HELP 3432 3433 options: 3434 -h, --help show this help message and exit 3435 -x X HELP 3436 --y Y Y HELP 3437 ''' 3438 3439 version = '' 3440 3441 3442class TestHelpReformatting(HelpTestCase): 3443 """Make sure that text after short names starts on the first line""" 3444 3445 parser_signature = Sig( 3446 prog='PROG', 3447 description=' oddly formatted\n' 3448 'description\n' 3449 '\n' 3450 'that is so long that it should go onto multiple ' 3451 'lines when wrapped') 3452 argument_signatures = [ 3453 Sig('-x', metavar='XX', help='oddly\n' 3454 ' formatted -x help'), 3455 Sig('y', metavar='yyy', help='normal y help'), 3456 ] 3457 argument_group_signatures = [ 3458 (Sig('title', description='\n' 3459 ' oddly formatted group\n' 3460 '\n' 3461 'description'), 3462 [Sig('-a', action='store_true', 3463 help=' oddly \n' 3464 'formatted -a help \n' 3465 ' again, so long that it should be wrapped over ' 3466 'multiple lines')]), 3467 ] 3468 usage = '''\ 3469 usage: PROG [-h] [-x XX] [-a] yyy 3470 ''' 3471 help = usage + '''\ 3472 3473 oddly formatted description that is so long that it should go onto \ 3474multiple 3475 lines when wrapped 3476 3477 positional arguments: 3478 yyy normal y help 3479 3480 options: 3481 -h, --help show this help message and exit 3482 -x XX oddly formatted -x help 3483 3484 title: 3485 oddly formatted group description 3486 3487 -a oddly formatted -a help again, so long that it should \ 3488be wrapped 3489 over multiple lines 3490 ''' 3491 version = '' 3492 3493 3494class TestHelpWrappingShortNames(HelpTestCase): 3495 """Make sure that text after short names starts on the first line""" 3496 3497 parser_signature = Sig(prog='PROG', description= 'D\nD' * 30) 3498 argument_signatures = [ 3499 Sig('-x', metavar='XX', help='XHH HX' * 20), 3500 Sig('y', metavar='yyy', help='YH YH' * 20), 3501 ] 3502 argument_group_signatures = [ 3503 (Sig('ALPHAS'), [ 3504 Sig('-a', action='store_true', help='AHHH HHA' * 10)]), 3505 ] 3506 usage = '''\ 3507 usage: PROG [-h] [-x XX] [-a] yyy 3508 ''' 3509 help = usage + '''\ 3510 3511 D DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD \ 3512DD DD DD 3513 DD DD DD DD D 3514 3515 positional arguments: 3516 yyy YH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH \ 3517YHYH YHYH 3518 YHYH YHYH YHYH YHYH YHYH YHYH YHYH YH 3519 3520 options: 3521 -h, --help show this help message and exit 3522 -x XX XHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH \ 3523HXXHH HXXHH 3524 HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HX 3525 3526 ALPHAS: 3527 -a AHHH HHAAHHH HHAAHHH HHAAHHH HHAAHHH HHAAHHH HHAAHHH \ 3528HHAAHHH 3529 HHAAHHH HHAAHHH HHA 3530 ''' 3531 version = '' 3532 3533 3534class TestHelpWrappingLongNames(HelpTestCase): 3535 """Make sure that text after long names starts on the next line""" 3536 3537 parser_signature = Sig(usage='USAGE', description= 'D D' * 30) 3538 argument_signatures = [ 3539 Sig('-v', '--version', action='version', version='V V' * 30), 3540 Sig('-x', metavar='X' * 25, help='XH XH' * 20), 3541 Sig('y', metavar='y' * 25, help='YH YH' * 20), 3542 ] 3543 argument_group_signatures = [ 3544 (Sig('ALPHAS'), [ 3545 Sig('-a', metavar='A' * 25, help='AH AH' * 20), 3546 Sig('z', metavar='z' * 25, help='ZH ZH' * 20)]), 3547 ] 3548 usage = '''\ 3549 usage: USAGE 3550 ''' 3551 help = usage + '''\ 3552 3553 D DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD \ 3554DD DD DD 3555 DD DD DD DD D 3556 3557 positional arguments: 3558 yyyyyyyyyyyyyyyyyyyyyyyyy 3559 YH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH \ 3560YHYH YHYH 3561 YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YH 3562 3563 options: 3564 -h, --help show this help message and exit 3565 -v, --version show program's version number and exit 3566 -x XXXXXXXXXXXXXXXXXXXXXXXXX 3567 XH XHXH XHXH XHXH XHXH XHXH XHXH XHXH XHXH \ 3568XHXH XHXH 3569 XHXH XHXH XHXH XHXH XHXH XHXH XHXH XHXH XHXH XH 3570 3571 ALPHAS: 3572 -a AAAAAAAAAAAAAAAAAAAAAAAAA 3573 AH AHAH AHAH AHAH AHAH AHAH AHAH AHAH AHAH \ 3574AHAH AHAH 3575 AHAH AHAH AHAH AHAH AHAH AHAH AHAH AHAH AHAH AH 3576 zzzzzzzzzzzzzzzzzzzzzzzzz 3577 ZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH \ 3578ZHZH ZHZH 3579 ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZH 3580 ''' 3581 version = '''\ 3582 V VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV \ 3583VV VV VV 3584 VV VV VV VV V 3585 ''' 3586 3587 3588class TestHelpUsage(HelpTestCase): 3589 """Test basic usage messages""" 3590 3591 parser_signature = Sig(prog='PROG') 3592 argument_signatures = [ 3593 Sig('-w', nargs='+', help='w'), 3594 Sig('-x', nargs='*', help='x'), 3595 Sig('a', help='a'), 3596 Sig('b', help='b', nargs=2), 3597 Sig('c', help='c', nargs='?'), 3598 Sig('--foo', help='Whether to foo', action=argparse.BooleanOptionalAction), 3599 Sig('--bar', help='Whether to bar', default=True, 3600 action=argparse.BooleanOptionalAction), 3601 Sig('-f', '--foobar', '--barfoo', action=argparse.BooleanOptionalAction), 3602 ] 3603 argument_group_signatures = [ 3604 (Sig('group'), [ 3605 Sig('-y', nargs='?', help='y'), 3606 Sig('-z', nargs=3, help='z'), 3607 Sig('d', help='d', nargs='*'), 3608 Sig('e', help='e', nargs='+'), 3609 ]) 3610 ] 3611 usage = '''\ 3612 usage: PROG [-h] [-w W [W ...]] [-x [X ...]] [--foo | --no-foo] 3613 [--bar | --no-bar] 3614 [-f | --foobar | --no-foobar | --barfoo | --no-barfoo] [-y [Y]] 3615 [-z Z Z Z] 3616 a b b [c] [d ...] e [e ...] 3617 ''' 3618 help = usage + '''\ 3619 3620 positional arguments: 3621 a a 3622 b b 3623 c c 3624 3625 options: 3626 -h, --help show this help message and exit 3627 -w W [W ...] w 3628 -x [X ...] x 3629 --foo, --no-foo Whether to foo 3630 --bar, --no-bar Whether to bar (default: True) 3631 -f, --foobar, --no-foobar, --barfoo, --no-barfoo 3632 3633 group: 3634 -y [Y] y 3635 -z Z Z Z z 3636 d d 3637 e e 3638 ''' 3639 version = '' 3640 3641 3642class TestHelpOnlyUserGroups(HelpTestCase): 3643 """Test basic usage messages""" 3644 3645 parser_signature = Sig(prog='PROG', add_help=False) 3646 argument_signatures = [] 3647 argument_group_signatures = [ 3648 (Sig('xxxx'), [ 3649 Sig('-x', help='x'), 3650 Sig('a', help='a'), 3651 ]), 3652 (Sig('yyyy'), [ 3653 Sig('b', help='b'), 3654 Sig('-y', help='y'), 3655 ]), 3656 ] 3657 usage = '''\ 3658 usage: PROG [-x X] [-y Y] a b 3659 ''' 3660 help = usage + '''\ 3661 3662 xxxx: 3663 -x X x 3664 a a 3665 3666 yyyy: 3667 b b 3668 -y Y y 3669 ''' 3670 version = '' 3671 3672 3673class TestHelpUsageLongProg(HelpTestCase): 3674 """Test usage messages where the prog is long""" 3675 3676 parser_signature = Sig(prog='P' * 60) 3677 argument_signatures = [ 3678 Sig('-w', metavar='W'), 3679 Sig('-x', metavar='X'), 3680 Sig('a'), 3681 Sig('b'), 3682 ] 3683 argument_group_signatures = [] 3684 usage = '''\ 3685 usage: PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP 3686 [-h] [-w W] [-x X] a b 3687 ''' 3688 help = usage + '''\ 3689 3690 positional arguments: 3691 a 3692 b 3693 3694 options: 3695 -h, --help show this help message and exit 3696 -w W 3697 -x X 3698 ''' 3699 version = '' 3700 3701 3702class TestHelpUsageLongProgOptionsWrap(HelpTestCase): 3703 """Test usage messages where the prog is long and the optionals wrap""" 3704 3705 parser_signature = Sig(prog='P' * 60) 3706 argument_signatures = [ 3707 Sig('-w', metavar='W' * 25), 3708 Sig('-x', metavar='X' * 25), 3709 Sig('-y', metavar='Y' * 25), 3710 Sig('-z', metavar='Z' * 25), 3711 Sig('a'), 3712 Sig('b'), 3713 ] 3714 argument_group_signatures = [] 3715 usage = '''\ 3716 usage: PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP 3717 [-h] [-w WWWWWWWWWWWWWWWWWWWWWWWWW] \ 3718[-x XXXXXXXXXXXXXXXXXXXXXXXXX] 3719 [-y YYYYYYYYYYYYYYYYYYYYYYYYY] [-z ZZZZZZZZZZZZZZZZZZZZZZZZZ] 3720 a b 3721 ''' 3722 help = usage + '''\ 3723 3724 positional arguments: 3725 a 3726 b 3727 3728 options: 3729 -h, --help show this help message and exit 3730 -w WWWWWWWWWWWWWWWWWWWWWWWWW 3731 -x XXXXXXXXXXXXXXXXXXXXXXXXX 3732 -y YYYYYYYYYYYYYYYYYYYYYYYYY 3733 -z ZZZZZZZZZZZZZZZZZZZZZZZZZ 3734 ''' 3735 version = '' 3736 3737 3738class TestHelpUsageLongProgPositionalsWrap(HelpTestCase): 3739 """Test usage messages where the prog is long and the positionals wrap""" 3740 3741 parser_signature = Sig(prog='P' * 60, add_help=False) 3742 argument_signatures = [ 3743 Sig('a' * 25), 3744 Sig('b' * 25), 3745 Sig('c' * 25), 3746 ] 3747 argument_group_signatures = [] 3748 usage = '''\ 3749 usage: PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP 3750 aaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbb 3751 ccccccccccccccccccccccccc 3752 ''' 3753 help = usage + '''\ 3754 3755 positional arguments: 3756 aaaaaaaaaaaaaaaaaaaaaaaaa 3757 bbbbbbbbbbbbbbbbbbbbbbbbb 3758 ccccccccccccccccccccccccc 3759 ''' 3760 version = '' 3761 3762 3763class TestHelpUsageOptionalsWrap(HelpTestCase): 3764 """Test usage messages where the optionals wrap""" 3765 3766 parser_signature = Sig(prog='PROG') 3767 argument_signatures = [ 3768 Sig('-w', metavar='W' * 25), 3769 Sig('-x', metavar='X' * 25), 3770 Sig('-y', metavar='Y' * 25), 3771 Sig('-z', metavar='Z' * 25), 3772 Sig('a'), 3773 Sig('b'), 3774 Sig('c'), 3775 ] 3776 argument_group_signatures = [] 3777 usage = '''\ 3778 usage: PROG [-h] [-w WWWWWWWWWWWWWWWWWWWWWWWWW] \ 3779[-x XXXXXXXXXXXXXXXXXXXXXXXXX] 3780 [-y YYYYYYYYYYYYYYYYYYYYYYYYY] \ 3781[-z ZZZZZZZZZZZZZZZZZZZZZZZZZ] 3782 a b c 3783 ''' 3784 help = usage + '''\ 3785 3786 positional arguments: 3787 a 3788 b 3789 c 3790 3791 options: 3792 -h, --help show this help message and exit 3793 -w WWWWWWWWWWWWWWWWWWWWWWWWW 3794 -x XXXXXXXXXXXXXXXXXXXXXXXXX 3795 -y YYYYYYYYYYYYYYYYYYYYYYYYY 3796 -z ZZZZZZZZZZZZZZZZZZZZZZZZZ 3797 ''' 3798 version = '' 3799 3800 3801class TestHelpUsagePositionalsWrap(HelpTestCase): 3802 """Test usage messages where the positionals wrap""" 3803 3804 parser_signature = Sig(prog='PROG') 3805 argument_signatures = [ 3806 Sig('-x'), 3807 Sig('-y'), 3808 Sig('-z'), 3809 Sig('a' * 25), 3810 Sig('b' * 25), 3811 Sig('c' * 25), 3812 ] 3813 argument_group_signatures = [] 3814 usage = '''\ 3815 usage: PROG [-h] [-x X] [-y Y] [-z Z] 3816 aaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbb 3817 ccccccccccccccccccccccccc 3818 ''' 3819 help = usage + '''\ 3820 3821 positional arguments: 3822 aaaaaaaaaaaaaaaaaaaaaaaaa 3823 bbbbbbbbbbbbbbbbbbbbbbbbb 3824 ccccccccccccccccccccccccc 3825 3826 options: 3827 -h, --help show this help message and exit 3828 -x X 3829 -y Y 3830 -z Z 3831 ''' 3832 version = '' 3833 3834 3835class TestHelpUsageOptionalsPositionalsWrap(HelpTestCase): 3836 """Test usage messages where the optionals and positionals wrap""" 3837 3838 parser_signature = Sig(prog='PROG') 3839 argument_signatures = [ 3840 Sig('-x', metavar='X' * 25), 3841 Sig('-y', metavar='Y' * 25), 3842 Sig('-z', metavar='Z' * 25), 3843 Sig('a' * 25), 3844 Sig('b' * 25), 3845 Sig('c' * 25), 3846 ] 3847 argument_group_signatures = [] 3848 usage = '''\ 3849 usage: PROG [-h] [-x XXXXXXXXXXXXXXXXXXXXXXXXX] \ 3850[-y YYYYYYYYYYYYYYYYYYYYYYYYY] 3851 [-z ZZZZZZZZZZZZZZZZZZZZZZZZZ] 3852 aaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbb 3853 ccccccccccccccccccccccccc 3854 ''' 3855 help = usage + '''\ 3856 3857 positional arguments: 3858 aaaaaaaaaaaaaaaaaaaaaaaaa 3859 bbbbbbbbbbbbbbbbbbbbbbbbb 3860 ccccccccccccccccccccccccc 3861 3862 options: 3863 -h, --help show this help message and exit 3864 -x XXXXXXXXXXXXXXXXXXXXXXXXX 3865 -y YYYYYYYYYYYYYYYYYYYYYYYYY 3866 -z ZZZZZZZZZZZZZZZZZZZZZZZZZ 3867 ''' 3868 version = '' 3869 3870 3871class TestHelpUsageOptionalsOnlyWrap(HelpTestCase): 3872 """Test usage messages where there are only optionals and they wrap""" 3873 3874 parser_signature = Sig(prog='PROG') 3875 argument_signatures = [ 3876 Sig('-x', metavar='X' * 25), 3877 Sig('-y', metavar='Y' * 25), 3878 Sig('-z', metavar='Z' * 25), 3879 ] 3880 argument_group_signatures = [] 3881 usage = '''\ 3882 usage: PROG [-h] [-x XXXXXXXXXXXXXXXXXXXXXXXXX] \ 3883[-y YYYYYYYYYYYYYYYYYYYYYYYYY] 3884 [-z ZZZZZZZZZZZZZZZZZZZZZZZZZ] 3885 ''' 3886 help = usage + '''\ 3887 3888 options: 3889 -h, --help show this help message and exit 3890 -x XXXXXXXXXXXXXXXXXXXXXXXXX 3891 -y YYYYYYYYYYYYYYYYYYYYYYYYY 3892 -z ZZZZZZZZZZZZZZZZZZZZZZZZZ 3893 ''' 3894 version = '' 3895 3896 3897class TestHelpUsagePositionalsOnlyWrap(HelpTestCase): 3898 """Test usage messages where there are only positionals and they wrap""" 3899 3900 parser_signature = Sig(prog='PROG', add_help=False) 3901 argument_signatures = [ 3902 Sig('a' * 25), 3903 Sig('b' * 25), 3904 Sig('c' * 25), 3905 ] 3906 argument_group_signatures = [] 3907 usage = '''\ 3908 usage: PROG aaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbb 3909 ccccccccccccccccccccccccc 3910 ''' 3911 help = usage + '''\ 3912 3913 positional arguments: 3914 aaaaaaaaaaaaaaaaaaaaaaaaa 3915 bbbbbbbbbbbbbbbbbbbbbbbbb 3916 ccccccccccccccccccccccccc 3917 ''' 3918 version = '' 3919 3920 3921class TestHelpVariableExpansion(HelpTestCase): 3922 """Test that variables are expanded properly in help messages""" 3923 3924 parser_signature = Sig(prog='PROG') 3925 argument_signatures = [ 3926 Sig('-x', type=int, 3927 help='x %(prog)s %(default)s %(type)s %%'), 3928 Sig('-y', action='store_const', default=42, const='XXX', 3929 help='y %(prog)s %(default)s %(const)s'), 3930 Sig('--foo', choices='abc', 3931 help='foo %(prog)s %(default)s %(choices)s'), 3932 Sig('--bar', default='baz', choices=[1, 2], metavar='BBB', 3933 help='bar %(prog)s %(default)s %(dest)s'), 3934 Sig('spam', help='spam %(prog)s %(default)s'), 3935 Sig('badger', default=0.5, help='badger %(prog)s %(default)s'), 3936 ] 3937 argument_group_signatures = [ 3938 (Sig('group'), [ 3939 Sig('-a', help='a %(prog)s %(default)s'), 3940 Sig('-b', default=-1, help='b %(prog)s %(default)s'), 3941 ]) 3942 ] 3943 usage = ('''\ 3944 usage: PROG [-h] [-x X] [-y] [--foo {a,b,c}] [--bar BBB] [-a A] [-b B] 3945 spam badger 3946 ''') 3947 help = usage + '''\ 3948 3949 positional arguments: 3950 spam spam PROG None 3951 badger badger PROG 0.5 3952 3953 options: 3954 -h, --help show this help message and exit 3955 -x X x PROG None int % 3956 -y y PROG 42 XXX 3957 --foo {a,b,c} foo PROG None a, b, c 3958 --bar BBB bar PROG baz bar 3959 3960 group: 3961 -a A a PROG None 3962 -b B b PROG -1 3963 ''' 3964 version = '' 3965 3966 3967class TestHelpVariableExpansionUsageSupplied(HelpTestCase): 3968 """Test that variables are expanded properly when usage= is present""" 3969 3970 parser_signature = Sig(prog='PROG', usage='%(prog)s FOO') 3971 argument_signatures = [] 3972 argument_group_signatures = [] 3973 usage = ('''\ 3974 usage: PROG FOO 3975 ''') 3976 help = usage + '''\ 3977 3978 options: 3979 -h, --help show this help message and exit 3980 ''' 3981 version = '' 3982 3983 3984class TestHelpVariableExpansionNoArguments(HelpTestCase): 3985 """Test that variables are expanded properly with no arguments""" 3986 3987 parser_signature = Sig(prog='PROG', add_help=False) 3988 argument_signatures = [] 3989 argument_group_signatures = [] 3990 usage = ('''\ 3991 usage: PROG 3992 ''') 3993 help = usage 3994 version = '' 3995 3996 3997class TestHelpSuppressUsage(HelpTestCase): 3998 """Test that items can be suppressed in usage messages""" 3999 4000 parser_signature = Sig(prog='PROG', usage=argparse.SUPPRESS) 4001 argument_signatures = [ 4002 Sig('--foo', help='foo help'), 4003 Sig('spam', help='spam help'), 4004 ] 4005 argument_group_signatures = [] 4006 help = '''\ 4007 positional arguments: 4008 spam spam help 4009 4010 options: 4011 -h, --help show this help message and exit 4012 --foo FOO foo help 4013 ''' 4014 usage = '' 4015 version = '' 4016 4017 4018class TestHelpSuppressOptional(HelpTestCase): 4019 """Test that optional arguments can be suppressed in help messages""" 4020 4021 parser_signature = Sig(prog='PROG', add_help=False) 4022 argument_signatures = [ 4023 Sig('--foo', help=argparse.SUPPRESS), 4024 Sig('spam', help='spam help'), 4025 ] 4026 argument_group_signatures = [] 4027 usage = '''\ 4028 usage: PROG spam 4029 ''' 4030 help = usage + '''\ 4031 4032 positional arguments: 4033 spam spam help 4034 ''' 4035 version = '' 4036 4037 4038class TestHelpSuppressOptionalGroup(HelpTestCase): 4039 """Test that optional groups can be suppressed in help messages""" 4040 4041 parser_signature = Sig(prog='PROG') 4042 argument_signatures = [ 4043 Sig('--foo', help='foo help'), 4044 Sig('spam', help='spam help'), 4045 ] 4046 argument_group_signatures = [ 4047 (Sig('group'), [Sig('--bar', help=argparse.SUPPRESS)]), 4048 ] 4049 usage = '''\ 4050 usage: PROG [-h] [--foo FOO] spam 4051 ''' 4052 help = usage + '''\ 4053 4054 positional arguments: 4055 spam spam help 4056 4057 options: 4058 -h, --help show this help message and exit 4059 --foo FOO foo help 4060 ''' 4061 version = '' 4062 4063 4064class TestHelpSuppressPositional(HelpTestCase): 4065 """Test that positional arguments can be suppressed in help messages""" 4066 4067 parser_signature = Sig(prog='PROG') 4068 argument_signatures = [ 4069 Sig('--foo', help='foo help'), 4070 Sig('spam', help=argparse.SUPPRESS), 4071 ] 4072 argument_group_signatures = [] 4073 usage = '''\ 4074 usage: PROG [-h] [--foo FOO] 4075 ''' 4076 help = usage + '''\ 4077 4078 options: 4079 -h, --help show this help message and exit 4080 --foo FOO foo help 4081 ''' 4082 version = '' 4083 4084 4085class TestHelpRequiredOptional(HelpTestCase): 4086 """Test that required options don't look optional""" 4087 4088 parser_signature = Sig(prog='PROG') 4089 argument_signatures = [ 4090 Sig('--foo', required=True, help='foo help'), 4091 ] 4092 argument_group_signatures = [] 4093 usage = '''\ 4094 usage: PROG [-h] --foo FOO 4095 ''' 4096 help = usage + '''\ 4097 4098 options: 4099 -h, --help show this help message and exit 4100 --foo FOO foo help 4101 ''' 4102 version = '' 4103 4104 4105class TestHelpAlternatePrefixChars(HelpTestCase): 4106 """Test that options display with different prefix characters""" 4107 4108 parser_signature = Sig(prog='PROG', prefix_chars='^;', add_help=False) 4109 argument_signatures = [ 4110 Sig('^^foo', action='store_true', help='foo help'), 4111 Sig(';b', ';;bar', help='bar help'), 4112 ] 4113 argument_group_signatures = [] 4114 usage = '''\ 4115 usage: PROG [^^foo] [;b BAR] 4116 ''' 4117 help = usage + '''\ 4118 4119 options: 4120 ^^foo foo help 4121 ;b BAR, ;;bar BAR bar help 4122 ''' 4123 version = '' 4124 4125 4126class TestHelpNoHelpOptional(HelpTestCase): 4127 """Test that the --help argument can be suppressed help messages""" 4128 4129 parser_signature = Sig(prog='PROG', add_help=False) 4130 argument_signatures = [ 4131 Sig('--foo', help='foo help'), 4132 Sig('spam', help='spam help'), 4133 ] 4134 argument_group_signatures = [] 4135 usage = '''\ 4136 usage: PROG [--foo FOO] spam 4137 ''' 4138 help = usage + '''\ 4139 4140 positional arguments: 4141 spam spam help 4142 4143 options: 4144 --foo FOO foo help 4145 ''' 4146 version = '' 4147 4148 4149class TestHelpNone(HelpTestCase): 4150 """Test that no errors occur if no help is specified""" 4151 4152 parser_signature = Sig(prog='PROG') 4153 argument_signatures = [ 4154 Sig('--foo'), 4155 Sig('spam'), 4156 ] 4157 argument_group_signatures = [] 4158 usage = '''\ 4159 usage: PROG [-h] [--foo FOO] spam 4160 ''' 4161 help = usage + '''\ 4162 4163 positional arguments: 4164 spam 4165 4166 options: 4167 -h, --help show this help message and exit 4168 --foo FOO 4169 ''' 4170 version = '' 4171 4172 4173class TestHelpTupleMetavar(HelpTestCase): 4174 """Test specifying metavar as a tuple""" 4175 4176 parser_signature = Sig(prog='PROG') 4177 argument_signatures = [ 4178 Sig('-w', help='w', nargs='+', metavar=('W1', 'W2')), 4179 Sig('-x', help='x', nargs='*', metavar=('X1', 'X2')), 4180 Sig('-y', help='y', nargs=3, metavar=('Y1', 'Y2', 'Y3')), 4181 Sig('-z', help='z', nargs='?', metavar=('Z1', )), 4182 ] 4183 argument_group_signatures = [] 4184 usage = '''\ 4185 usage: PROG [-h] [-w W1 [W2 ...]] [-x [X1 [X2 ...]]] [-y Y1 Y2 Y3] \ 4186[-z [Z1]] 4187 ''' 4188 help = usage + '''\ 4189 4190 options: 4191 -h, --help show this help message and exit 4192 -w W1 [W2 ...] w 4193 -x [X1 [X2 ...]] x 4194 -y Y1 Y2 Y3 y 4195 -z [Z1] z 4196 ''' 4197 version = '' 4198 4199 4200class TestHelpRawText(HelpTestCase): 4201 """Test the RawTextHelpFormatter""" 4202 4203 parser_signature = Sig( 4204 prog='PROG', formatter_class=argparse.RawTextHelpFormatter, 4205 description='Keep the formatting\n' 4206 ' exactly as it is written\n' 4207 '\n' 4208 'here\n') 4209 4210 argument_signatures = [ 4211 Sig('--foo', help=' foo help should also\n' 4212 'appear as given here'), 4213 Sig('spam', help='spam help'), 4214 ] 4215 argument_group_signatures = [ 4216 (Sig('title', description=' This text\n' 4217 ' should be indented\n' 4218 ' exactly like it is here\n'), 4219 [Sig('--bar', help='bar help')]), 4220 ] 4221 usage = '''\ 4222 usage: PROG [-h] [--foo FOO] [--bar BAR] spam 4223 ''' 4224 help = usage + '''\ 4225 4226 Keep the formatting 4227 exactly as it is written 4228 4229 here 4230 4231 positional arguments: 4232 spam spam help 4233 4234 options: 4235 -h, --help show this help message and exit 4236 --foo FOO foo help should also 4237 appear as given here 4238 4239 title: 4240 This text 4241 should be indented 4242 exactly like it is here 4243 4244 --bar BAR bar help 4245 ''' 4246 version = '' 4247 4248 4249class TestHelpRawDescription(HelpTestCase): 4250 """Test the RawTextHelpFormatter""" 4251 4252 parser_signature = Sig( 4253 prog='PROG', formatter_class=argparse.RawDescriptionHelpFormatter, 4254 description='Keep the formatting\n' 4255 ' exactly as it is written\n' 4256 '\n' 4257 'here\n') 4258 4259 argument_signatures = [ 4260 Sig('--foo', help=' foo help should not\n' 4261 ' retain this odd formatting'), 4262 Sig('spam', help='spam help'), 4263 ] 4264 argument_group_signatures = [ 4265 (Sig('title', description=' This text\n' 4266 ' should be indented\n' 4267 ' exactly like it is here\n'), 4268 [Sig('--bar', help='bar help')]), 4269 ] 4270 usage = '''\ 4271 usage: PROG [-h] [--foo FOO] [--bar BAR] spam 4272 ''' 4273 help = usage + '''\ 4274 4275 Keep the formatting 4276 exactly as it is written 4277 4278 here 4279 4280 positional arguments: 4281 spam spam help 4282 4283 options: 4284 -h, --help show this help message and exit 4285 --foo FOO foo help should not retain this odd formatting 4286 4287 title: 4288 This text 4289 should be indented 4290 exactly like it is here 4291 4292 --bar BAR bar help 4293 ''' 4294 version = '' 4295 4296 4297class TestHelpArgumentDefaults(HelpTestCase): 4298 """Test the ArgumentDefaultsHelpFormatter""" 4299 4300 parser_signature = Sig( 4301 prog='PROG', formatter_class=argparse.ArgumentDefaultsHelpFormatter, 4302 description='description') 4303 4304 argument_signatures = [ 4305 Sig('--foo', help='foo help - oh and by the way, %(default)s'), 4306 Sig('--bar', action='store_true', help='bar help'), 4307 Sig('--taz', action=argparse.BooleanOptionalAction, 4308 help='Whether to taz it', default=True), 4309 Sig('--quux', help="Set the quux", default=42), 4310 Sig('spam', help='spam help'), 4311 Sig('badger', nargs='?', default='wooden', help='badger help'), 4312 ] 4313 argument_group_signatures = [ 4314 (Sig('title', description='description'), 4315 [Sig('--baz', type=int, default=42, help='baz help')]), 4316 ] 4317 usage = '''\ 4318 usage: PROG [-h] [--foo FOO] [--bar] [--taz | --no-taz] [--quux QUUX] 4319 [--baz BAZ] 4320 spam [badger] 4321 ''' 4322 help = usage + '''\ 4323 4324 description 4325 4326 positional arguments: 4327 spam spam help 4328 badger badger help (default: wooden) 4329 4330 options: 4331 -h, --help show this help message and exit 4332 --foo FOO foo help - oh and by the way, None 4333 --bar bar help (default: False) 4334 --taz, --no-taz Whether to taz it (default: True) 4335 --quux QUUX Set the quux (default: 42) 4336 4337 title: 4338 description 4339 4340 --baz BAZ baz help (default: 42) 4341 ''' 4342 version = '' 4343 4344class TestHelpVersionAction(HelpTestCase): 4345 """Test the default help for the version action""" 4346 4347 parser_signature = Sig(prog='PROG', description='description') 4348 argument_signatures = [Sig('-V', '--version', action='version', version='3.6')] 4349 argument_group_signatures = [] 4350 usage = '''\ 4351 usage: PROG [-h] [-V] 4352 ''' 4353 help = usage + '''\ 4354 4355 description 4356 4357 options: 4358 -h, --help show this help message and exit 4359 -V, --version show program's version number and exit 4360 ''' 4361 version = '' 4362 4363 4364class TestHelpVersionActionSuppress(HelpTestCase): 4365 """Test that the --version argument can be suppressed in help messages""" 4366 4367 parser_signature = Sig(prog='PROG') 4368 argument_signatures = [ 4369 Sig('-v', '--version', action='version', version='1.0', 4370 help=argparse.SUPPRESS), 4371 Sig('--foo', help='foo help'), 4372 Sig('spam', help='spam help'), 4373 ] 4374 argument_group_signatures = [] 4375 usage = '''\ 4376 usage: PROG [-h] [--foo FOO] spam 4377 ''' 4378 help = usage + '''\ 4379 4380 positional arguments: 4381 spam spam help 4382 4383 options: 4384 -h, --help show this help message and exit 4385 --foo FOO foo help 4386 ''' 4387 4388 4389class TestHelpSubparsersOrdering(HelpTestCase): 4390 """Test ordering of subcommands in help matches the code""" 4391 parser_signature = Sig(prog='PROG', 4392 description='display some subcommands') 4393 argument_signatures = [Sig('-v', '--version', action='version', version='0.1')] 4394 4395 subparsers_signatures = [Sig(name=name) 4396 for name in ('a', 'b', 'c', 'd', 'e')] 4397 4398 usage = '''\ 4399 usage: PROG [-h] [-v] {a,b,c,d,e} ... 4400 ''' 4401 4402 help = usage + '''\ 4403 4404 display some subcommands 4405 4406 positional arguments: 4407 {a,b,c,d,e} 4408 4409 options: 4410 -h, --help show this help message and exit 4411 -v, --version show program's version number and exit 4412 ''' 4413 4414 version = '''\ 4415 0.1 4416 ''' 4417 4418class TestHelpSubparsersWithHelpOrdering(HelpTestCase): 4419 """Test ordering of subcommands in help matches the code""" 4420 parser_signature = Sig(prog='PROG', 4421 description='display some subcommands') 4422 argument_signatures = [Sig('-v', '--version', action='version', version='0.1')] 4423 4424 subcommand_data = (('a', 'a subcommand help'), 4425 ('b', 'b subcommand help'), 4426 ('c', 'c subcommand help'), 4427 ('d', 'd subcommand help'), 4428 ('e', 'e subcommand help'), 4429 ) 4430 4431 subparsers_signatures = [Sig(name=name, help=help) 4432 for name, help in subcommand_data] 4433 4434 usage = '''\ 4435 usage: PROG [-h] [-v] {a,b,c,d,e} ... 4436 ''' 4437 4438 help = usage + '''\ 4439 4440 display some subcommands 4441 4442 positional arguments: 4443 {a,b,c,d,e} 4444 a a subcommand help 4445 b b subcommand help 4446 c c subcommand help 4447 d d subcommand help 4448 e e subcommand help 4449 4450 options: 4451 -h, --help show this help message and exit 4452 -v, --version show program's version number and exit 4453 ''' 4454 4455 version = '''\ 4456 0.1 4457 ''' 4458 4459 4460 4461class TestHelpMetavarTypeFormatter(HelpTestCase): 4462 4463 def custom_type(string): 4464 return string 4465 4466 parser_signature = Sig(prog='PROG', description='description', 4467 formatter_class=argparse.MetavarTypeHelpFormatter) 4468 argument_signatures = [Sig('a', type=int), 4469 Sig('-b', type=custom_type), 4470 Sig('-c', type=float, metavar='SOME FLOAT')] 4471 argument_group_signatures = [] 4472 usage = '''\ 4473 usage: PROG [-h] [-b custom_type] [-c SOME FLOAT] int 4474 ''' 4475 help = usage + '''\ 4476 4477 description 4478 4479 positional arguments: 4480 int 4481 4482 options: 4483 -h, --help show this help message and exit 4484 -b custom_type 4485 -c SOME FLOAT 4486 ''' 4487 version = '' 4488 4489 4490# ===================================== 4491# Optional/Positional constructor tests 4492# ===================================== 4493 4494class TestInvalidArgumentConstructors(TestCase): 4495 """Test a bunch of invalid Argument constructors""" 4496 4497 def assertTypeError(self, *args, **kwargs): 4498 parser = argparse.ArgumentParser() 4499 self.assertRaises(TypeError, parser.add_argument, 4500 *args, **kwargs) 4501 4502 def assertValueError(self, *args, **kwargs): 4503 parser = argparse.ArgumentParser() 4504 self.assertRaises(ValueError, parser.add_argument, 4505 *args, **kwargs) 4506 4507 def test_invalid_keyword_arguments(self): 4508 self.assertTypeError('-x', bar=None) 4509 self.assertTypeError('-y', callback='foo') 4510 self.assertTypeError('-y', callback_args=()) 4511 self.assertTypeError('-y', callback_kwargs={}) 4512 4513 def test_missing_destination(self): 4514 self.assertTypeError() 4515 for action in ['append', 'store']: 4516 self.assertTypeError(action=action) 4517 4518 def test_invalid_option_strings(self): 4519 self.assertValueError('--') 4520 self.assertValueError('---') 4521 4522 def test_invalid_type(self): 4523 self.assertValueError('--foo', type='int') 4524 self.assertValueError('--foo', type=(int, float)) 4525 4526 def test_invalid_action(self): 4527 self.assertValueError('-x', action='foo') 4528 self.assertValueError('foo', action='baz') 4529 self.assertValueError('--foo', action=('store', 'append')) 4530 parser = argparse.ArgumentParser() 4531 with self.assertRaises(ValueError) as cm: 4532 parser.add_argument("--foo", action="store-true") 4533 self.assertIn('unknown action', str(cm.exception)) 4534 4535 def test_multiple_dest(self): 4536 parser = argparse.ArgumentParser() 4537 parser.add_argument(dest='foo') 4538 with self.assertRaises(ValueError) as cm: 4539 parser.add_argument('bar', dest='baz') 4540 self.assertIn('dest supplied twice for positional argument', 4541 str(cm.exception)) 4542 4543 def test_no_argument_actions(self): 4544 for action in ['store_const', 'store_true', 'store_false', 4545 'append_const', 'count']: 4546 for attrs in [dict(type=int), dict(nargs='+'), 4547 dict(choices='ab')]: 4548 self.assertTypeError('-x', action=action, **attrs) 4549 4550 def test_no_argument_no_const_actions(self): 4551 # options with zero arguments 4552 for action in ['store_true', 'store_false', 'count']: 4553 4554 # const is always disallowed 4555 self.assertTypeError('-x', const='foo', action=action) 4556 4557 # nargs is always disallowed 4558 self.assertTypeError('-x', nargs='*', action=action) 4559 4560 def test_more_than_one_argument_actions(self): 4561 for action in ['store', 'append']: 4562 4563 # nargs=0 is disallowed 4564 self.assertValueError('-x', nargs=0, action=action) 4565 self.assertValueError('spam', nargs=0, action=action) 4566 4567 # const is disallowed with non-optional arguments 4568 for nargs in [1, '*', '+']: 4569 self.assertValueError('-x', const='foo', 4570 nargs=nargs, action=action) 4571 self.assertValueError('spam', const='foo', 4572 nargs=nargs, action=action) 4573 4574 def test_required_const_actions(self): 4575 for action in ['store_const', 'append_const']: 4576 4577 # nargs is always disallowed 4578 self.assertTypeError('-x', nargs='+', action=action) 4579 4580 def test_parsers_action_missing_params(self): 4581 self.assertTypeError('command', action='parsers') 4582 self.assertTypeError('command', action='parsers', prog='PROG') 4583 self.assertTypeError('command', action='parsers', 4584 parser_class=argparse.ArgumentParser) 4585 4586 def test_required_positional(self): 4587 self.assertTypeError('foo', required=True) 4588 4589 def test_user_defined_action(self): 4590 4591 class Success(Exception): 4592 pass 4593 4594 class Action(object): 4595 4596 def __init__(self, 4597 option_strings, 4598 dest, 4599 const, 4600 default, 4601 required=False): 4602 if dest == 'spam': 4603 if const is Success: 4604 if default is Success: 4605 raise Success() 4606 4607 def __call__(self, *args, **kwargs): 4608 pass 4609 4610 parser = argparse.ArgumentParser() 4611 self.assertRaises(Success, parser.add_argument, '--spam', 4612 action=Action, default=Success, const=Success) 4613 self.assertRaises(Success, parser.add_argument, 'spam', 4614 action=Action, default=Success, const=Success) 4615 4616# ================================ 4617# Actions returned by add_argument 4618# ================================ 4619 4620class TestActionsReturned(TestCase): 4621 4622 def test_dest(self): 4623 parser = argparse.ArgumentParser() 4624 action = parser.add_argument('--foo') 4625 self.assertEqual(action.dest, 'foo') 4626 action = parser.add_argument('-b', '--bar') 4627 self.assertEqual(action.dest, 'bar') 4628 action = parser.add_argument('-x', '-y') 4629 self.assertEqual(action.dest, 'x') 4630 4631 def test_misc(self): 4632 parser = argparse.ArgumentParser() 4633 action = parser.add_argument('--foo', nargs='?', const=42, 4634 default=84, type=int, choices=[1, 2], 4635 help='FOO', metavar='BAR', dest='baz') 4636 self.assertEqual(action.nargs, '?') 4637 self.assertEqual(action.const, 42) 4638 self.assertEqual(action.default, 84) 4639 self.assertEqual(action.type, int) 4640 self.assertEqual(action.choices, [1, 2]) 4641 self.assertEqual(action.help, 'FOO') 4642 self.assertEqual(action.metavar, 'BAR') 4643 self.assertEqual(action.dest, 'baz') 4644 4645 4646# ================================ 4647# Argument conflict handling tests 4648# ================================ 4649 4650class TestConflictHandling(TestCase): 4651 4652 def test_bad_type(self): 4653 self.assertRaises(ValueError, argparse.ArgumentParser, 4654 conflict_handler='foo') 4655 4656 def test_conflict_error(self): 4657 parser = argparse.ArgumentParser() 4658 parser.add_argument('-x') 4659 self.assertRaises(argparse.ArgumentError, 4660 parser.add_argument, '-x') 4661 parser.add_argument('--spam') 4662 self.assertRaises(argparse.ArgumentError, 4663 parser.add_argument, '--spam') 4664 4665 def test_resolve_error(self): 4666 get_parser = argparse.ArgumentParser 4667 parser = get_parser(prog='PROG', conflict_handler='resolve') 4668 4669 parser.add_argument('-x', help='OLD X') 4670 parser.add_argument('-x', help='NEW X') 4671 self.assertEqual(parser.format_help(), textwrap.dedent('''\ 4672 usage: PROG [-h] [-x X] 4673 4674 options: 4675 -h, --help show this help message and exit 4676 -x X NEW X 4677 ''')) 4678 4679 parser.add_argument('--spam', metavar='OLD_SPAM') 4680 parser.add_argument('--spam', metavar='NEW_SPAM') 4681 self.assertEqual(parser.format_help(), textwrap.dedent('''\ 4682 usage: PROG [-h] [-x X] [--spam NEW_SPAM] 4683 4684 options: 4685 -h, --help show this help message and exit 4686 -x X NEW X 4687 --spam NEW_SPAM 4688 ''')) 4689 4690 4691# ============================= 4692# Help and Version option tests 4693# ============================= 4694 4695class TestOptionalsHelpVersionActions(TestCase): 4696 """Test the help and version actions""" 4697 4698 def assertPrintHelpExit(self, parser, args_str): 4699 with self.assertRaises(ArgumentParserError) as cm: 4700 parser.parse_args(args_str.split()) 4701 self.assertEqual(parser.format_help(), cm.exception.stdout) 4702 4703 def assertArgumentParserError(self, parser, *args): 4704 self.assertRaises(ArgumentParserError, parser.parse_args, args) 4705 4706 def test_version(self): 4707 parser = ErrorRaisingArgumentParser() 4708 parser.add_argument('-v', '--version', action='version', version='1.0') 4709 self.assertPrintHelpExit(parser, '-h') 4710 self.assertPrintHelpExit(parser, '--help') 4711 self.assertRaises(AttributeError, getattr, parser, 'format_version') 4712 4713 def test_version_format(self): 4714 parser = ErrorRaisingArgumentParser(prog='PPP') 4715 parser.add_argument('-v', '--version', action='version', version='%(prog)s 3.5') 4716 with self.assertRaises(ArgumentParserError) as cm: 4717 parser.parse_args(['-v']) 4718 self.assertEqual('PPP 3.5\n', cm.exception.stdout) 4719 4720 def test_version_no_help(self): 4721 parser = ErrorRaisingArgumentParser(add_help=False) 4722 parser.add_argument('-v', '--version', action='version', version='1.0') 4723 self.assertArgumentParserError(parser, '-h') 4724 self.assertArgumentParserError(parser, '--help') 4725 self.assertRaises(AttributeError, getattr, parser, 'format_version') 4726 4727 def test_version_action(self): 4728 parser = ErrorRaisingArgumentParser(prog='XXX') 4729 parser.add_argument('-V', action='version', version='%(prog)s 3.7') 4730 with self.assertRaises(ArgumentParserError) as cm: 4731 parser.parse_args(['-V']) 4732 self.assertEqual('XXX 3.7\n', cm.exception.stdout) 4733 4734 def test_no_help(self): 4735 parser = ErrorRaisingArgumentParser(add_help=False) 4736 self.assertArgumentParserError(parser, '-h') 4737 self.assertArgumentParserError(parser, '--help') 4738 self.assertArgumentParserError(parser, '-v') 4739 self.assertArgumentParserError(parser, '--version') 4740 4741 def test_alternate_help_version(self): 4742 parser = ErrorRaisingArgumentParser() 4743 parser.add_argument('-x', action='help') 4744 parser.add_argument('-y', action='version') 4745 self.assertPrintHelpExit(parser, '-x') 4746 self.assertArgumentParserError(parser, '-v') 4747 self.assertArgumentParserError(parser, '--version') 4748 self.assertRaises(AttributeError, getattr, parser, 'format_version') 4749 4750 def test_help_version_extra_arguments(self): 4751 parser = ErrorRaisingArgumentParser() 4752 parser.add_argument('--version', action='version', version='1.0') 4753 parser.add_argument('-x', action='store_true') 4754 parser.add_argument('y') 4755 4756 # try all combinations of valid prefixes and suffixes 4757 valid_prefixes = ['', '-x', 'foo', '-x bar', 'baz -x'] 4758 valid_suffixes = valid_prefixes + ['--bad-option', 'foo bar baz'] 4759 for prefix in valid_prefixes: 4760 for suffix in valid_suffixes: 4761 format = '%s %%s %s' % (prefix, suffix) 4762 self.assertPrintHelpExit(parser, format % '-h') 4763 self.assertPrintHelpExit(parser, format % '--help') 4764 self.assertRaises(AttributeError, getattr, parser, 'format_version') 4765 4766 4767# ====================== 4768# str() and repr() tests 4769# ====================== 4770 4771class TestStrings(TestCase): 4772 """Test str() and repr() on Optionals and Positionals""" 4773 4774 def assertStringEqual(self, obj, result_string): 4775 for func in [str, repr]: 4776 self.assertEqual(func(obj), result_string) 4777 4778 def test_optional(self): 4779 option = argparse.Action( 4780 option_strings=['--foo', '-a', '-b'], 4781 dest='b', 4782 type='int', 4783 nargs='+', 4784 default=42, 4785 choices=[1, 2, 3], 4786 help='HELP', 4787 metavar='METAVAR') 4788 string = ( 4789 "Action(option_strings=['--foo', '-a', '-b'], dest='b', " 4790 "nargs='+', const=None, default=42, type='int', " 4791 "choices=[1, 2, 3], help='HELP', metavar='METAVAR')") 4792 self.assertStringEqual(option, string) 4793 4794 def test_argument(self): 4795 argument = argparse.Action( 4796 option_strings=[], 4797 dest='x', 4798 type=float, 4799 nargs='?', 4800 default=2.5, 4801 choices=[0.5, 1.5, 2.5], 4802 help='H HH H', 4803 metavar='MV MV MV') 4804 string = ( 4805 "Action(option_strings=[], dest='x', nargs='?', " 4806 "const=None, default=2.5, type=%r, choices=[0.5, 1.5, 2.5], " 4807 "help='H HH H', metavar='MV MV MV')" % float) 4808 self.assertStringEqual(argument, string) 4809 4810 def test_namespace(self): 4811 ns = argparse.Namespace(foo=42, bar='spam') 4812 string = "Namespace(foo=42, bar='spam')" 4813 self.assertStringEqual(ns, string) 4814 4815 def test_namespace_starkwargs_notidentifier(self): 4816 ns = argparse.Namespace(**{'"': 'quote'}) 4817 string = """Namespace(**{'"': 'quote'})""" 4818 self.assertStringEqual(ns, string) 4819 4820 def test_namespace_kwargs_and_starkwargs_notidentifier(self): 4821 ns = argparse.Namespace(a=1, **{'"': 'quote'}) 4822 string = """Namespace(a=1, **{'"': 'quote'})""" 4823 self.assertStringEqual(ns, string) 4824 4825 def test_namespace_starkwargs_identifier(self): 4826 ns = argparse.Namespace(**{'valid': True}) 4827 string = "Namespace(valid=True)" 4828 self.assertStringEqual(ns, string) 4829 4830 def test_parser(self): 4831 parser = argparse.ArgumentParser(prog='PROG') 4832 string = ( 4833 "ArgumentParser(prog='PROG', usage=None, description=None, " 4834 "formatter_class=%r, conflict_handler='error', " 4835 "add_help=True)" % argparse.HelpFormatter) 4836 self.assertStringEqual(parser, string) 4837 4838# =============== 4839# Namespace tests 4840# =============== 4841 4842class TestNamespace(TestCase): 4843 4844 def test_constructor(self): 4845 ns = argparse.Namespace() 4846 self.assertRaises(AttributeError, getattr, ns, 'x') 4847 4848 ns = argparse.Namespace(a=42, b='spam') 4849 self.assertEqual(ns.a, 42) 4850 self.assertEqual(ns.b, 'spam') 4851 4852 def test_equality(self): 4853 ns1 = argparse.Namespace(a=1, b=2) 4854 ns2 = argparse.Namespace(b=2, a=1) 4855 ns3 = argparse.Namespace(a=1) 4856 ns4 = argparse.Namespace(b=2) 4857 4858 self.assertEqual(ns1, ns2) 4859 self.assertNotEqual(ns1, ns3) 4860 self.assertNotEqual(ns1, ns4) 4861 self.assertNotEqual(ns2, ns3) 4862 self.assertNotEqual(ns2, ns4) 4863 self.assertTrue(ns1 != ns3) 4864 self.assertTrue(ns1 != ns4) 4865 self.assertTrue(ns2 != ns3) 4866 self.assertTrue(ns2 != ns4) 4867 4868 def test_equality_returns_notimplemented(self): 4869 # See issue 21481 4870 ns = argparse.Namespace(a=1, b=2) 4871 self.assertIs(ns.__eq__(None), NotImplemented) 4872 self.assertIs(ns.__ne__(None), NotImplemented) 4873 4874 4875# =================== 4876# File encoding tests 4877# =================== 4878 4879class TestEncoding(TestCase): 4880 4881 def _test_module_encoding(self, path): 4882 path, _ = os.path.splitext(path) 4883 path += ".py" 4884 with open(path, 'r', encoding='utf-8') as f: 4885 f.read() 4886 4887 def test_argparse_module_encoding(self): 4888 self._test_module_encoding(argparse.__file__) 4889 4890 def test_test_argparse_module_encoding(self): 4891 self._test_module_encoding(__file__) 4892 4893# =================== 4894# ArgumentError tests 4895# =================== 4896 4897class TestArgumentError(TestCase): 4898 4899 def test_argument_error(self): 4900 msg = "my error here" 4901 error = argparse.ArgumentError(None, msg) 4902 self.assertEqual(str(error), msg) 4903 4904# ======================= 4905# ArgumentTypeError tests 4906# ======================= 4907 4908class TestArgumentTypeError(TestCase): 4909 4910 def test_argument_type_error(self): 4911 4912 def spam(string): 4913 raise argparse.ArgumentTypeError('spam!') 4914 4915 parser = ErrorRaisingArgumentParser(prog='PROG', add_help=False) 4916 parser.add_argument('x', type=spam) 4917 with self.assertRaises(ArgumentParserError) as cm: 4918 parser.parse_args(['XXX']) 4919 self.assertEqual('usage: PROG x\nPROG: error: argument x: spam!\n', 4920 cm.exception.stderr) 4921 4922# ========================= 4923# MessageContentError tests 4924# ========================= 4925 4926class TestMessageContentError(TestCase): 4927 4928 def test_missing_argument_name_in_message(self): 4929 parser = ErrorRaisingArgumentParser(prog='PROG', usage='') 4930 parser.add_argument('req_pos', type=str) 4931 parser.add_argument('-req_opt', type=int, required=True) 4932 parser.add_argument('need_one', type=str, nargs='+') 4933 4934 with self.assertRaises(ArgumentParserError) as cm: 4935 parser.parse_args([]) 4936 msg = str(cm.exception) 4937 self.assertRegex(msg, 'req_pos') 4938 self.assertRegex(msg, 'req_opt') 4939 self.assertRegex(msg, 'need_one') 4940 with self.assertRaises(ArgumentParserError) as cm: 4941 parser.parse_args(['myXargument']) 4942 msg = str(cm.exception) 4943 self.assertNotIn(msg, 'req_pos') 4944 self.assertRegex(msg, 'req_opt') 4945 self.assertRegex(msg, 'need_one') 4946 with self.assertRaises(ArgumentParserError) as cm: 4947 parser.parse_args(['myXargument', '-req_opt=1']) 4948 msg = str(cm.exception) 4949 self.assertNotIn(msg, 'req_pos') 4950 self.assertNotIn(msg, 'req_opt') 4951 self.assertRegex(msg, 'need_one') 4952 4953 def test_optional_optional_not_in_message(self): 4954 parser = ErrorRaisingArgumentParser(prog='PROG', usage='') 4955 parser.add_argument('req_pos', type=str) 4956 parser.add_argument('--req_opt', type=int, required=True) 4957 parser.add_argument('--opt_opt', type=bool, nargs='?', 4958 default=True) 4959 with self.assertRaises(ArgumentParserError) as cm: 4960 parser.parse_args([]) 4961 msg = str(cm.exception) 4962 self.assertRegex(msg, 'req_pos') 4963 self.assertRegex(msg, 'req_opt') 4964 self.assertNotIn(msg, 'opt_opt') 4965 with self.assertRaises(ArgumentParserError) as cm: 4966 parser.parse_args(['--req_opt=1']) 4967 msg = str(cm.exception) 4968 self.assertRegex(msg, 'req_pos') 4969 self.assertNotIn(msg, 'req_opt') 4970 self.assertNotIn(msg, 'opt_opt') 4971 4972 def test_optional_positional_not_in_message(self): 4973 parser = ErrorRaisingArgumentParser(prog='PROG', usage='') 4974 parser.add_argument('req_pos') 4975 parser.add_argument('optional_positional', nargs='?', default='eggs') 4976 with self.assertRaises(ArgumentParserError) as cm: 4977 parser.parse_args([]) 4978 msg = str(cm.exception) 4979 self.assertRegex(msg, 'req_pos') 4980 self.assertNotIn(msg, 'optional_positional') 4981 4982 4983# ================================================ 4984# Check that the type function is called only once 4985# ================================================ 4986 4987class TestTypeFunctionCallOnlyOnce(TestCase): 4988 4989 def test_type_function_call_only_once(self): 4990 def spam(string_to_convert): 4991 self.assertEqual(string_to_convert, 'spam!') 4992 return 'foo_converted' 4993 4994 parser = argparse.ArgumentParser() 4995 parser.add_argument('--foo', type=spam, default='bar') 4996 args = parser.parse_args('--foo spam!'.split()) 4997 self.assertEqual(NS(foo='foo_converted'), args) 4998 4999# ================================================================== 5000# Check semantics regarding the default argument and type conversion 5001# ================================================================== 5002 5003class TestTypeFunctionCalledOnDefault(TestCase): 5004 5005 def test_type_function_call_with_non_string_default(self): 5006 def spam(int_to_convert): 5007 self.assertEqual(int_to_convert, 0) 5008 return 'foo_converted' 5009 5010 parser = argparse.ArgumentParser() 5011 parser.add_argument('--foo', type=spam, default=0) 5012 args = parser.parse_args([]) 5013 # foo should *not* be converted because its default is not a string. 5014 self.assertEqual(NS(foo=0), args) 5015 5016 def test_type_function_call_with_string_default(self): 5017 def spam(int_to_convert): 5018 return 'foo_converted' 5019 5020 parser = argparse.ArgumentParser() 5021 parser.add_argument('--foo', type=spam, default='0') 5022 args = parser.parse_args([]) 5023 # foo is converted because its default is a string. 5024 self.assertEqual(NS(foo='foo_converted'), args) 5025 5026 def test_no_double_type_conversion_of_default(self): 5027 def extend(str_to_convert): 5028 return str_to_convert + '*' 5029 5030 parser = argparse.ArgumentParser() 5031 parser.add_argument('--test', type=extend, default='*') 5032 args = parser.parse_args([]) 5033 # The test argument will be two stars, one coming from the default 5034 # value and one coming from the type conversion being called exactly 5035 # once. 5036 self.assertEqual(NS(test='**'), args) 5037 5038 def test_issue_15906(self): 5039 # Issue #15906: When action='append', type=str, default=[] are 5040 # providing, the dest value was the string representation "[]" when it 5041 # should have been an empty list. 5042 parser = argparse.ArgumentParser() 5043 parser.add_argument('--test', dest='test', type=str, 5044 default=[], action='append') 5045 args = parser.parse_args([]) 5046 self.assertEqual(args.test, []) 5047 5048# ====================== 5049# parse_known_args tests 5050# ====================== 5051 5052class TestParseKnownArgs(TestCase): 5053 5054 def test_arguments_tuple(self): 5055 parser = argparse.ArgumentParser() 5056 parser.parse_args(()) 5057 5058 def test_arguments_list(self): 5059 parser = argparse.ArgumentParser() 5060 parser.parse_args([]) 5061 5062 def test_arguments_tuple_positional(self): 5063 parser = argparse.ArgumentParser() 5064 parser.add_argument('x') 5065 parser.parse_args(('x',)) 5066 5067 def test_arguments_list_positional(self): 5068 parser = argparse.ArgumentParser() 5069 parser.add_argument('x') 5070 parser.parse_args(['x']) 5071 5072 def test_optionals(self): 5073 parser = argparse.ArgumentParser() 5074 parser.add_argument('--foo') 5075 args, extras = parser.parse_known_args('--foo F --bar --baz'.split()) 5076 self.assertEqual(NS(foo='F'), args) 5077 self.assertEqual(['--bar', '--baz'], extras) 5078 5079 def test_mixed(self): 5080 parser = argparse.ArgumentParser() 5081 parser.add_argument('-v', nargs='?', const=1, type=int) 5082 parser.add_argument('--spam', action='store_false') 5083 parser.add_argument('badger') 5084 5085 argv = ["B", "C", "--foo", "-v", "3", "4"] 5086 args, extras = parser.parse_known_args(argv) 5087 self.assertEqual(NS(v=3, spam=True, badger="B"), args) 5088 self.assertEqual(["C", "--foo", "4"], extras) 5089 5090# =========================== 5091# parse_intermixed_args tests 5092# =========================== 5093 5094class TestIntermixedArgs(TestCase): 5095 def test_basic(self): 5096 # test parsing intermixed optionals and positionals 5097 parser = argparse.ArgumentParser(prog='PROG') 5098 parser.add_argument('--foo', dest='foo') 5099 bar = parser.add_argument('--bar', dest='bar', required=True) 5100 parser.add_argument('cmd') 5101 parser.add_argument('rest', nargs='*', type=int) 5102 argv = 'cmd --foo x 1 --bar y 2 3'.split() 5103 args = parser.parse_intermixed_args(argv) 5104 # rest gets [1,2,3] despite the foo and bar strings 5105 self.assertEqual(NS(bar='y', cmd='cmd', foo='x', rest=[1, 2, 3]), args) 5106 5107 args, extras = parser.parse_known_args(argv) 5108 # cannot parse the '1,2,3' 5109 self.assertEqual(NS(bar='y', cmd='cmd', foo='x', rest=[]), args) 5110 self.assertEqual(["1", "2", "3"], extras) 5111 5112 argv = 'cmd --foo x 1 --error 2 --bar y 3'.split() 5113 args, extras = parser.parse_known_intermixed_args(argv) 5114 # unknown optionals go into extras 5115 self.assertEqual(NS(bar='y', cmd='cmd', foo='x', rest=[1]), args) 5116 self.assertEqual(['--error', '2', '3'], extras) 5117 5118 # restores attributes that were temporarily changed 5119 self.assertIsNone(parser.usage) 5120 self.assertEqual(bar.required, True) 5121 5122 def test_remainder(self): 5123 # Intermixed and remainder are incompatible 5124 parser = ErrorRaisingArgumentParser(prog='PROG') 5125 parser.add_argument('-z') 5126 parser.add_argument('x') 5127 parser.add_argument('y', nargs='...') 5128 argv = 'X A B -z Z'.split() 5129 # intermixed fails with '...' (also 'A...') 5130 # self.assertRaises(TypeError, parser.parse_intermixed_args, argv) 5131 with self.assertRaises(TypeError) as cm: 5132 parser.parse_intermixed_args(argv) 5133 self.assertRegex(str(cm.exception), r'\.\.\.') 5134 5135 def test_exclusive(self): 5136 # mutually exclusive group; intermixed works fine 5137 parser = ErrorRaisingArgumentParser(prog='PROG') 5138 group = parser.add_mutually_exclusive_group(required=True) 5139 group.add_argument('--foo', action='store_true', help='FOO') 5140 group.add_argument('--spam', help='SPAM') 5141 parser.add_argument('badger', nargs='*', default='X', help='BADGER') 5142 args = parser.parse_intermixed_args('1 --foo 2'.split()) 5143 self.assertEqual(NS(badger=['1', '2'], foo=True, spam=None), args) 5144 self.assertRaises(ArgumentParserError, parser.parse_intermixed_args, '1 2'.split()) 5145 self.assertEqual(group.required, True) 5146 5147 def test_exclusive_incompatible(self): 5148 # mutually exclusive group including positional - fail 5149 parser = ErrorRaisingArgumentParser(prog='PROG') 5150 group = parser.add_mutually_exclusive_group(required=True) 5151 group.add_argument('--foo', action='store_true', help='FOO') 5152 group.add_argument('--spam', help='SPAM') 5153 group.add_argument('badger', nargs='*', default='X', help='BADGER') 5154 self.assertRaises(TypeError, parser.parse_intermixed_args, []) 5155 self.assertEqual(group.required, True) 5156 5157class TestIntermixedMessageContentError(TestCase): 5158 # case where Intermixed gives different error message 5159 # error is raised by 1st parsing step 5160 def test_missing_argument_name_in_message(self): 5161 parser = ErrorRaisingArgumentParser(prog='PROG', usage='') 5162 parser.add_argument('req_pos', type=str) 5163 parser.add_argument('-req_opt', type=int, required=True) 5164 5165 with self.assertRaises(ArgumentParserError) as cm: 5166 parser.parse_args([]) 5167 msg = str(cm.exception) 5168 self.assertRegex(msg, 'req_pos') 5169 self.assertRegex(msg, 'req_opt') 5170 5171 with self.assertRaises(ArgumentParserError) as cm: 5172 parser.parse_intermixed_args([]) 5173 msg = str(cm.exception) 5174 self.assertNotRegex(msg, 'req_pos') 5175 self.assertRegex(msg, 'req_opt') 5176 5177# ========================== 5178# add_argument metavar tests 5179# ========================== 5180 5181class TestAddArgumentMetavar(TestCase): 5182 5183 EXPECTED_MESSAGE = "length of metavar tuple does not match nargs" 5184 5185 def do_test_no_exception(self, nargs, metavar): 5186 parser = argparse.ArgumentParser() 5187 parser.add_argument("--foo", nargs=nargs, metavar=metavar) 5188 5189 def do_test_exception(self, nargs, metavar): 5190 parser = argparse.ArgumentParser() 5191 with self.assertRaises(ValueError) as cm: 5192 parser.add_argument("--foo", nargs=nargs, metavar=metavar) 5193 self.assertEqual(cm.exception.args[0], self.EXPECTED_MESSAGE) 5194 5195 # Unit tests for different values of metavar when nargs=None 5196 5197 def test_nargs_None_metavar_string(self): 5198 self.do_test_no_exception(nargs=None, metavar="1") 5199 5200 def test_nargs_None_metavar_length0(self): 5201 self.do_test_exception(nargs=None, metavar=tuple()) 5202 5203 def test_nargs_None_metavar_length1(self): 5204 self.do_test_no_exception(nargs=None, metavar=("1",)) 5205 5206 def test_nargs_None_metavar_length2(self): 5207 self.do_test_exception(nargs=None, metavar=("1", "2")) 5208 5209 def test_nargs_None_metavar_length3(self): 5210 self.do_test_exception(nargs=None, metavar=("1", "2", "3")) 5211 5212 # Unit tests for different values of metavar when nargs=? 5213 5214 def test_nargs_optional_metavar_string(self): 5215 self.do_test_no_exception(nargs="?", metavar="1") 5216 5217 def test_nargs_optional_metavar_length0(self): 5218 self.do_test_exception(nargs="?", metavar=tuple()) 5219 5220 def test_nargs_optional_metavar_length1(self): 5221 self.do_test_no_exception(nargs="?", metavar=("1",)) 5222 5223 def test_nargs_optional_metavar_length2(self): 5224 self.do_test_exception(nargs="?", metavar=("1", "2")) 5225 5226 def test_nargs_optional_metavar_length3(self): 5227 self.do_test_exception(nargs="?", metavar=("1", "2", "3")) 5228 5229 # Unit tests for different values of metavar when nargs=* 5230 5231 def test_nargs_zeroormore_metavar_string(self): 5232 self.do_test_no_exception(nargs="*", metavar="1") 5233 5234 def test_nargs_zeroormore_metavar_length0(self): 5235 self.do_test_exception(nargs="*", metavar=tuple()) 5236 5237 def test_nargs_zeroormore_metavar_length1(self): 5238 self.do_test_no_exception(nargs="*", metavar=("1",)) 5239 5240 def test_nargs_zeroormore_metavar_length2(self): 5241 self.do_test_no_exception(nargs="*", metavar=("1", "2")) 5242 5243 def test_nargs_zeroormore_metavar_length3(self): 5244 self.do_test_exception(nargs="*", metavar=("1", "2", "3")) 5245 5246 # Unit tests for different values of metavar when nargs=+ 5247 5248 def test_nargs_oneormore_metavar_string(self): 5249 self.do_test_no_exception(nargs="+", metavar="1") 5250 5251 def test_nargs_oneormore_metavar_length0(self): 5252 self.do_test_exception(nargs="+", metavar=tuple()) 5253 5254 def test_nargs_oneormore_metavar_length1(self): 5255 self.do_test_exception(nargs="+", metavar=("1",)) 5256 5257 def test_nargs_oneormore_metavar_length2(self): 5258 self.do_test_no_exception(nargs="+", metavar=("1", "2")) 5259 5260 def test_nargs_oneormore_metavar_length3(self): 5261 self.do_test_exception(nargs="+", metavar=("1", "2", "3")) 5262 5263 # Unit tests for different values of metavar when nargs=... 5264 5265 def test_nargs_remainder_metavar_string(self): 5266 self.do_test_no_exception(nargs="...", metavar="1") 5267 5268 def test_nargs_remainder_metavar_length0(self): 5269 self.do_test_no_exception(nargs="...", metavar=tuple()) 5270 5271 def test_nargs_remainder_metavar_length1(self): 5272 self.do_test_no_exception(nargs="...", metavar=("1",)) 5273 5274 def test_nargs_remainder_metavar_length2(self): 5275 self.do_test_no_exception(nargs="...", metavar=("1", "2")) 5276 5277 def test_nargs_remainder_metavar_length3(self): 5278 self.do_test_no_exception(nargs="...", metavar=("1", "2", "3")) 5279 5280 # Unit tests for different values of metavar when nargs=A... 5281 5282 def test_nargs_parser_metavar_string(self): 5283 self.do_test_no_exception(nargs="A...", metavar="1") 5284 5285 def test_nargs_parser_metavar_length0(self): 5286 self.do_test_exception(nargs="A...", metavar=tuple()) 5287 5288 def test_nargs_parser_metavar_length1(self): 5289 self.do_test_no_exception(nargs="A...", metavar=("1",)) 5290 5291 def test_nargs_parser_metavar_length2(self): 5292 self.do_test_exception(nargs="A...", metavar=("1", "2")) 5293 5294 def test_nargs_parser_metavar_length3(self): 5295 self.do_test_exception(nargs="A...", metavar=("1", "2", "3")) 5296 5297 # Unit tests for different values of metavar when nargs=1 5298 5299 def test_nargs_1_metavar_string(self): 5300 self.do_test_no_exception(nargs=1, metavar="1") 5301 5302 def test_nargs_1_metavar_length0(self): 5303 self.do_test_exception(nargs=1, metavar=tuple()) 5304 5305 def test_nargs_1_metavar_length1(self): 5306 self.do_test_no_exception(nargs=1, metavar=("1",)) 5307 5308 def test_nargs_1_metavar_length2(self): 5309 self.do_test_exception(nargs=1, metavar=("1", "2")) 5310 5311 def test_nargs_1_metavar_length3(self): 5312 self.do_test_exception(nargs=1, metavar=("1", "2", "3")) 5313 5314 # Unit tests for different values of metavar when nargs=2 5315 5316 def test_nargs_2_metavar_string(self): 5317 self.do_test_no_exception(nargs=2, metavar="1") 5318 5319 def test_nargs_2_metavar_length0(self): 5320 self.do_test_exception(nargs=2, metavar=tuple()) 5321 5322 def test_nargs_2_metavar_length1(self): 5323 self.do_test_exception(nargs=2, metavar=("1",)) 5324 5325 def test_nargs_2_metavar_length2(self): 5326 self.do_test_no_exception(nargs=2, metavar=("1", "2")) 5327 5328 def test_nargs_2_metavar_length3(self): 5329 self.do_test_exception(nargs=2, metavar=("1", "2", "3")) 5330 5331 # Unit tests for different values of metavar when nargs=3 5332 5333 def test_nargs_3_metavar_string(self): 5334 self.do_test_no_exception(nargs=3, metavar="1") 5335 5336 def test_nargs_3_metavar_length0(self): 5337 self.do_test_exception(nargs=3, metavar=tuple()) 5338 5339 def test_nargs_3_metavar_length1(self): 5340 self.do_test_exception(nargs=3, metavar=("1",)) 5341 5342 def test_nargs_3_metavar_length2(self): 5343 self.do_test_exception(nargs=3, metavar=("1", "2")) 5344 5345 def test_nargs_3_metavar_length3(self): 5346 self.do_test_no_exception(nargs=3, metavar=("1", "2", "3")) 5347 5348 5349class TestInvalidNargs(TestCase): 5350 5351 EXPECTED_INVALID_MESSAGE = "invalid nargs value" 5352 EXPECTED_RANGE_MESSAGE = ("nargs for store actions must be != 0; if you " 5353 "have nothing to store, actions such as store " 5354 "true or store const may be more appropriate") 5355 5356 def do_test_range_exception(self, nargs): 5357 parser = argparse.ArgumentParser() 5358 with self.assertRaises(ValueError) as cm: 5359 parser.add_argument("--foo", nargs=nargs) 5360 self.assertEqual(cm.exception.args[0], self.EXPECTED_RANGE_MESSAGE) 5361 5362 def do_test_invalid_exception(self, nargs): 5363 parser = argparse.ArgumentParser() 5364 with self.assertRaises(ValueError) as cm: 5365 parser.add_argument("--foo", nargs=nargs) 5366 self.assertEqual(cm.exception.args[0], self.EXPECTED_INVALID_MESSAGE) 5367 5368 # Unit tests for different values of nargs 5369 5370 def test_nargs_alphabetic(self): 5371 self.do_test_invalid_exception(nargs='a') 5372 self.do_test_invalid_exception(nargs="abcd") 5373 5374 def test_nargs_zero(self): 5375 self.do_test_range_exception(nargs=0) 5376 5377# ============================ 5378# from argparse import * tests 5379# ============================ 5380 5381class TestImportStar(TestCase): 5382 5383 def test(self): 5384 for name in argparse.__all__: 5385 self.assertTrue(hasattr(argparse, name)) 5386 5387 def test_all_exports_everything_but_modules(self): 5388 items = [ 5389 name 5390 for name, value in vars(argparse).items() 5391 if not (name.startswith("_") or name == 'ngettext') 5392 if not inspect.ismodule(value) 5393 ] 5394 self.assertEqual(sorted(items), sorted(argparse.__all__)) 5395 5396 5397class TestWrappingMetavar(TestCase): 5398 5399 def setUp(self): 5400 super().setUp() 5401 self.parser = ErrorRaisingArgumentParser( 5402 'this_is_spammy_prog_with_a_long_name_sorry_about_the_name' 5403 ) 5404 # this metavar was triggering library assertion errors due to usage 5405 # message formatting incorrectly splitting on the ] chars within 5406 metavar = '<http[s]://example:1234>' 5407 self.parser.add_argument('--proxy', metavar=metavar) 5408 5409 def test_help_with_metavar(self): 5410 help_text = self.parser.format_help() 5411 self.assertEqual(help_text, textwrap.dedent('''\ 5412 usage: this_is_spammy_prog_with_a_long_name_sorry_about_the_name 5413 [-h] [--proxy <http[s]://example:1234>] 5414 5415 options: 5416 -h, --help show this help message and exit 5417 --proxy <http[s]://example:1234> 5418 ''')) 5419 5420 5421class TestExitOnError(TestCase): 5422 5423 def setUp(self): 5424 self.parser = argparse.ArgumentParser(exit_on_error=False) 5425 self.parser.add_argument('--integers', metavar='N', type=int) 5426 5427 def test_exit_on_error_with_good_args(self): 5428 ns = self.parser.parse_args('--integers 4'.split()) 5429 self.assertEqual(ns, argparse.Namespace(integers=4)) 5430 5431 def test_exit_on_error_with_bad_args(self): 5432 with self.assertRaises(argparse.ArgumentError): 5433 self.parser.parse_args('--integers a'.split()) 5434 5435 5436def tearDownModule(): 5437 # Remove global references to avoid looking like we have refleaks. 5438 RFile.seen = {} 5439 WFile.seen = set() 5440 5441 5442if __name__ == '__main__': 5443 unittest.main() 5444