1# 2# Test suite for Optik. Supplied by Johannes Gijsbers 3# (taradino@softhome.net) -- translated from the original Optik 4# test suite to this PyUnit-based version. 5# 6# $Id$ 7# 8 9import sys 10import os 11import re 12import copy 13import types 14import unittest 15 16from StringIO import StringIO 17from test import test_support 18 19 20from optparse import make_option, Option, \ 21 TitledHelpFormatter, OptionParser, OptionGroup, \ 22 SUPPRESS_USAGE, OptionError, OptionConflictError, \ 23 BadOptionError, OptionValueError, Values 24from optparse import _match_abbrev 25from optparse import _parse_num 26 27retype = type(re.compile('')) 28 29class 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 41class 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 49class 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 """ 68Options are %(optdict)s. 69Should be %(expected_opts)s. 70Args were %(args)s.""" % locals()) 71 72 self.assertEqual(positional_args, expected_positional_args, 73 """ 74Positional arguments are %(positional_args)s. 75Should be %(expected_positional_args)s. 76Args 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, err: 109 actual_message = str(err) 110 if isinstance(expected_message, retype): 111 self.assertTrue(expected_message.search(actual_message), 112 """\ 113expected exception message pattern: 114/%s/ 115actual exception message: 116'''%s''' 117""" % (expected_message.pattern, actual_message)) 118 else: 119 self.assertEqual(actual_message, 120 expected_message, 121 """\ 122expected exception message: 123'''%s''' 124actual 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 131called %(func)r 132with args %(args)r 133and 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, 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 encoding = getattr(save_stdout, 'encoding', None) 159 try: 160 try: 161 sys.stdout = StringIO() 162 if encoding: 163 sys.stdout.encoding = encoding 164 self.parser.parse_args(cmdline_args) 165 finally: 166 output = sys.stdout.getvalue() 167 sys.stdout = save_stdout 168 169 except InterceptedError, err: 170 self.assertTrue( 171 type(output) is types.StringType, 172 "expected output to be an ordinary string, not %r" 173 % type(output)) 174 175 if output != expected_output: 176 self.fail("expected: \n'''\n" + expected_output + 177 "'''\nbut got \n'''\n" + output + "'''") 178 self.assertEqual(err.exit_status, expected_status) 179 self.assertEqual(err.exit_message, expected_error) 180 else: 181 self.assertFalse("expected parser.exit()") 182 183 def assertTypeError(self, func, expected_message, *args): 184 """Assert that TypeError is raised when executing func.""" 185 self.assertRaises(func, args, None, TypeError, expected_message) 186 187 def assertHelp(self, parser, expected_help): 188 actual_help = parser.format_help() 189 if actual_help != expected_help: 190 raise self.failureException( 191 'help text failure; expected:\n"' + 192 expected_help + '"; got:\n"' + 193 actual_help + '"\n') 194 195# -- Test make_option() aka Option ------------------------------------- 196 197# It's not necessary to test correct options here. All the tests in the 198# parser.parse_args() section deal with those, because they're needed 199# there. 200 201class TestOptionChecks(BaseTest): 202 def setUp(self): 203 self.parser = OptionParser(usage=SUPPRESS_USAGE) 204 205 def assertOptionError(self, expected_message, args=[], kwargs={}): 206 self.assertRaises(make_option, args, kwargs, 207 OptionError, expected_message) 208 209 def test_opt_string_empty(self): 210 self.assertTypeError(make_option, 211 "at least one option string must be supplied") 212 213 def test_opt_string_too_short(self): 214 self.assertOptionError( 215 "invalid option string 'b': must be at least two characters long", 216 ["b"]) 217 218 def test_opt_string_short_invalid(self): 219 self.assertOptionError( 220 "invalid short option string '--': must be " 221 "of the form -x, (x any non-dash char)", 222 ["--"]) 223 224 def test_opt_string_long_invalid(self): 225 self.assertOptionError( 226 "invalid long option string '---': " 227 "must start with --, followed by non-dash", 228 ["---"]) 229 230 def test_attr_invalid(self): 231 self.assertOptionError( 232 "option -b: invalid keyword arguments: bar, foo", 233 ["-b"], {'foo': None, 'bar': None}) 234 235 def test_action_invalid(self): 236 self.assertOptionError( 237 "option -b: invalid action: 'foo'", 238 ["-b"], {'action': 'foo'}) 239 240 def test_type_invalid(self): 241 self.assertOptionError( 242 "option -b: invalid option type: 'foo'", 243 ["-b"], {'type': 'foo'}) 244 self.assertOptionError( 245 "option -b: invalid option type: 'tuple'", 246 ["-b"], {'type': tuple}) 247 248 def test_no_type_for_action(self): 249 self.assertOptionError( 250 "option -b: must not supply a type for action 'count'", 251 ["-b"], {'action': 'count', 'type': 'int'}) 252 253 def test_no_choices_list(self): 254 self.assertOptionError( 255 "option -b/--bad: must supply a list of " 256 "choices for type 'choice'", 257 ["-b", "--bad"], {'type': "choice"}) 258 259 def test_bad_choices_list(self): 260 typename = type('').__name__ 261 self.assertOptionError( 262 "option -b/--bad: choices must be a list of " 263 "strings ('%s' supplied)" % typename, 264 ["-b", "--bad"], 265 {'type': "choice", 'choices':"bad choices"}) 266 267 def test_no_choices_for_type(self): 268 self.assertOptionError( 269 "option -b: must not supply choices for type 'int'", 270 ["-b"], {'type': 'int', 'choices':"bad"}) 271 272 def test_no_const_for_action(self): 273 self.assertOptionError( 274 "option -b: 'const' must not be supplied for action 'store'", 275 ["-b"], {'action': 'store', 'const': 1}) 276 277 def test_no_nargs_for_action(self): 278 self.assertOptionError( 279 "option -b: 'nargs' must not be supplied for action 'count'", 280 ["-b"], {'action': 'count', 'nargs': 2}) 281 282 def test_callback_not_callable(self): 283 self.assertOptionError( 284 "option -b: callback not callable: 'foo'", 285 ["-b"], {'action': 'callback', 286 'callback': 'foo'}) 287 288 def dummy(self): 289 pass 290 291 def test_callback_args_no_tuple(self): 292 self.assertOptionError( 293 "option -b: callback_args, if supplied, " 294 "must be a tuple: not 'foo'", 295 ["-b"], {'action': 'callback', 296 'callback': self.dummy, 297 'callback_args': 'foo'}) 298 299 def test_callback_kwargs_no_dict(self): 300 self.assertOptionError( 301 "option -b: callback_kwargs, if supplied, " 302 "must be a dict: not 'foo'", 303 ["-b"], {'action': 'callback', 304 'callback': self.dummy, 305 'callback_kwargs': 'foo'}) 306 307 def test_no_callback_for_action(self): 308 self.assertOptionError( 309 "option -b: callback supplied ('foo') for non-callback option", 310 ["-b"], {'action': 'store', 311 'callback': 'foo'}) 312 313 def test_no_callback_args_for_action(self): 314 self.assertOptionError( 315 "option -b: callback_args supplied for non-callback option", 316 ["-b"], {'action': 'store', 317 'callback_args': 'foo'}) 318 319 def test_no_callback_kwargs_for_action(self): 320 self.assertOptionError( 321 "option -b: callback_kwargs supplied for non-callback option", 322 ["-b"], {'action': 'store', 323 'callback_kwargs': 'foo'}) 324 325class TestOptionParser(BaseTest): 326 def setUp(self): 327 self.parser = OptionParser() 328 self.parser.add_option("-v", "--verbose", "-n", "--noisy", 329 action="store_true", dest="verbose") 330 self.parser.add_option("-q", "--quiet", "--silent", 331 action="store_false", dest="verbose") 332 333 def test_add_option_no_Option(self): 334 self.assertTypeError(self.parser.add_option, 335 "not an Option instance: None", None) 336 337 def test_add_option_invalid_arguments(self): 338 self.assertTypeError(self.parser.add_option, 339 "invalid arguments", None, None) 340 341 def test_get_option(self): 342 opt1 = self.parser.get_option("-v") 343 self.assertIsInstance(opt1, Option) 344 self.assertEqual(opt1._short_opts, ["-v", "-n"]) 345 self.assertEqual(opt1._long_opts, ["--verbose", "--noisy"]) 346 self.assertEqual(opt1.action, "store_true") 347 self.assertEqual(opt1.dest, "verbose") 348 349 def test_get_option_equals(self): 350 opt1 = self.parser.get_option("-v") 351 opt2 = self.parser.get_option("--verbose") 352 opt3 = self.parser.get_option("-n") 353 opt4 = self.parser.get_option("--noisy") 354 self.assertTrue(opt1 is opt2 is opt3 is opt4) 355 356 def test_has_option(self): 357 self.assertTrue(self.parser.has_option("-v")) 358 self.assertTrue(self.parser.has_option("--verbose")) 359 360 def assertTrueremoved(self): 361 self.assertTrue(self.parser.get_option("-v") is None) 362 self.assertTrue(self.parser.get_option("--verbose") is None) 363 self.assertTrue(self.parser.get_option("-n") is None) 364 self.assertTrue(self.parser.get_option("--noisy") is None) 365 366 self.assertFalse(self.parser.has_option("-v")) 367 self.assertFalse(self.parser.has_option("--verbose")) 368 self.assertFalse(self.parser.has_option("-n")) 369 self.assertFalse(self.parser.has_option("--noisy")) 370 371 self.assertTrue(self.parser.has_option("-q")) 372 self.assertTrue(self.parser.has_option("--silent")) 373 374 def test_remove_short_opt(self): 375 self.parser.remove_option("-n") 376 self.assertTrueremoved() 377 378 def test_remove_long_opt(self): 379 self.parser.remove_option("--verbose") 380 self.assertTrueremoved() 381 382 def test_remove_nonexistent(self): 383 self.assertRaises(self.parser.remove_option, ('foo',), None, 384 ValueError, "no such option 'foo'") 385 386 @test_support.impl_detail('Relies on sys.getrefcount', cpython=True) 387 def test_refleak(self): 388 # If an OptionParser is carrying around a reference to a large 389 # object, various cycles can prevent it from being GC'd in 390 # a timely fashion. destroy() breaks the cycles to ensure stuff 391 # can be cleaned up. 392 big_thing = [42] 393 refcount = sys.getrefcount(big_thing) 394 parser = OptionParser() 395 parser.add_option("-a", "--aaarggh") 396 parser.big_thing = big_thing 397 398 parser.destroy() 399 #self.assertEqual(refcount, sys.getrefcount(big_thing)) 400 del parser 401 self.assertEqual(refcount, sys.getrefcount(big_thing)) 402 403 404class TestOptionValues(BaseTest): 405 def setUp(self): 406 pass 407 408 def test_basics(self): 409 values = Values() 410 self.assertEqual(vars(values), {}) 411 self.assertEqual(values, {}) 412 self.assertNotEqual(values, {"foo": "bar"}) 413 self.assertNotEqual(values, "") 414 415 dict = {"foo": "bar", "baz": 42} 416 values = Values(defaults=dict) 417 self.assertEqual(vars(values), dict) 418 self.assertEqual(values, dict) 419 self.assertNotEqual(values, {"foo": "bar"}) 420 self.assertNotEqual(values, {}) 421 self.assertNotEqual(values, "") 422 self.assertNotEqual(values, []) 423 424 425class TestTypeAliases(BaseTest): 426 def setUp(self): 427 self.parser = OptionParser() 428 429 def test_str_aliases_string(self): 430 self.parser.add_option("-s", type="str") 431 self.assertEqual(self.parser.get_option("-s").type, "string") 432 433 def test_new_type_object(self): 434 self.parser.add_option("-s", type=str) 435 self.assertEqual(self.parser.get_option("-s").type, "string") 436 self.parser.add_option("-x", type=int) 437 self.assertEqual(self.parser.get_option("-x").type, "int") 438 439 def test_old_type_object(self): 440 self.parser.add_option("-s", type=types.StringType) 441 self.assertEqual(self.parser.get_option("-s").type, "string") 442 self.parser.add_option("-x", type=types.IntType) 443 self.assertEqual(self.parser.get_option("-x").type, "int") 444 445 446# Custom type for testing processing of default values. 447_time_units = { 's' : 1, 'm' : 60, 'h' : 60*60, 'd' : 60*60*24 } 448 449def _check_duration(option, opt, value): 450 try: 451 if value[-1].isdigit(): 452 return int(value) 453 else: 454 return int(value[:-1]) * _time_units[value[-1]] 455 except (ValueError, IndexError): 456 raise OptionValueError( 457 'option %s: invalid duration: %r' % (opt, value)) 458 459class DurationOption(Option): 460 TYPES = Option.TYPES + ('duration',) 461 TYPE_CHECKER = copy.copy(Option.TYPE_CHECKER) 462 TYPE_CHECKER['duration'] = _check_duration 463 464class TestDefaultValues(BaseTest): 465 def setUp(self): 466 self.parser = OptionParser() 467 self.parser.add_option("-v", "--verbose", default=True) 468 self.parser.add_option("-q", "--quiet", dest='verbose') 469 self.parser.add_option("-n", type="int", default=37) 470 self.parser.add_option("-m", type="int") 471 self.parser.add_option("-s", default="foo") 472 self.parser.add_option("-t") 473 self.parser.add_option("-u", default=None) 474 self.expected = { 'verbose': True, 475 'n': 37, 476 'm': None, 477 's': "foo", 478 't': None, 479 'u': None } 480 481 def test_basic_defaults(self): 482 self.assertEqual(self.parser.get_default_values(), self.expected) 483 484 def test_mixed_defaults_post(self): 485 self.parser.set_defaults(n=42, m=-100) 486 self.expected.update({'n': 42, 'm': -100}) 487 self.assertEqual(self.parser.get_default_values(), self.expected) 488 489 def test_mixed_defaults_pre(self): 490 self.parser.set_defaults(x="barf", y="blah") 491 self.parser.add_option("-x", default="frob") 492 self.parser.add_option("-y") 493 494 self.expected.update({'x': "frob", 'y': "blah"}) 495 self.assertEqual(self.parser.get_default_values(), self.expected) 496 497 self.parser.remove_option("-y") 498 self.parser.add_option("-y", default=None) 499 self.expected.update({'y': None}) 500 self.assertEqual(self.parser.get_default_values(), self.expected) 501 502 def test_process_default(self): 503 self.parser.option_class = DurationOption 504 self.parser.add_option("-d", type="duration", default=300) 505 self.parser.add_option("-e", type="duration", default="6m") 506 self.parser.set_defaults(n="42") 507 self.expected.update({'d': 300, 'e': 360, 'n': 42}) 508 self.assertEqual(self.parser.get_default_values(), self.expected) 509 510 self.parser.set_process_default_values(False) 511 self.expected.update({'d': 300, 'e': "6m", 'n': "42"}) 512 self.assertEqual(self.parser.get_default_values(), self.expected) 513 514 515class TestProgName(BaseTest): 516 """ 517 Test that %prog expands to the right thing in usage, version, 518 and help strings. 519 """ 520 521 def assertUsage(self, parser, expected_usage): 522 self.assertEqual(parser.get_usage(), expected_usage) 523 524 def assertVersion(self, parser, expected_version): 525 self.assertEqual(parser.get_version(), expected_version) 526 527 528 def test_default_progname(self): 529 # Make sure that program name taken from sys.argv[0] by default. 530 save_argv = sys.argv[:] 531 try: 532 sys.argv[0] = os.path.join("foo", "bar", "baz.py") 533 parser = OptionParser("%prog ...", version="%prog 1.2") 534 expected_usage = "Usage: baz.py ...\n" 535 self.assertUsage(parser, expected_usage) 536 self.assertVersion(parser, "baz.py 1.2") 537 self.assertHelp(parser, 538 expected_usage + "\n" + 539 "Options:\n" 540 " --version show program's version number and exit\n" 541 " -h, --help show this help message and exit\n") 542 finally: 543 sys.argv[:] = save_argv 544 545 def test_custom_progname(self): 546 parser = OptionParser(prog="thingy", 547 version="%prog 0.1", 548 usage="%prog arg arg") 549 parser.remove_option("-h") 550 parser.remove_option("--version") 551 expected_usage = "Usage: thingy arg arg\n" 552 self.assertUsage(parser, expected_usage) 553 self.assertVersion(parser, "thingy 0.1") 554 self.assertHelp(parser, expected_usage + "\n") 555 556 557class TestExpandDefaults(BaseTest): 558 def setUp(self): 559 self.parser = OptionParser(prog="test") 560 self.help_prefix = """\ 561Usage: test [options] 562 563Options: 564 -h, --help show this help message and exit 565""" 566 self.file_help = "read from FILE [default: %default]" 567 self.expected_help_file = self.help_prefix + \ 568 " -f FILE, --file=FILE read from FILE [default: foo.txt]\n" 569 self.expected_help_none = self.help_prefix + \ 570 " -f FILE, --file=FILE read from FILE [default: none]\n" 571 572 def test_option_default(self): 573 self.parser.add_option("-f", "--file", 574 default="foo.txt", 575 help=self.file_help) 576 self.assertHelp(self.parser, self.expected_help_file) 577 578 def test_parser_default_1(self): 579 self.parser.add_option("-f", "--file", 580 help=self.file_help) 581 self.parser.set_default('file', "foo.txt") 582 self.assertHelp(self.parser, self.expected_help_file) 583 584 def test_parser_default_2(self): 585 self.parser.add_option("-f", "--file", 586 help=self.file_help) 587 self.parser.set_defaults(file="foo.txt") 588 self.assertHelp(self.parser, self.expected_help_file) 589 590 def test_no_default(self): 591 self.parser.add_option("-f", "--file", 592 help=self.file_help) 593 self.assertHelp(self.parser, self.expected_help_none) 594 595 def test_default_none_1(self): 596 self.parser.add_option("-f", "--file", 597 default=None, 598 help=self.file_help) 599 self.assertHelp(self.parser, self.expected_help_none) 600 601 def test_default_none_2(self): 602 self.parser.add_option("-f", "--file", 603 help=self.file_help) 604 self.parser.set_defaults(file=None) 605 self.assertHelp(self.parser, self.expected_help_none) 606 607 def test_float_default(self): 608 self.parser.add_option( 609 "-p", "--prob", 610 help="blow up with probability PROB [default: %default]") 611 self.parser.set_defaults(prob=0.43) 612 expected_help = self.help_prefix + \ 613 " -p PROB, --prob=PROB blow up with probability PROB [default: 0.43]\n" 614 self.assertHelp(self.parser, expected_help) 615 616 def test_alt_expand(self): 617 self.parser.add_option("-f", "--file", 618 default="foo.txt", 619 help="read from FILE [default: *DEFAULT*]") 620 self.parser.formatter.default_tag = "*DEFAULT*" 621 self.assertHelp(self.parser, self.expected_help_file) 622 623 def test_no_expand(self): 624 self.parser.add_option("-f", "--file", 625 default="foo.txt", 626 help="read from %default file") 627 self.parser.formatter.default_tag = None 628 expected_help = self.help_prefix + \ 629 " -f FILE, --file=FILE read from %default file\n" 630 self.assertHelp(self.parser, expected_help) 631 632 633# -- Test parser.parse_args() ------------------------------------------ 634 635class TestStandard(BaseTest): 636 def setUp(self): 637 options = [make_option("-a", type="string"), 638 make_option("-b", "--boo", type="int", dest='boo'), 639 make_option("--foo", action="append")] 640 641 self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE, 642 option_list=options) 643 644 def test_required_value(self): 645 self.assertParseFail(["-a"], "-a option requires an argument") 646 647 def test_invalid_integer(self): 648 self.assertParseFail(["-b", "5x"], 649 "option -b: invalid integer value: '5x'") 650 651 def test_no_such_option(self): 652 self.assertParseFail(["--boo13"], "no such option: --boo13") 653 654 def test_long_invalid_integer(self): 655 self.assertParseFail(["--boo=x5"], 656 "option --boo: invalid integer value: 'x5'") 657 658 def test_empty(self): 659 self.assertParseOK([], {'a': None, 'boo': None, 'foo': None}, []) 660 661 def test_shortopt_empty_longopt_append(self): 662 self.assertParseOK(["-a", "", "--foo=blah", "--foo="], 663 {'a': "", 'boo': None, 'foo': ["blah", ""]}, 664 []) 665 666 def test_long_option_append(self): 667 self.assertParseOK(["--foo", "bar", "--foo", "", "--foo=x"], 668 {'a': None, 669 'boo': None, 670 'foo': ["bar", "", "x"]}, 671 []) 672 673 def test_option_argument_joined(self): 674 self.assertParseOK(["-abc"], 675 {'a': "bc", 'boo': None, 'foo': None}, 676 []) 677 678 def test_option_argument_split(self): 679 self.assertParseOK(["-a", "34"], 680 {'a': "34", 'boo': None, 'foo': None}, 681 []) 682 683 def test_option_argument_joined_integer(self): 684 self.assertParseOK(["-b34"], 685 {'a': None, 'boo': 34, 'foo': None}, 686 []) 687 688 def test_option_argument_split_negative_integer(self): 689 self.assertParseOK(["-b", "-5"], 690 {'a': None, 'boo': -5, 'foo': None}, 691 []) 692 693 def test_long_option_argument_joined(self): 694 self.assertParseOK(["--boo=13"], 695 {'a': None, 'boo': 13, 'foo': None}, 696 []) 697 698 def test_long_option_argument_split(self): 699 self.assertParseOK(["--boo", "111"], 700 {'a': None, 'boo': 111, 'foo': None}, 701 []) 702 703 def test_long_option_short_option(self): 704 self.assertParseOK(["--foo=bar", "-axyz"], 705 {'a': 'xyz', 'boo': None, 'foo': ["bar"]}, 706 []) 707 708 def test_abbrev_long_option(self): 709 self.assertParseOK(["--f=bar", "-axyz"], 710 {'a': 'xyz', 'boo': None, 'foo': ["bar"]}, 711 []) 712 713 def test_defaults(self): 714 (options, args) = self.parser.parse_args([]) 715 defaults = self.parser.get_default_values() 716 self.assertEqual(vars(defaults), vars(options)) 717 718 def test_ambiguous_option(self): 719 self.parser.add_option("--foz", action="store", 720 type="string", dest="foo") 721 self.assertParseFail(["--f=bar"], 722 "ambiguous option: --f (--foo, --foz?)") 723 724 725 def test_short_and_long_option_split(self): 726 self.assertParseOK(["-a", "xyz", "--foo", "bar"], 727 {'a': 'xyz', 'boo': None, 'foo': ["bar"]}, 728 []), 729 730 def test_short_option_split_long_option_append(self): 731 self.assertParseOK(["--foo=bar", "-b", "123", "--foo", "baz"], 732 {'a': None, 'boo': 123, 'foo': ["bar", "baz"]}, 733 []) 734 735 def test_short_option_split_one_positional_arg(self): 736 self.assertParseOK(["-a", "foo", "bar"], 737 {'a': "foo", 'boo': None, 'foo': None}, 738 ["bar"]), 739 740 def test_short_option_consumes_separator(self): 741 self.assertParseOK(["-a", "--", "foo", "bar"], 742 {'a': "--", 'boo': None, 'foo': None}, 743 ["foo", "bar"]), 744 self.assertParseOK(["-a", "--", "--foo", "bar"], 745 {'a': "--", 'boo': None, 'foo': ["bar"]}, 746 []), 747 748 def test_short_option_joined_and_separator(self): 749 self.assertParseOK(["-ab", "--", "--foo", "bar"], 750 {'a': "b", 'boo': None, 'foo': None}, 751 ["--foo", "bar"]), 752 753 def test_hyphen_becomes_positional_arg(self): 754 self.assertParseOK(["-ab", "-", "--foo", "bar"], 755 {'a': "b", 'boo': None, 'foo': ["bar"]}, 756 ["-"]) 757 758 def test_no_append_versus_append(self): 759 self.assertParseOK(["-b3", "-b", "5", "--foo=bar", "--foo", "baz"], 760 {'a': None, 'boo': 5, 'foo': ["bar", "baz"]}, 761 []) 762 763 def test_option_consumes_optionlike_string(self): 764 self.assertParseOK(["-a", "-b3"], 765 {'a': "-b3", 'boo': None, 'foo': None}, 766 []) 767 768 def test_combined_single_invalid_option(self): 769 self.parser.add_option("-t", action="store_true") 770 self.assertParseFail(["-test"], 771 "no such option: -e") 772 773 def test_add_option_accepts_unicode(self): 774 self.parser.add_option(u"-u", u"--unicode", action="store_true") 775 self.assertParseOK(["-u"], 776 {'a': None, 'boo': None, 'foo': None, 'unicode': True}, 777 []) 778 779 780class 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 815class 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 838class 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 896class 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 921class 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 950class 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 968class 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 984class 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 1018class 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(test_support.TESTFN): 1027 os.rmdir(test_support.TESTFN) 1028 elif os.path.isfile(test_support.TESTFN): 1029 os.unlink(test_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 open(test_support.TESTFN, "w").close() 1045 self.assertParseOK(["--file", test_support.TESTFN, "-afoo"], 1046 {'file': test_support.TESTFN, 'a': 'foo'}, 1047 []) 1048 1049 def test_filetype_noexist(self): 1050 self.assertParseFail(["--file", test_support.TESTFN, "-afoo"], 1051 "%s: file does not exist" % 1052 test_support.TESTFN) 1053 1054 def test_filetype_notfile(self): 1055 os.mkdir(test_support.TESTFN) 1056 self.assertParseFail(["--file", test_support.TESTFN, "-afoo"], 1057 "%s: not a regular file" % 1058 test_support.TESTFN) 1059 1060 1061class 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 1092class 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 1146class 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 1171class 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 1200class 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 1225class 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 1237class 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 1290class 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 1300class 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 1326class 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"], """\ 1348Options: 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 1369class 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"], """\ 1387Options: 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 = """\ 1400Usage: bar.py [options] 1401 1402Options: 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 = """\ 1411Usage: bar.py [options] 1412 1413Options: 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 = """\ 1422Usage 1423===== 1424 bar.py [options] 1425 1426Options 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 = """\ 1436Usage: bar.py [options] 1437 1438Options: 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 = """\ 1449Usage: bar.py [options] 1450 1451Options: 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 1481class 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 test_support.EnvironmentVarGuard() as env: 1503 env['COLUMNS'] = str(columns) 1504 return InterceptingOptionParser(option_list=options) 1505 1506 def assertHelpEquals(self, expected_output): 1507 if type(expected_output) is types.UnicodeType: 1508 encoding = self.parser._get_encoding(sys.stdout) 1509 expected_output = expected_output.encode(encoding, "replace") 1510 1511 save_argv = sys.argv[:] 1512 try: 1513 # Make optparse believe bar.py is being executed. 1514 sys.argv[0] = os.path.join("foo", "bar.py") 1515 self.assertOutput(["-h"], expected_output) 1516 finally: 1517 sys.argv[:] = save_argv 1518 1519 def test_help(self): 1520 self.assertHelpEquals(_expected_help_basic) 1521 1522 def test_help_old_usage(self): 1523 self.parser.set_usage("Usage: %prog [options]") 1524 self.assertHelpEquals(_expected_help_basic) 1525 1526 def test_help_long_opts_first(self): 1527 self.parser.formatter.short_first = 0 1528 self.assertHelpEquals(_expected_help_long_opts_first) 1529 1530 def test_help_title_formatter(self): 1531 with test_support.EnvironmentVarGuard() as env: 1532 env["COLUMNS"] = "80" 1533 self.parser.formatter = TitledHelpFormatter() 1534 self.assertHelpEquals(_expected_help_title_formatter) 1535 1536 def test_wrap_columns(self): 1537 # Ensure that wrapping respects $COLUMNS environment variable. 1538 # Need to reconstruct the parser, since that's the only time 1539 # we look at $COLUMNS. 1540 self.parser = self.make_parser(60) 1541 self.assertHelpEquals(_expected_help_short_lines) 1542 self.parser = self.make_parser(0) 1543 self.assertHelpEquals(_expected_very_help_short_lines) 1544 1545 def test_help_unicode(self): 1546 self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE) 1547 self.parser.add_option("-a", action="store_true", help=u"ol\u00E9!") 1548 expect = u"""\ 1549Options: 1550 -h, --help show this help message and exit 1551 -a ol\u00E9! 1552""" 1553 self.assertHelpEquals(expect) 1554 1555 def test_help_unicode_description(self): 1556 self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE, 1557 description=u"ol\u00E9!") 1558 expect = u"""\ 1559ol\u00E9! 1560 1561Options: 1562 -h, --help show this help message and exit 1563""" 1564 self.assertHelpEquals(expect) 1565 1566 def test_help_description_groups(self): 1567 self.parser.set_description( 1568 "This is the program description for %prog. %prog has " 1569 "an option group as well as single options.") 1570 1571 group = OptionGroup( 1572 self.parser, "Dangerous Options", 1573 "Caution: use of these options is at your own risk. " 1574 "It is believed that some of them bite.") 1575 group.add_option("-g", action="store_true", help="Group option.") 1576 self.parser.add_option_group(group) 1577 1578 expect = """\ 1579Usage: bar.py [options] 1580 1581This is the program description for bar.py. bar.py has an option group as 1582well as single options. 1583 1584Options: 1585 -a APPLE throw APPLEs at basket 1586 -b NUM, --boo=NUM shout "boo!" NUM times (in order to frighten away all the 1587 evil spirits that cause trouble and mayhem) 1588 --foo=FOO store FOO in the foo list for later fooing 1589 -h, --help show this help message and exit 1590 1591 Dangerous Options: 1592 Caution: use of these options is at your own risk. It is believed 1593 that some of them bite. 1594 1595 -g Group option. 1596""" 1597 1598 self.assertHelpEquals(expect) 1599 1600 self.parser.epilog = "Please report bugs to /dev/null." 1601 self.assertHelpEquals(expect + "\nPlease report bugs to /dev/null.\n") 1602 1603 1604class TestMatchAbbrev(BaseTest): 1605 def test_match_abbrev(self): 1606 self.assertEqual(_match_abbrev("--f", 1607 {"--foz": None, 1608 "--foo": None, 1609 "--fie": None, 1610 "--f": None}), 1611 "--f") 1612 1613 def test_match_abbrev_error(self): 1614 s = "--f" 1615 wordmap = {"--foz": None, "--foo": None, "--fie": None} 1616 self.assertRaises( 1617 _match_abbrev, (s, wordmap), None, 1618 BadOptionError, "ambiguous option: --f (--fie, --foo, --foz?)") 1619 1620 1621class TestParseNumber(BaseTest): 1622 def setUp(self): 1623 self.parser = InterceptingOptionParser() 1624 self.parser.add_option("-n", type=int) 1625 self.parser.add_option("-l", type=long) 1626 1627 def test_parse_num_fail(self): 1628 self.assertRaises( 1629 _parse_num, ("", int), {}, 1630 ValueError, 1631 re.compile(r"invalid literal for int().*: '?'?")) 1632 self.assertRaises( 1633 _parse_num, ("0xOoops", long), {}, 1634 ValueError, 1635 re.compile(r"invalid literal for long().*: '?0xOoops'?")) 1636 1637 def test_parse_num_ok(self): 1638 self.assertEqual(_parse_num("0", int), 0) 1639 self.assertEqual(_parse_num("0x10", int), 16) 1640 self.assertEqual(_parse_num("0XA", long), 10L) 1641 self.assertEqual(_parse_num("010", long), 8L) 1642 self.assertEqual(_parse_num("0b11", int), 3) 1643 self.assertEqual(_parse_num("0b", long), 0L) 1644 1645 def test_numeric_options(self): 1646 self.assertParseOK(["-n", "42", "-l", "0x20"], 1647 { "n": 42, "l": 0x20 }, []) 1648 self.assertParseOK(["-n", "0b0101", "-l010"], 1649 { "n": 5, "l": 8 }, []) 1650 self.assertParseFail(["-n008"], 1651 "option -n: invalid integer value: '008'") 1652 self.assertParseFail(["-l0b0123"], 1653 "option -l: invalid long integer value: '0b0123'") 1654 self.assertParseFail(["-l", "0x12x"], 1655 "option -l: invalid long integer value: '0x12x'") 1656 1657 1658def test_main(): 1659 test_support.run_unittest(__name__) 1660 1661if __name__ == '__main__': 1662 test_main() 1663