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