1 # 2 # Test suite for Optik. Supplied by Johannes Gijsbers 3 # (taradino (at] softhome.net) -- translated from the original Optik 4 # test suite to this PyUnit-based version. 5 # 6 # $Id$ 7 # 8 9 import sys 10 import os 11 import re 12 import copy 13 import unittest 14 15 from io import StringIO 16 from test import support 17 18 19 import optparse 20 from optparse import make_option, Option, \ 21 TitledHelpFormatter, OptionParser, OptionGroup, \ 22 SUPPRESS_USAGE, OptionError, OptionConflictError, \ 23 BadOptionError, OptionValueError, Values 24 from optparse import _match_abbrev 25 from optparse import _parse_num 26 27 retype = type(re.compile('')) 28 29 class InterceptedError(Exception): 30 def __init__(self, 31 error_message=None, 32 exit_status=None, 33 exit_message=None): 34 self.error_message = error_message 35 self.exit_status = exit_status 36 self.exit_message = exit_message 37 38 def __str__(self): 39 return self.error_message or self.exit_message or "intercepted error" 40 41 class InterceptingOptionParser(OptionParser): 42 def exit(self, status=0, msg=None): 43 raise InterceptedError(exit_status=status, exit_message=msg) 44 45 def error(self, msg): 46 raise InterceptedError(error_message=msg) 47 48 49 class BaseTest(unittest.TestCase): 50 def assertParseOK(self, args, expected_opts, expected_positional_args): 51 """Assert the options are what we expected when parsing arguments. 52 53 Otherwise, fail with a nicely formatted message. 54 55 Keyword arguments: 56 args -- A list of arguments to parse with OptionParser. 57 expected_opts -- The options expected. 58 expected_positional_args -- The positional arguments expected. 59 60 Returns the options and positional args for further testing. 61 """ 62 63 (options, positional_args) = self.parser.parse_args(args) 64 optdict = vars(options) 65 66 self.assertEqual(optdict, expected_opts, 67 """ 68 Options are %(optdict)s. 69 Should be %(expected_opts)s. 70 Args were %(args)s.""" % locals()) 71 72 self.assertEqual(positional_args, expected_positional_args, 73 """ 74 Positional arguments are %(positional_args)s. 75 Should be %(expected_positional_args)s. 76 Args were %(args)s.""" % locals ()) 77 78 return (options, positional_args) 79 80 def assertRaises(self, 81 func, 82 args, 83 kwargs, 84 expected_exception, 85 expected_message): 86 """ 87 Assert that the expected exception is raised when calling a 88 function, and that the right error message is included with 89 that exception. 90 91 Arguments: 92 func -- the function to call 93 args -- positional arguments to `func` 94 kwargs -- keyword arguments to `func` 95 expected_exception -- exception that should be raised 96 expected_message -- expected exception message (or pattern 97 if a compiled regex object) 98 99 Returns the exception raised for further testing. 100 """ 101 if args is None: 102 args = () 103 if kwargs is None: 104 kwargs = {} 105 106 try: 107 func(*args, **kwargs) 108 except expected_exception as err: 109 actual_message = str(err) 110 if isinstance(expected_message, retype): 111 self.assertTrue(expected_message.search(actual_message), 112 """\ 113 expected exception message pattern: 114 /%s/ 115 actual exception message: 116 '''%s''' 117 """ % (expected_message.pattern, actual_message)) 118 else: 119 self.assertEqual(actual_message, 120 expected_message, 121 """\ 122 expected exception message: 123 '''%s''' 124 actual exception message: 125 '''%s''' 126 """ % (expected_message, actual_message)) 127 128 return err 129 else: 130 self.fail("""expected exception %(expected_exception)s not raised 131 called %(func)r 132 with args %(args)r 133 and kwargs %(kwargs)r 134 """ % locals ()) 135 136 137 # -- Assertions used in more than one class -------------------- 138 139 def assertParseFail(self, cmdline_args, expected_output): 140 """ 141 Assert the parser fails with the expected message. Caller 142 must ensure that self.parser is an InterceptingOptionParser. 143 """ 144 try: 145 self.parser.parse_args(cmdline_args) 146 except InterceptedError as err: 147 self.assertEqual(err.error_message, expected_output) 148 else: 149 self.assertFalse("expected parse failure") 150 151 def assertOutput(self, 152 cmdline_args, 153 expected_output, 154 expected_status=0, 155 expected_error=None): 156 """Assert the parser prints the expected output on stdout.""" 157 save_stdout = sys.stdout 158 try: 159 try: 160 sys.stdout = StringIO() 161 self.parser.parse_args(cmdline_args) 162 finally: 163 output = sys.stdout.getvalue() 164 sys.stdout = save_stdout 165 166 except InterceptedError as err: 167 self.assertTrue( 168 isinstance(output, str), 169 "expected output to be an ordinary string, not %r" 170 % type(output)) 171 172 if output != expected_output: 173 self.fail("expected: \n'''\n" + expected_output + 174 "'''\nbut got \n'''\n" + output + "'''") 175 self.assertEqual(err.exit_status, expected_status) 176 self.assertEqual(err.exit_message, expected_error) 177 else: 178 self.assertFalse("expected parser.exit()") 179 180 def assertTypeError(self, func, expected_message, *args): 181 """Assert that TypeError is raised when executing func.""" 182 self.assertRaises(func, args, None, TypeError, expected_message) 183 184 def assertHelp(self, parser, expected_help): 185 actual_help = parser.format_help() 186 if actual_help != expected_help: 187 raise self.failureException( 188 'help text failure; expected:\n"' + 189 expected_help + '"; got:\n"' + 190 actual_help + '"\n') 191 192 # -- Test make_option() aka Option ------------------------------------- 193 194 # It's not necessary to test correct options here. All the tests in the 195 # parser.parse_args() section deal with those, because they're needed 196 # there. 197 198 class TestOptionChecks(BaseTest): 199 def setUp(self): 200 self.parser = OptionParser(usage=SUPPRESS_USAGE) 201 202 def assertOptionError(self, expected_message, args=[], kwargs={}): 203 self.assertRaises(make_option, args, kwargs, 204 OptionError, expected_message) 205 206 def test_opt_string_empty(self): 207 self.assertTypeError(make_option, 208 "at least one option string must be supplied") 209 210 def test_opt_string_too_short(self): 211 self.assertOptionError( 212 "invalid option string 'b': must be at least two characters long", 213 ["b"]) 214 215 def test_opt_string_short_invalid(self): 216 self.assertOptionError( 217 "invalid short option string '--': must be " 218 "of the form -x, (x any non-dash char)", 219 ["--"]) 220 221 def test_opt_string_long_invalid(self): 222 self.assertOptionError( 223 "invalid long option string '---': " 224 "must start with --, followed by non-dash", 225 ["---"]) 226 227 def test_attr_invalid(self): 228 self.assertOptionError( 229 "option -b: invalid keyword arguments: bar, foo", 230 ["-b"], {'foo': None, 'bar': None}) 231 232 def test_action_invalid(self): 233 self.assertOptionError( 234 "option -b: invalid action: 'foo'", 235 ["-b"], {'action': 'foo'}) 236 237 def test_type_invalid(self): 238 self.assertOptionError( 239 "option -b: invalid option type: 'foo'", 240 ["-b"], {'type': 'foo'}) 241 self.assertOptionError( 242 "option -b: invalid option type: 'tuple'", 243 ["-b"], {'type': tuple}) 244 245 def test_no_type_for_action(self): 246 self.assertOptionError( 247 "option -b: must not supply a type for action 'count'", 248 ["-b"], {'action': 'count', 'type': 'int'}) 249 250 def test_no_choices_list(self): 251 self.assertOptionError( 252 "option -b/--bad: must supply a list of " 253 "choices for type 'choice'", 254 ["-b", "--bad"], {'type': "choice"}) 255 256 def test_bad_choices_list(self): 257 typename = type('').__name__ 258 self.assertOptionError( 259 "option -b/--bad: choices must be a list of " 260 "strings ('%s' supplied)" % typename, 261 ["-b", "--bad"], 262 {'type': "choice", 'choices':"bad choices"}) 263 264 def test_no_choices_for_type(self): 265 self.assertOptionError( 266 "option -b: must not supply choices for type 'int'", 267 ["-b"], {'type': 'int', 'choices':"bad"}) 268 269 def test_no_const_for_action(self): 270 self.assertOptionError( 271 "option -b: 'const' must not be supplied for action 'store'", 272 ["-b"], {'action': 'store', 'const': 1}) 273 274 def test_no_nargs_for_action(self): 275 self.assertOptionError( 276 "option -b: 'nargs' must not be supplied for action 'count'", 277 ["-b"], {'action': 'count', 'nargs': 2}) 278 279 def test_callback_not_callable(self): 280 self.assertOptionError( 281 "option -b: callback not callable: 'foo'", 282 ["-b"], {'action': 'callback', 283 'callback': 'foo'}) 284 285 def dummy(self): 286 pass 287 288 def test_callback_args_no_tuple(self): 289 self.assertOptionError( 290 "option -b: callback_args, if supplied, " 291 "must be a tuple: not 'foo'", 292 ["-b"], {'action': 'callback', 293 'callback': self.dummy, 294 'callback_args': 'foo'}) 295 296 def test_callback_kwargs_no_dict(self): 297 self.assertOptionError( 298 "option -b: callback_kwargs, if supplied, " 299 "must be a dict: not 'foo'", 300 ["-b"], {'action': 'callback', 301 'callback': self.dummy, 302 'callback_kwargs': 'foo'}) 303 304 def test_no_callback_for_action(self): 305 self.assertOptionError( 306 "option -b: callback supplied ('foo') for non-callback option", 307 ["-b"], {'action': 'store', 308 'callback': 'foo'}) 309 310 def test_no_callback_args_for_action(self): 311 self.assertOptionError( 312 "option -b: callback_args supplied for non-callback option", 313 ["-b"], {'action': 'store', 314 'callback_args': 'foo'}) 315 316 def test_no_callback_kwargs_for_action(self): 317 self.assertOptionError( 318 "option -b: callback_kwargs supplied for non-callback option", 319 ["-b"], {'action': 'store', 320 'callback_kwargs': 'foo'}) 321 322 def test_no_single_dash(self): 323 self.assertOptionError( 324 "invalid long option string '-debug': " 325 "must start with --, followed by non-dash", 326 ["-debug"]) 327 328 self.assertOptionError( 329 "option -d: invalid long option string '-debug': must start with" 330 " --, followed by non-dash", 331 ["-d", "-debug"]) 332 333 self.assertOptionError( 334 "invalid long option string '-debug': " 335 "must start with --, followed by non-dash", 336 ["-debug", "--debug"]) 337 338 class TestOptionParser(BaseTest): 339 def setUp(self): 340 self.parser = OptionParser() 341 self.parser.add_option("-v", "--verbose", "-n", "--noisy", 342 action="store_true", dest="verbose") 343 self.parser.add_option("-q", "--quiet", "--silent", 344 action="store_false", dest="verbose") 345 346 def test_add_option_no_Option(self): 347 self.assertTypeError(self.parser.add_option, 348 "not an Option instance: None", None) 349 350 def test_add_option_invalid_arguments(self): 351 self.assertTypeError(self.parser.add_option, 352 "invalid arguments", None, None) 353 354 def test_get_option(self): 355 opt1 = self.parser.get_option("-v") 356 self.assertIsInstance(opt1, Option) 357 self.assertEqual(opt1._short_opts, ["-v", "-n"]) 358 self.assertEqual(opt1._long_opts, ["--verbose", "--noisy"]) 359 self.assertEqual(opt1.action, "store_true") 360 self.assertEqual(opt1.dest, "verbose") 361 362 def test_get_option_equals(self): 363 opt1 = self.parser.get_option("-v") 364 opt2 = self.parser.get_option("--verbose") 365 opt3 = self.parser.get_option("-n") 366 opt4 = self.parser.get_option("--noisy") 367 self.assertTrue(opt1 is opt2 is opt3 is opt4) 368 369 def test_has_option(self): 370 self.assertTrue(self.parser.has_option("-v")) 371 self.assertTrue(self.parser.has_option("--verbose")) 372 373 def assertTrueremoved(self): 374 self.assertTrue(self.parser.get_option("-v") is None) 375 self.assertTrue(self.parser.get_option("--verbose") is None) 376 self.assertTrue(self.parser.get_option("-n") is None) 377 self.assertTrue(self.parser.get_option("--noisy") is None) 378 379 self.assertFalse(self.parser.has_option("-v")) 380 self.assertFalse(self.parser.has_option("--verbose")) 381 self.assertFalse(self.parser.has_option("-n")) 382 self.assertFalse(self.parser.has_option("--noisy")) 383 384 self.assertTrue(self.parser.has_option("-q")) 385 self.assertTrue(self.parser.has_option("--silent")) 386 387 def test_remove_short_opt(self): 388 self.parser.remove_option("-n") 389 self.assertTrueremoved() 390 391 def test_remove_long_opt(self): 392 self.parser.remove_option("--verbose") 393 self.assertTrueremoved() 394 395 def test_remove_nonexistent(self): 396 self.assertRaises(self.parser.remove_option, ('foo',), None, 397 ValueError, "no such option 'foo'") 398 399 @support.impl_detail('Relies on sys.getrefcount', cpython=True) 400 def test_refleak(self): 401 # If an OptionParser is carrying around a reference to a large 402 # object, various cycles can prevent it from being GC'd in 403 # a timely fashion. destroy() breaks the cycles to ensure stuff 404 # can be cleaned up. 405 big_thing = [42] 406 refcount = sys.getrefcount(big_thing) 407 parser = OptionParser() 408 parser.add_option("-a", "--aaarggh") 409 parser.big_thing = big_thing 410 411 parser.destroy() 412 #self.assertEqual(refcount, sys.getrefcount(big_thing)) 413 del parser 414 self.assertEqual(refcount, sys.getrefcount(big_thing)) 415 416 417 class TestOptionValues(BaseTest): 418 def setUp(self): 419 pass 420 421 def test_basics(self): 422 values = Values() 423 self.assertEqual(vars(values), {}) 424 self.assertEqual(values, {}) 425 self.assertNotEqual(values, {"foo": "bar"}) 426 self.assertNotEqual(values, "") 427 428 dict = {"foo": "bar", "baz": 42} 429 values = Values(defaults=dict) 430 self.assertEqual(vars(values), dict) 431 self.assertEqual(values, dict) 432 self.assertNotEqual(values, {"foo": "bar"}) 433 self.assertNotEqual(values, {}) 434 self.assertNotEqual(values, "") 435 self.assertNotEqual(values, []) 436 437 438 class TestTypeAliases(BaseTest): 439 def setUp(self): 440 self.parser = OptionParser() 441 442 def test_str_aliases_string(self): 443 self.parser.add_option("-s", type="str") 444 self.assertEqual(self.parser.get_option("-s").type, "string") 445 446 def test_type_object(self): 447 self.parser.add_option("-s", type=str) 448 self.assertEqual(self.parser.get_option("-s").type, "string") 449 self.parser.add_option("-x", type=int) 450 self.assertEqual(self.parser.get_option("-x").type, "int") 451 452 453 # Custom type for testing processing of default values. 454 _time_units = { 's' : 1, 'm' : 60, 'h' : 60*60, 'd' : 60*60*24 } 455 456 def _check_duration(option, opt, value): 457 try: 458 if value[-1].isdigit(): 459 return int(value) 460 else: 461 return int(value[:-1]) * _time_units[value[-1]] 462 except (ValueError, IndexError): 463 raise OptionValueError( 464 'option %s: invalid duration: %r' % (opt, value)) 465 466 class DurationOption(Option): 467 TYPES = Option.TYPES + ('duration',) 468 TYPE_CHECKER = copy.copy(Option.TYPE_CHECKER) 469 TYPE_CHECKER['duration'] = _check_duration 470 471 class TestDefaultValues(BaseTest): 472 def setUp(self): 473 self.parser = OptionParser() 474 self.parser.add_option("-v", "--verbose", default=True) 475 self.parser.add_option("-q", "--quiet", dest='verbose') 476 self.parser.add_option("-n", type="int", default=37) 477 self.parser.add_option("-m", type="int") 478 self.parser.add_option("-s", default="foo") 479 self.parser.add_option("-t") 480 self.parser.add_option("-u", default=None) 481 self.expected = { 'verbose': True, 482 'n': 37, 483 'm': None, 484 's': "foo", 485 't': None, 486 'u': None } 487 488 def test_basic_defaults(self): 489 self.assertEqual(self.parser.get_default_values(), self.expected) 490 491 def test_mixed_defaults_post(self): 492 self.parser.set_defaults(n=42, m=-100) 493 self.expected.update({'n': 42, 'm': -100}) 494 self.assertEqual(self.parser.get_default_values(), self.expected) 495 496 def test_mixed_defaults_pre(self): 497 self.parser.set_defaults(x="barf", y="blah") 498 self.parser.add_option("-x", default="frob") 499 self.parser.add_option("-y") 500 501 self.expected.update({'x': "frob", 'y': "blah"}) 502 self.assertEqual(self.parser.get_default_values(), self.expected) 503 504 self.parser.remove_option("-y") 505 self.parser.add_option("-y", default=None) 506 self.expected.update({'y': None}) 507 self.assertEqual(self.parser.get_default_values(), self.expected) 508 509 def test_process_default(self): 510 self.parser.option_class = DurationOption 511 self.parser.add_option("-d", type="duration", default=300) 512 self.parser.add_option("-e", type="duration", default="6m") 513 self.parser.set_defaults(n="42") 514 self.expected.update({'d': 300, 'e': 360, 'n': 42}) 515 self.assertEqual(self.parser.get_default_values(), self.expected) 516 517 self.parser.set_process_default_values(False) 518 self.expected.update({'d': 300, 'e': "6m", 'n': "42"}) 519 self.assertEqual(self.parser.get_default_values(), self.expected) 520 521 522 class TestProgName(BaseTest): 523 """ 524 Test that %prog expands to the right thing in usage, version, 525 and help strings. 526 """ 527 528 def assertUsage(self, parser, expected_usage): 529 self.assertEqual(parser.get_usage(), expected_usage) 530 531 def assertVersion(self, parser, expected_version): 532 self.assertEqual(parser.get_version(), expected_version) 533 534 535 def test_default_progname(self): 536 # Make sure that program name taken from sys.argv[0] by default. 537 save_argv = sys.argv[:] 538 try: 539 sys.argv[0] = os.path.join("foo", "bar", "baz.py") 540 parser = OptionParser("%prog ...", version="%prog 1.2") 541 expected_usage = "Usage: baz.py ...\n" 542 self.assertUsage(parser, expected_usage) 543 self.assertVersion(parser, "baz.py 1.2") 544 self.assertHelp(parser, 545 expected_usage + "\n" + 546 "Options:\n" 547 " --version show program's version number and exit\n" 548 " -h, --help show this help message and exit\n") 549 finally: 550 sys.argv[:] = save_argv 551 552 def test_custom_progname(self): 553 parser = OptionParser(prog="thingy", 554 version="%prog 0.1", 555 usage="%prog arg arg") 556 parser.remove_option("-h") 557 parser.remove_option("--version") 558 expected_usage = "Usage: thingy arg arg\n" 559 self.assertUsage(parser, expected_usage) 560 self.assertVersion(parser, "thingy 0.1") 561 self.assertHelp(parser, expected_usage + "\n") 562 563 564 class TestExpandDefaults(BaseTest): 565 def setUp(self): 566 self.parser = OptionParser(prog="test") 567 self.help_prefix = """\ 568 Usage: test [options] 569 570 Options: 571 -h, --help show this help message and exit 572 """ 573 self.file_help = "read from FILE [default: %default]" 574 self.expected_help_file = self.help_prefix + \ 575 " -f FILE, --file=FILE read from FILE [default: foo.txt]\n" 576 self.expected_help_none = self.help_prefix + \ 577 " -f FILE, --file=FILE read from FILE [default: none]\n" 578 579 def test_option_default(self): 580 self.parser.add_option("-f", "--file", 581 default="foo.txt", 582 help=self.file_help) 583 self.assertHelp(self.parser, self.expected_help_file) 584 585 def test_parser_default_1(self): 586 self.parser.add_option("-f", "--file", 587 help=self.file_help) 588 self.parser.set_default('file', "foo.txt") 589 self.assertHelp(self.parser, self.expected_help_file) 590 591 def test_parser_default_2(self): 592 self.parser.add_option("-f", "--file", 593 help=self.file_help) 594 self.parser.set_defaults(file="foo.txt") 595 self.assertHelp(self.parser, self.expected_help_file) 596 597 def test_no_default(self): 598 self.parser.add_option("-f", "--file", 599 help=self.file_help) 600 self.assertHelp(self.parser, self.expected_help_none) 601 602 def test_default_none_1(self): 603 self.parser.add_option("-f", "--file", 604 default=None, 605 help=self.file_help) 606 self.assertHelp(self.parser, self.expected_help_none) 607 608 def test_default_none_2(self): 609 self.parser.add_option("-f", "--file", 610 help=self.file_help) 611 self.parser.set_defaults(file=None) 612 self.assertHelp(self.parser, self.expected_help_none) 613 614 def test_float_default(self): 615 self.parser.add_option( 616 "-p", "--prob", 617 help="blow up with probability PROB [default: %default]") 618 self.parser.set_defaults(prob=0.43) 619 expected_help = self.help_prefix + \ 620 " -p PROB, --prob=PROB blow up with probability PROB [default: 0.43]\n" 621 self.assertHelp(self.parser, expected_help) 622 623 def test_alt_expand(self): 624 self.parser.add_option("-f", "--file", 625 default="foo.txt", 626 help="read from FILE [default: *DEFAULT*]") 627 self.parser.formatter.default_tag = "*DEFAULT*" 628 self.assertHelp(self.parser, self.expected_help_file) 629 630 def test_no_expand(self): 631 self.parser.add_option("-f", "--file", 632 default="foo.txt", 633 help="read from %default file") 634 self.parser.formatter.default_tag = None 635 expected_help = self.help_prefix + \ 636 " -f FILE, --file=FILE read from %default file\n" 637 self.assertHelp(self.parser, expected_help) 638 639 640 # -- Test parser.parse_args() ------------------------------------------ 641 642 class TestStandard(BaseTest): 643 def setUp(self): 644 options = [make_option("-a", type="string"), 645 make_option("-b", "--boo", type="int", dest='boo'), 646 make_option("--foo", action="append")] 647 648 self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE, 649 option_list=options) 650 651 def test_required_value(self): 652 self.assertParseFail(["-a"], "-a option requires 1 argument") 653 654 def test_invalid_integer(self): 655 self.assertParseFail(["-b", "5x"], 656 "option -b: invalid integer value: '5x'") 657 658 def test_no_such_option(self): 659 self.assertParseFail(["--boo13"], "no such option: --boo13") 660 661 def test_long_invalid_integer(self): 662 self.assertParseFail(["--boo=x5"], 663 "option --boo: invalid integer value: 'x5'") 664 665 def test_empty(self): 666 self.assertParseOK([], {'a': None, 'boo': None, 'foo': None}, []) 667 668 def test_shortopt_empty_longopt_append(self): 669 self.assertParseOK(["-a", "", "--foo=blah", "--foo="], 670 {'a': "", 'boo': None, 'foo': ["blah", ""]}, 671 []) 672 673 def test_long_option_append(self): 674 self.assertParseOK(["--foo", "bar", "--foo", "", "--foo=x"], 675 {'a': None, 676 'boo': None, 677 'foo': ["bar", "", "x"]}, 678 []) 679 680 def test_option_argument_joined(self): 681 self.assertParseOK(["-abc"], 682 {'a': "bc", 'boo': None, 'foo': None}, 683 []) 684 685 def test_option_argument_split(self): 686 self.assertParseOK(["-a", "34"], 687 {'a': "34", 'boo': None, 'foo': None}, 688 []) 689 690 def test_option_argument_joined_integer(self): 691 self.assertParseOK(["-b34"], 692 {'a': None, 'boo': 34, 'foo': None}, 693 []) 694 695 def test_option_argument_split_negative_integer(self): 696 self.assertParseOK(["-b", "-5"], 697 {'a': None, 'boo': -5, 'foo': None}, 698 []) 699 700 def test_long_option_argument_joined(self): 701 self.assertParseOK(["--boo=13"], 702 {'a': None, 'boo': 13, 'foo': None}, 703 []) 704 705 def test_long_option_argument_split(self): 706 self.assertParseOK(["--boo", "111"], 707 {'a': None, 'boo': 111, 'foo': None}, 708 []) 709 710 def test_long_option_short_option(self): 711 self.assertParseOK(["--foo=bar", "-axyz"], 712 {'a': 'xyz', 'boo': None, 'foo': ["bar"]}, 713 []) 714 715 def test_abbrev_long_option(self): 716 self.assertParseOK(["--f=bar", "-axyz"], 717 {'a': 'xyz', 'boo': None, 'foo': ["bar"]}, 718 []) 719 720 def test_defaults(self): 721 (options, args) = self.parser.parse_args([]) 722 defaults = self.parser.get_default_values() 723 self.assertEqual(vars(defaults), vars(options)) 724 725 def test_ambiguous_option(self): 726 self.parser.add_option("--foz", action="store", 727 type="string", dest="foo") 728 self.assertParseFail(["--f=bar"], 729 "ambiguous option: --f (--foo, --foz?)") 730 731 732 def test_short_and_long_option_split(self): 733 self.assertParseOK(["-a", "xyz", "--foo", "bar"], 734 {'a': 'xyz', 'boo': None, 'foo': ["bar"]}, 735 []) 736 737 def test_short_option_split_long_option_append(self): 738 self.assertParseOK(["--foo=bar", "-b", "123", "--foo", "baz"], 739 {'a': None, 'boo': 123, 'foo': ["bar", "baz"]}, 740 []) 741 742 def test_short_option_split_one_positional_arg(self): 743 self.assertParseOK(["-a", "foo", "bar"], 744 {'a': "foo", 'boo': None, 'foo': None}, 745 ["bar"]) 746 747 def test_short_option_consumes_separator(self): 748 self.assertParseOK(["-a", "--", "foo", "bar"], 749 {'a': "--", 'boo': None, 'foo': None}, 750 ["foo", "bar"]) 751 self.assertParseOK(["-a", "--", "--foo", "bar"], 752 {'a': "--", 'boo': None, 'foo': ["bar"]}, 753 []) 754 755 def test_short_option_joined_and_separator(self): 756 self.assertParseOK(["-ab", "--", "--foo", "bar"], 757 {'a': "b", 'boo': None, 'foo': None}, 758 ["--foo", "bar"]), 759 760 def test_hyphen_becomes_positional_arg(self): 761 self.assertParseOK(["-ab", "-", "--foo", "bar"], 762 {'a': "b", 'boo': None, 'foo': ["bar"]}, 763 ["-"]) 764 765 def test_no_append_versus_append(self): 766 self.assertParseOK(["-b3", "-b", "5", "--foo=bar", "--foo", "baz"], 767 {'a': None, 'boo': 5, 'foo': ["bar", "baz"]}, 768 []) 769 770 def test_option_consumes_optionlike_string(self): 771 self.assertParseOK(["-a", "-b3"], 772 {'a': "-b3", 'boo': None, 'foo': None}, 773 []) 774 775 def test_combined_single_invalid_option(self): 776 self.parser.add_option("-t", action="store_true") 777 self.assertParseFail(["-test"], 778 "no such option: -e") 779 780 class TestBool(BaseTest): 781 def setUp(self): 782 options = [make_option("-v", 783 "--verbose", 784 action="store_true", 785 dest="verbose", 786 default=''), 787 make_option("-q", 788 "--quiet", 789 action="store_false", 790 dest="verbose")] 791 self.parser = OptionParser(option_list = options) 792 793 def test_bool_default(self): 794 self.assertParseOK([], 795 {'verbose': ''}, 796 []) 797 798 def test_bool_false(self): 799 (options, args) = self.assertParseOK(["-q"], 800 {'verbose': 0}, 801 []) 802 self.assertTrue(options.verbose is False) 803 804 def test_bool_true(self): 805 (options, args) = self.assertParseOK(["-v"], 806 {'verbose': 1}, 807 []) 808 self.assertTrue(options.verbose is True) 809 810 def test_bool_flicker_on_and_off(self): 811 self.assertParseOK(["-qvq", "-q", "-v"], 812 {'verbose': 1}, 813 []) 814 815 class TestChoice(BaseTest): 816 def setUp(self): 817 self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE) 818 self.parser.add_option("-c", action="store", type="choice", 819 dest="choice", choices=["one", "two", "three"]) 820 821 def test_valid_choice(self): 822 self.assertParseOK(["-c", "one", "xyz"], 823 {'choice': 'one'}, 824 ["xyz"]) 825 826 def test_invalid_choice(self): 827 self.assertParseFail(["-c", "four", "abc"], 828 "option -c: invalid choice: 'four' " 829 "(choose from 'one', 'two', 'three')") 830 831 def test_add_choice_option(self): 832 self.parser.add_option("-d", "--default", 833 choices=["four", "five", "six"]) 834 opt = self.parser.get_option("-d") 835 self.assertEqual(opt.type, "choice") 836 self.assertEqual(opt.action, "store") 837 838 class TestCount(BaseTest): 839 def setUp(self): 840 self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE) 841 self.v_opt = make_option("-v", action="count", dest="verbose") 842 self.parser.add_option(self.v_opt) 843 self.parser.add_option("--verbose", type="int", dest="verbose") 844 self.parser.add_option("-q", "--quiet", 845 action="store_const", dest="verbose", const=0) 846 847 def test_empty(self): 848 self.assertParseOK([], {'verbose': None}, []) 849 850 def test_count_one(self): 851 self.assertParseOK(["-v"], {'verbose': 1}, []) 852 853 def test_count_three(self): 854 self.assertParseOK(["-vvv"], {'verbose': 3}, []) 855 856 def test_count_three_apart(self): 857 self.assertParseOK(["-v", "-v", "-v"], {'verbose': 3}, []) 858 859 def test_count_override_amount(self): 860 self.assertParseOK(["-vvv", "--verbose=2"], {'verbose': 2}, []) 861 862 def test_count_override_quiet(self): 863 self.assertParseOK(["-vvv", "--verbose=2", "-q"], {'verbose': 0}, []) 864 865 def test_count_overriding(self): 866 self.assertParseOK(["-vvv", "--verbose=2", "-q", "-v"], 867 {'verbose': 1}, []) 868 869 def test_count_interspersed_args(self): 870 self.assertParseOK(["--quiet", "3", "-v"], 871 {'verbose': 1}, 872 ["3"]) 873 874 def test_count_no_interspersed_args(self): 875 self.parser.disable_interspersed_args() 876 self.assertParseOK(["--quiet", "3", "-v"], 877 {'verbose': 0}, 878 ["3", "-v"]) 879 880 def test_count_no_such_option(self): 881 self.assertParseFail(["-q3", "-v"], "no such option: -3") 882 883 def test_count_option_no_value(self): 884 self.assertParseFail(["--quiet=3", "-v"], 885 "--quiet option does not take a value") 886 887 def test_count_with_default(self): 888 self.parser.set_default('verbose', 0) 889 self.assertParseOK([], {'verbose':0}, []) 890 891 def test_count_overriding_default(self): 892 self.parser.set_default('verbose', 0) 893 self.assertParseOK(["-vvv", "--verbose=2", "-q", "-v"], 894 {'verbose': 1}, []) 895 896 class TestMultipleArgs(BaseTest): 897 def setUp(self): 898 self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE) 899 self.parser.add_option("-p", "--point", 900 action="store", nargs=3, type="float", dest="point") 901 902 def test_nargs_with_positional_args(self): 903 self.assertParseOK(["foo", "-p", "1", "2.5", "-4.3", "xyz"], 904 {'point': (1.0, 2.5, -4.3)}, 905 ["foo", "xyz"]) 906 907 def test_nargs_long_opt(self): 908 self.assertParseOK(["--point", "-1", "2.5", "-0", "xyz"], 909 {'point': (-1.0, 2.5, -0.0)}, 910 ["xyz"]) 911 912 def test_nargs_invalid_float_value(self): 913 self.assertParseFail(["-p", "1.0", "2x", "3.5"], 914 "option -p: " 915 "invalid floating-point value: '2x'") 916 917 def test_nargs_required_values(self): 918 self.assertParseFail(["--point", "1.0", "3.5"], 919 "--point option requires 3 arguments") 920 921 class TestMultipleArgsAppend(BaseTest): 922 def setUp(self): 923 self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE) 924 self.parser.add_option("-p", "--point", action="store", nargs=3, 925 type="float", dest="point") 926 self.parser.add_option("-f", "--foo", action="append", nargs=2, 927 type="int", dest="foo") 928 self.parser.add_option("-z", "--zero", action="append_const", 929 dest="foo", const=(0, 0)) 930 931 def test_nargs_append(self): 932 self.assertParseOK(["-f", "4", "-3", "blah", "--foo", "1", "666"], 933 {'point': None, 'foo': [(4, -3), (1, 666)]}, 934 ["blah"]) 935 936 def test_nargs_append_required_values(self): 937 self.assertParseFail(["-f4,3"], 938 "-f option requires 2 arguments") 939 940 def test_nargs_append_simple(self): 941 self.assertParseOK(["--foo=3", "4"], 942 {'point': None, 'foo':[(3, 4)]}, 943 []) 944 945 def test_nargs_append_const(self): 946 self.assertParseOK(["--zero", "--foo", "3", "4", "-z"], 947 {'point': None, 'foo':[(0, 0), (3, 4), (0, 0)]}, 948 []) 949 950 class TestVersion(BaseTest): 951 def test_version(self): 952 self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE, 953 version="%prog 0.1") 954 save_argv = sys.argv[:] 955 try: 956 sys.argv[0] = os.path.join(os.curdir, "foo", "bar") 957 self.assertOutput(["--version"], "bar 0.1\n") 958 finally: 959 sys.argv[:] = save_argv 960 961 def test_no_version(self): 962 self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE) 963 self.assertParseFail(["--version"], 964 "no such option: --version") 965 966 # -- Test conflicting default values and parser.parse_args() ----------- 967 968 class TestConflictingDefaults(BaseTest): 969 """Conflicting default values: the last one should win.""" 970 def setUp(self): 971 self.parser = OptionParser(option_list=[ 972 make_option("-v", action="store_true", dest="verbose", default=1)]) 973 974 def test_conflict_default(self): 975 self.parser.add_option("-q", action="store_false", dest="verbose", 976 default=0) 977 self.assertParseOK([], {'verbose': 0}, []) 978 979 def test_conflict_default_none(self): 980 self.parser.add_option("-q", action="store_false", dest="verbose", 981 default=None) 982 self.assertParseOK([], {'verbose': None}, []) 983 984 class TestOptionGroup(BaseTest): 985 def setUp(self): 986 self.parser = OptionParser(usage=SUPPRESS_USAGE) 987 988 def test_option_group_create_instance(self): 989 group = OptionGroup(self.parser, "Spam") 990 self.parser.add_option_group(group) 991 group.add_option("--spam", action="store_true", 992 help="spam spam spam spam") 993 self.assertParseOK(["--spam"], {'spam': 1}, []) 994 995 def test_add_group_no_group(self): 996 self.assertTypeError(self.parser.add_option_group, 997 "not an OptionGroup instance: None", None) 998 999 def test_add_group_invalid_arguments(self): 1000 self.assertTypeError(self.parser.add_option_group, 1001 "invalid arguments", None, None) 1002 1003 def test_add_group_wrong_parser(self): 1004 group = OptionGroup(self.parser, "Spam") 1005 group.parser = OptionParser() 1006 self.assertRaises(self.parser.add_option_group, (group,), None, 1007 ValueError, "invalid OptionGroup (wrong parser)") 1008 1009 def test_group_manipulate(self): 1010 group = self.parser.add_option_group("Group 2", 1011 description="Some more options") 1012 group.set_title("Bacon") 1013 group.add_option("--bacon", type="int") 1014 self.assertTrue(self.parser.get_option_group("--bacon"), group) 1015 1016 # -- Test extending and parser.parse_args() ---------------------------- 1017 1018 class TestExtendAddTypes(BaseTest): 1019 def setUp(self): 1020 self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE, 1021 option_class=self.MyOption) 1022 self.parser.add_option("-a", None, type="string", dest="a") 1023 self.parser.add_option("-f", "--file", type="file", dest="file") 1024 1025 def tearDown(self): 1026 if os.path.isdir(support.TESTFN): 1027 os.rmdir(support.TESTFN) 1028 elif os.path.isfile(support.TESTFN): 1029 os.unlink(support.TESTFN) 1030 1031 class MyOption (Option): 1032 def check_file(option, opt, value): 1033 if not os.path.exists(value): 1034 raise OptionValueError("%s: file does not exist" % value) 1035 elif not os.path.isfile(value): 1036 raise OptionValueError("%s: not a regular file" % value) 1037 return value 1038 1039 TYPES = Option.TYPES + ("file",) 1040 TYPE_CHECKER = copy.copy(Option.TYPE_CHECKER) 1041 TYPE_CHECKER["file"] = check_file 1042 1043 def test_filetype_ok(self): 1044 support.create_empty_file(support.TESTFN) 1045 self.assertParseOK(["--file", support.TESTFN, "-afoo"], 1046 {'file': support.TESTFN, 'a': 'foo'}, 1047 []) 1048 1049 def test_filetype_noexist(self): 1050 self.assertParseFail(["--file", support.TESTFN, "-afoo"], 1051 "%s: file does not exist" % 1052 support.TESTFN) 1053 1054 def test_filetype_notfile(self): 1055 os.mkdir(support.TESTFN) 1056 self.assertParseFail(["--file", support.TESTFN, "-afoo"], 1057 "%s: not a regular file" % 1058 support.TESTFN) 1059 1060 1061 class TestExtendAddActions(BaseTest): 1062 def setUp(self): 1063 options = [self.MyOption("-a", "--apple", action="extend", 1064 type="string", dest="apple")] 1065 self.parser = OptionParser(option_list=options) 1066 1067 class MyOption (Option): 1068 ACTIONS = Option.ACTIONS + ("extend",) 1069 STORE_ACTIONS = Option.STORE_ACTIONS + ("extend",) 1070 TYPED_ACTIONS = Option.TYPED_ACTIONS + ("extend",) 1071 1072 def take_action(self, action, dest, opt, value, values, parser): 1073 if action == "extend": 1074 lvalue = value.split(",") 1075 values.ensure_value(dest, []).extend(lvalue) 1076 else: 1077 Option.take_action(self, action, dest, opt, parser, value, 1078 values) 1079 1080 def test_extend_add_action(self): 1081 self.assertParseOK(["-afoo,bar", "--apple=blah"], 1082 {'apple': ["foo", "bar", "blah"]}, 1083 []) 1084 1085 def test_extend_add_action_normal(self): 1086 self.assertParseOK(["-a", "foo", "-abar", "--apple=x,y"], 1087 {'apple': ["foo", "bar", "x", "y"]}, 1088 []) 1089 1090 # -- Test callbacks and parser.parse_args() ---------------------------- 1091 1092 class TestCallback(BaseTest): 1093 def setUp(self): 1094 options = [make_option("-x", 1095 None, 1096 action="callback", 1097 callback=self.process_opt), 1098 make_option("-f", 1099 "--file", 1100 action="callback", 1101 callback=self.process_opt, 1102 type="string", 1103 dest="filename")] 1104 self.parser = OptionParser(option_list=options) 1105 1106 def process_opt(self, option, opt, value, parser_): 1107 if opt == "-x": 1108 self.assertEqual(option._short_opts, ["-x"]) 1109 self.assertEqual(option._long_opts, []) 1110 self.assertTrue(parser_ is self.parser) 1111 self.assertTrue(value is None) 1112 self.assertEqual(vars(parser_.values), {'filename': None}) 1113 1114 parser_.values.x = 42 1115 elif opt == "--file": 1116 self.assertEqual(option._short_opts, ["-f"]) 1117 self.assertEqual(option._long_opts, ["--file"]) 1118 self.assertTrue(parser_ is self.parser) 1119 self.assertEqual(value, "foo") 1120 self.assertEqual(vars(parser_.values), {'filename': None, 'x': 42}) 1121 1122 setattr(parser_.values, option.dest, value) 1123 else: 1124 self.fail("Unknown option %r in process_opt." % opt) 1125 1126 def test_callback(self): 1127 self.assertParseOK(["-x", "--file=foo"], 1128 {'filename': "foo", 'x': 42}, 1129 []) 1130 1131 def test_callback_help(self): 1132 # This test was prompted by SF bug #960515 -- the point is 1133 # not to inspect the help text, just to make sure that 1134 # format_help() doesn't crash. 1135 parser = OptionParser(usage=SUPPRESS_USAGE) 1136 parser.remove_option("-h") 1137 parser.add_option("-t", "--test", action="callback", 1138 callback=lambda: None, type="string", 1139 help="foo") 1140 1141 expected_help = ("Options:\n" 1142 " -t TEST, --test=TEST foo\n") 1143 self.assertHelp(parser, expected_help) 1144 1145 1146 class TestCallbackExtraArgs(BaseTest): 1147 def setUp(self): 1148 options = [make_option("-p", "--point", action="callback", 1149 callback=self.process_tuple, 1150 callback_args=(3, int), type="string", 1151 dest="points", default=[])] 1152 self.parser = OptionParser(option_list=options) 1153 1154 def process_tuple(self, option, opt, value, parser_, len, type): 1155 self.assertEqual(len, 3) 1156 self.assertTrue(type is int) 1157 1158 if opt == "-p": 1159 self.assertEqual(value, "1,2,3") 1160 elif opt == "--point": 1161 self.assertEqual(value, "4,5,6") 1162 1163 value = tuple(map(type, value.split(","))) 1164 getattr(parser_.values, option.dest).append(value) 1165 1166 def test_callback_extra_args(self): 1167 self.assertParseOK(["-p1,2,3", "--point", "4,5,6"], 1168 {'points': [(1,2,3), (4,5,6)]}, 1169 []) 1170 1171 class TestCallbackMeddleArgs(BaseTest): 1172 def setUp(self): 1173 options = [make_option(str(x), action="callback", 1174 callback=self.process_n, dest='things') 1175 for x in range(-1, -6, -1)] 1176 self.parser = OptionParser(option_list=options) 1177 1178 # Callback that meddles in rargs, largs 1179 def process_n(self, option, opt, value, parser_): 1180 # option is -3, -5, etc. 1181 nargs = int(opt[1:]) 1182 rargs = parser_.rargs 1183 if len(rargs) < nargs: 1184 self.fail("Expected %d arguments for %s option." % (nargs, opt)) 1185 dest = parser_.values.ensure_value(option.dest, []) 1186 dest.append(tuple(rargs[0:nargs])) 1187 parser_.largs.append(nargs) 1188 del rargs[0:nargs] 1189 1190 def test_callback_meddle_args(self): 1191 self.assertParseOK(["-1", "foo", "-3", "bar", "baz", "qux"], 1192 {'things': [("foo",), ("bar", "baz", "qux")]}, 1193 [1, 3]) 1194 1195 def test_callback_meddle_args_separator(self): 1196 self.assertParseOK(["-2", "foo", "--"], 1197 {'things': [('foo', '--')]}, 1198 [2]) 1199 1200 class TestCallbackManyArgs(BaseTest): 1201 def setUp(self): 1202 options = [make_option("-a", "--apple", action="callback", nargs=2, 1203 callback=self.process_many, type="string"), 1204 make_option("-b", "--bob", action="callback", nargs=3, 1205 callback=self.process_many, type="int")] 1206 self.parser = OptionParser(option_list=options) 1207 1208 def process_many(self, option, opt, value, parser_): 1209 if opt == "-a": 1210 self.assertEqual(value, ("foo", "bar")) 1211 elif opt == "--apple": 1212 self.assertEqual(value, ("ding", "dong")) 1213 elif opt == "-b": 1214 self.assertEqual(value, (1, 2, 3)) 1215 elif opt == "--bob": 1216 self.assertEqual(value, (-666, 42, 0)) 1217 1218 def test_many_args(self): 1219 self.assertParseOK(["-a", "foo", "bar", "--apple", "ding", "dong", 1220 "-b", "1", "2", "3", "--bob", "-666", "42", 1221 "0"], 1222 {"apple": None, "bob": None}, 1223 []) 1224 1225 class TestCallbackCheckAbbrev(BaseTest): 1226 def setUp(self): 1227 self.parser = OptionParser() 1228 self.parser.add_option("--foo-bar", action="callback", 1229 callback=self.check_abbrev) 1230 1231 def check_abbrev(self, option, opt, value, parser): 1232 self.assertEqual(opt, "--foo-bar") 1233 1234 def test_abbrev_callback_expansion(self): 1235 self.assertParseOK(["--foo"], {}, []) 1236 1237 class TestCallbackVarArgs(BaseTest): 1238 def setUp(self): 1239 options = [make_option("-a", type="int", nargs=2, dest="a"), 1240 make_option("-b", action="store_true", dest="b"), 1241 make_option("-c", "--callback", action="callback", 1242 callback=self.variable_args, dest="c")] 1243 self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE, 1244 option_list=options) 1245 1246 def variable_args(self, option, opt, value, parser): 1247 self.assertTrue(value is None) 1248 value = [] 1249 rargs = parser.rargs 1250 while rargs: 1251 arg = rargs[0] 1252 if ((arg[:2] == "--" and len(arg) > 2) or 1253 (arg[:1] == "-" and len(arg) > 1 and arg[1] != "-")): 1254 break 1255 else: 1256 value.append(arg) 1257 del rargs[0] 1258 setattr(parser.values, option.dest, value) 1259 1260 def test_variable_args(self): 1261 self.assertParseOK(["-a3", "-5", "--callback", "foo", "bar"], 1262 {'a': (3, -5), 'b': None, 'c': ["foo", "bar"]}, 1263 []) 1264 1265 def test_consume_separator_stop_at_option(self): 1266 self.assertParseOK(["-c", "37", "--", "xxx", "-b", "hello"], 1267 {'a': None, 1268 'b': True, 1269 'c': ["37", "--", "xxx"]}, 1270 ["hello"]) 1271 1272 def test_positional_arg_and_variable_args(self): 1273 self.assertParseOK(["hello", "-c", "foo", "-", "bar"], 1274 {'a': None, 1275 'b': None, 1276 'c':["foo", "-", "bar"]}, 1277 ["hello"]) 1278 1279 def test_stop_at_option(self): 1280 self.assertParseOK(["-c", "foo", "-b"], 1281 {'a': None, 'b': True, 'c': ["foo"]}, 1282 []) 1283 1284 def test_stop_at_invalid_option(self): 1285 self.assertParseFail(["-c", "3", "-5", "-a"], "no such option: -5") 1286 1287 1288 # -- Test conflict handling and parser.parse_args() -------------------- 1289 1290 class ConflictBase(BaseTest): 1291 def setUp(self): 1292 options = [make_option("-v", "--verbose", action="count", 1293 dest="verbose", help="increment verbosity")] 1294 self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE, 1295 option_list=options) 1296 1297 def show_version(self, option, opt, value, parser): 1298 parser.values.show_version = 1 1299 1300 class TestConflict(ConflictBase): 1301 """Use the default conflict resolution for Optik 1.2: error.""" 1302 def assertTrueconflict_error(self, func): 1303 err = self.assertRaises( 1304 func, ("-v", "--version"), {'action' : "callback", 1305 'callback' : self.show_version, 1306 'help' : "show version"}, 1307 OptionConflictError, 1308 "option -v/--version: conflicting option string(s): -v") 1309 1310 self.assertEqual(err.msg, "conflicting option string(s): -v") 1311 self.assertEqual(err.option_id, "-v/--version") 1312 1313 def test_conflict_error(self): 1314 self.assertTrueconflict_error(self.parser.add_option) 1315 1316 def test_conflict_error_group(self): 1317 group = OptionGroup(self.parser, "Group 1") 1318 self.assertTrueconflict_error(group.add_option) 1319 1320 def test_no_such_conflict_handler(self): 1321 self.assertRaises( 1322 self.parser.set_conflict_handler, ('foo',), None, 1323 ValueError, "invalid conflict_resolution value 'foo'") 1324 1325 1326 class TestConflictResolve(ConflictBase): 1327 def setUp(self): 1328 ConflictBase.setUp(self) 1329 self.parser.set_conflict_handler("resolve") 1330 self.parser.add_option("-v", "--version", action="callback", 1331 callback=self.show_version, help="show version") 1332 1333 def test_conflict_resolve(self): 1334 v_opt = self.parser.get_option("-v") 1335 verbose_opt = self.parser.get_option("--verbose") 1336 version_opt = self.parser.get_option("--version") 1337 1338 self.assertTrue(v_opt is version_opt) 1339 self.assertTrue(v_opt is not verbose_opt) 1340 self.assertEqual(v_opt._long_opts, ["--version"]) 1341 self.assertEqual(version_opt._short_opts, ["-v"]) 1342 self.assertEqual(version_opt._long_opts, ["--version"]) 1343 self.assertEqual(verbose_opt._short_opts, []) 1344 self.assertEqual(verbose_opt._long_opts, ["--verbose"]) 1345 1346 def test_conflict_resolve_help(self): 1347 self.assertOutput(["-h"], """\ 1348 Options: 1349 --verbose increment verbosity 1350 -h, --help show this help message and exit 1351 -v, --version show version 1352 """) 1353 1354 def test_conflict_resolve_short_opt(self): 1355 self.assertParseOK(["-v"], 1356 {'verbose': None, 'show_version': 1}, 1357 []) 1358 1359 def test_conflict_resolve_long_opt(self): 1360 self.assertParseOK(["--verbose"], 1361 {'verbose': 1}, 1362 []) 1363 1364 def test_conflict_resolve_long_opts(self): 1365 self.assertParseOK(["--verbose", "--version"], 1366 {'verbose': 1, 'show_version': 1}, 1367 []) 1368 1369 class TestConflictOverride(BaseTest): 1370 def setUp(self): 1371 self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE) 1372 self.parser.set_conflict_handler("resolve") 1373 self.parser.add_option("-n", "--dry-run", 1374 action="store_true", dest="dry_run", 1375 help="don't do anything") 1376 self.parser.add_option("--dry-run", "-n", 1377 action="store_const", const=42, dest="dry_run", 1378 help="dry run mode") 1379 1380 def test_conflict_override_opts(self): 1381 opt = self.parser.get_option("--dry-run") 1382 self.assertEqual(opt._short_opts, ["-n"]) 1383 self.assertEqual(opt._long_opts, ["--dry-run"]) 1384 1385 def test_conflict_override_help(self): 1386 self.assertOutput(["-h"], """\ 1387 Options: 1388 -h, --help show this help message and exit 1389 -n, --dry-run dry run mode 1390 """) 1391 1392 def test_conflict_override_args(self): 1393 self.assertParseOK(["-n"], 1394 {'dry_run': 42}, 1395 []) 1396 1397 # -- Other testing. ---------------------------------------------------- 1398 1399 _expected_help_basic = """\ 1400 Usage: bar.py [options] 1401 1402 Options: 1403 -a APPLE throw APPLEs at basket 1404 -b NUM, --boo=NUM shout "boo!" NUM times (in order to frighten away all the 1405 evil spirits that cause trouble and mayhem) 1406 --foo=FOO store FOO in the foo list for later fooing 1407 -h, --help show this help message and exit 1408 """ 1409 1410 _expected_help_long_opts_first = """\ 1411 Usage: bar.py [options] 1412 1413 Options: 1414 -a APPLE throw APPLEs at basket 1415 --boo=NUM, -b NUM shout "boo!" NUM times (in order to frighten away all the 1416 evil spirits that cause trouble and mayhem) 1417 --foo=FOO store FOO in the foo list for later fooing 1418 --help, -h show this help message and exit 1419 """ 1420 1421 _expected_help_title_formatter = """\ 1422 Usage 1423 ===== 1424 bar.py [options] 1425 1426 Options 1427 ======= 1428 -a APPLE throw APPLEs at basket 1429 --boo=NUM, -b NUM shout "boo!" NUM times (in order to frighten away all the 1430 evil spirits that cause trouble and mayhem) 1431 --foo=FOO store FOO in the foo list for later fooing 1432 --help, -h show this help message and exit 1433 """ 1434 1435 _expected_help_short_lines = """\ 1436 Usage: bar.py [options] 1437 1438 Options: 1439 -a APPLE throw APPLEs at basket 1440 -b NUM, --boo=NUM shout "boo!" NUM times (in order to 1441 frighten away all the evil spirits 1442 that cause trouble and mayhem) 1443 --foo=FOO store FOO in the foo list for later 1444 fooing 1445 -h, --help show this help message and exit 1446 """ 1447 1448 _expected_very_help_short_lines = """\ 1449 Usage: bar.py [options] 1450 1451 Options: 1452 -a APPLE 1453 throw 1454 APPLEs at 1455 basket 1456 -b NUM, --boo=NUM 1457 shout 1458 "boo!" NUM 1459 times (in 1460 order to 1461 frighten 1462 away all 1463 the evil 1464 spirits 1465 that cause 1466 trouble and 1467 mayhem) 1468 --foo=FOO 1469 store FOO 1470 in the foo 1471 list for 1472 later 1473 fooing 1474 -h, --help 1475 show this 1476 help 1477 message and 1478 exit 1479 """ 1480 1481 class TestHelp(BaseTest): 1482 def setUp(self): 1483 self.parser = self.make_parser(80) 1484 1485 def make_parser(self, columns): 1486 options = [ 1487 make_option("-a", type="string", dest='a', 1488 metavar="APPLE", help="throw APPLEs at basket"), 1489 make_option("-b", "--boo", type="int", dest='boo', 1490 metavar="NUM", 1491 help= 1492 "shout \"boo!\" NUM times (in order to frighten away " 1493 "all the evil spirits that cause trouble and mayhem)"), 1494 make_option("--foo", action="append", type="string", dest='foo', 1495 help="store FOO in the foo list for later fooing"), 1496 ] 1497 1498 # We need to set COLUMNS for the OptionParser constructor, but 1499 # we must restore its original value -- otherwise, this test 1500 # screws things up for other tests when it's part of the Python 1501 # test suite. 1502 with support.EnvironmentVarGuard() as env: 1503 env['COLUMNS'] = str(columns) 1504 return InterceptingOptionParser(option_list=options) 1505 1506 def assertHelpEquals(self, expected_output): 1507 save_argv = sys.argv[:] 1508 try: 1509 # Make optparse believe bar.py is being executed. 1510 sys.argv[0] = os.path.join("foo", "bar.py") 1511 self.assertOutput(["-h"], expected_output) 1512 finally: 1513 sys.argv[:] = save_argv 1514 1515 def test_help(self): 1516 self.assertHelpEquals(_expected_help_basic) 1517 1518 def test_help_old_usage(self): 1519 self.parser.set_usage("Usage: %prog [options]") 1520 self.assertHelpEquals(_expected_help_basic) 1521 1522 def test_help_long_opts_first(self): 1523 self.parser.formatter.short_first = 0 1524 self.assertHelpEquals(_expected_help_long_opts_first) 1525 1526 def test_help_title_formatter(self): 1527 with support.EnvironmentVarGuard() as env: 1528 env["COLUMNS"] = "80" 1529 self.parser.formatter = TitledHelpFormatter() 1530 self.assertHelpEquals(_expected_help_title_formatter) 1531 1532 def test_wrap_columns(self): 1533 # Ensure that wrapping respects $COLUMNS environment variable. 1534 # Need to reconstruct the parser, since that's the only time 1535 # we look at $COLUMNS. 1536 self.parser = self.make_parser(60) 1537 self.assertHelpEquals(_expected_help_short_lines) 1538 self.parser = self.make_parser(0) 1539 self.assertHelpEquals(_expected_very_help_short_lines) 1540 1541 def test_help_unicode(self): 1542 self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE) 1543 self.parser.add_option("-a", action="store_true", help="ol\u00E9!") 1544 expect = """\ 1545 Options: 1546 -h, --help show this help message and exit 1547 -a ol\u00E9! 1548 """ 1549 self.assertHelpEquals(expect) 1550 1551 def test_help_unicode_description(self): 1552 self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE, 1553 description="ol\u00E9!") 1554 expect = """\ 1555 ol\u00E9! 1556 1557 Options: 1558 -h, --help show this help message and exit 1559 """ 1560 self.assertHelpEquals(expect) 1561 1562 def test_help_description_groups(self): 1563 self.parser.set_description( 1564 "This is the program description for %prog. %prog has " 1565 "an option group as well as single options.") 1566 1567 group = OptionGroup( 1568 self.parser, "Dangerous Options", 1569 "Caution: use of these options is at your own risk. " 1570 "It is believed that some of them bite.") 1571 group.add_option("-g", action="store_true", help="Group option.") 1572 self.parser.add_option_group(group) 1573 1574 expect = """\ 1575 Usage: bar.py [options] 1576 1577 This is the program description for bar.py. bar.py has an option group as 1578 well as single options. 1579 1580 Options: 1581 -a APPLE throw APPLEs at basket 1582 -b NUM, --boo=NUM shout "boo!" NUM times (in order to frighten away all the 1583 evil spirits that cause trouble and mayhem) 1584 --foo=FOO store FOO in the foo list for later fooing 1585 -h, --help show this help message and exit 1586 1587 Dangerous Options: 1588 Caution: use of these options is at your own risk. It is believed 1589 that some of them bite. 1590 1591 -g Group option. 1592 """ 1593 1594 self.assertHelpEquals(expect) 1595 1596 self.parser.epilog = "Please report bugs to /dev/null." 1597 self.assertHelpEquals(expect + "\nPlease report bugs to /dev/null.\n") 1598 1599 1600 class TestMatchAbbrev(BaseTest): 1601 def test_match_abbrev(self): 1602 self.assertEqual(_match_abbrev("--f", 1603 {"--foz": None, 1604 "--foo": None, 1605 "--fie": None, 1606 "--f": None}), 1607 "--f") 1608 1609 def test_match_abbrev_error(self): 1610 s = "--f" 1611 wordmap = {"--foz": None, "--foo": None, "--fie": None} 1612 self.assertRaises( 1613 _match_abbrev, (s, wordmap), None, 1614 BadOptionError, "ambiguous option: --f (--fie, --foo, --foz?)") 1615 1616 1617 class TestParseNumber(BaseTest): 1618 def setUp(self): 1619 self.parser = InterceptingOptionParser() 1620 self.parser.add_option("-n", type=int) 1621 self.parser.add_option("-l", type=int) 1622 1623 def test_parse_num_fail(self): 1624 self.assertRaises( 1625 _parse_num, ("", int), {}, 1626 ValueError, 1627 re.compile(r"invalid literal for int().*: '?'?")) 1628 self.assertRaises( 1629 _parse_num, ("0xOoops", int), {}, 1630 ValueError, 1631 re.compile(r"invalid literal for int().*: s?'?0xOoops'?")) 1632 1633 def test_parse_num_ok(self): 1634 self.assertEqual(_parse_num("0", int), 0) 1635 self.assertEqual(_parse_num("0x10", int), 16) 1636 self.assertEqual(_parse_num("0XA", int), 10) 1637 self.assertEqual(_parse_num("010", int), 8) 1638 self.assertEqual(_parse_num("0b11", int), 3) 1639 self.assertEqual(_parse_num("0b", int), 0) 1640 1641 def test_numeric_options(self): 1642 self.assertParseOK(["-n", "42", "-l", "0x20"], 1643 { "n": 42, "l": 0x20 }, []) 1644 self.assertParseOK(["-n", "0b0101", "-l010"], 1645 { "n": 5, "l": 8 }, []) 1646 self.assertParseFail(["-n008"], 1647 "option -n: invalid integer value: '008'") 1648 self.assertParseFail(["-l0b0123"], 1649 "option -l: invalid integer value: '0b0123'") 1650 self.assertParseFail(["-l", "0x12x"], 1651 "option -l: invalid integer value: '0x12x'") 1652 1653 1654 class MiscTestCase(unittest.TestCase): 1655 def test__all__(self): 1656 blacklist = {'check_builtin', 'AmbiguousOptionError', 'NO_DEFAULT'} 1657 support.check__all__(self, optparse, blacklist=blacklist) 1658 1659 1660 def test_main(): 1661 support.run_unittest(__name__) 1662 1663 if __name__ == '__main__': 1664 test_main() 1665