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