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