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