1import os 2import re 3 4import pytest 5 6import click 7from click import Option 8 9 10def test_prefixes(runner): 11 @click.command() 12 @click.option("++foo", is_flag=True, help="das foo") 13 @click.option("--bar", is_flag=True, help="das bar") 14 def cli(foo, bar): 15 click.echo(f"foo={foo} bar={bar}") 16 17 result = runner.invoke(cli, ["++foo", "--bar"]) 18 assert not result.exception 19 assert result.output == "foo=True bar=True\n" 20 21 result = runner.invoke(cli, ["--help"]) 22 assert re.search(r"\+\+foo\s+das foo", result.output) is not None 23 assert re.search(r"--bar\s+das bar", result.output) is not None 24 25 26def test_invalid_option(runner): 27 with pytest.raises(TypeError, match="name was passed") as exc_info: 28 click.Option(["foo"]) 29 30 message = str(exc_info.value) 31 assert "name was passed (foo)" in message 32 assert "declare an argument" in message 33 assert "'--foo'" in message 34 35 36def test_invalid_nargs(runner): 37 with pytest.raises(TypeError, match="nargs=-1"): 38 39 @click.command() 40 @click.option("--foo", nargs=-1) 41 def cli(foo): 42 pass 43 44 45def test_nargs_tup_composite_mult(runner): 46 @click.command() 47 @click.option("--item", type=(str, int), multiple=True) 48 def copy(item): 49 for name, id in item: 50 click.echo(f"name={name} id={id:d}") 51 52 result = runner.invoke(copy, ["--item", "peter", "1", "--item", "max", "2"]) 53 assert not result.exception 54 assert result.output.splitlines() == ["name=peter id=1", "name=max id=2"] 55 56 57def test_counting(runner): 58 @click.command() 59 @click.option("-v", count=True, help="Verbosity", type=click.IntRange(0, 3)) 60 def cli(v): 61 click.echo(f"verbosity={v:d}") 62 63 result = runner.invoke(cli, ["-vvv"]) 64 assert not result.exception 65 assert result.output == "verbosity=3\n" 66 67 result = runner.invoke(cli, ["-vvvv"]) 68 assert result.exception 69 assert "Invalid value for '-v': 4 is not in the range 0<=x<=3." in result.output 70 71 result = runner.invoke(cli, []) 72 assert not result.exception 73 assert result.output == "verbosity=0\n" 74 75 result = runner.invoke(cli, ["--help"]) 76 assert re.search(r"-v\s+Verbosity", result.output) is not None 77 78 79@pytest.mark.parametrize("unknown_flag", ["--foo", "-f"]) 80def test_unknown_options(runner, unknown_flag): 81 @click.command() 82 def cli(): 83 pass 84 85 result = runner.invoke(cli, [unknown_flag]) 86 assert result.exception 87 assert f"No such option: {unknown_flag}" in result.output 88 89 90@pytest.mark.parametrize( 91 ("value", "expect"), 92 [ 93 ("--cat", "Did you mean --count?"), 94 ("--bounds", "(Possible options: --bound, --count)"), 95 ("--bount", "(Possible options: --bound, --count)"), 96 ], 97) 98def test_suggest_possible_options(runner, value, expect): 99 cli = click.Command( 100 "cli", params=[click.Option(["--bound"]), click.Option(["--count"])] 101 ) 102 result = runner.invoke(cli, [value]) 103 assert expect in result.output 104 105 106def test_multiple_required(runner): 107 @click.command() 108 @click.option("-m", "--message", multiple=True, required=True) 109 def cli(message): 110 click.echo("\n".join(message)) 111 112 result = runner.invoke(cli, ["-m", "foo", "-mbar"]) 113 assert not result.exception 114 assert result.output == "foo\nbar\n" 115 116 result = runner.invoke(cli, []) 117 assert result.exception 118 assert "Error: Missing option '-m' / '--message'." in result.output 119 120 121@pytest.mark.parametrize( 122 ("multiple", "nargs", "default"), 123 [ 124 (True, 1, []), 125 (True, 1, [1]), 126 # (False, -1, []), 127 # (False, -1, [1]), 128 (False, 2, [1, 2]), 129 # (True, -1, [[]]), 130 # (True, -1, []), 131 # (True, -1, [[1]]), 132 (True, 2, []), 133 (True, 2, [[1, 2]]), 134 ], 135) 136def test_init_good_default_list(runner, multiple, nargs, default): 137 click.Option(["-a"], multiple=multiple, nargs=nargs, default=default) 138 139 140@pytest.mark.parametrize( 141 ("multiple", "nargs", "default"), 142 [ 143 (True, 1, 1), 144 # (False, -1, 1), 145 (False, 2, [1]), 146 (True, 2, [[1]]), 147 ], 148) 149def test_init_bad_default_list(runner, multiple, nargs, default): 150 type = (str, str) if nargs == 2 else None 151 152 with pytest.raises(ValueError, match="default"): 153 click.Option(["-a"], type=type, multiple=multiple, nargs=nargs, default=default) 154 155 156def test_empty_envvar(runner): 157 @click.command() 158 @click.option("--mypath", type=click.Path(exists=True), envvar="MYPATH") 159 def cli(mypath): 160 click.echo(f"mypath: {mypath}") 161 162 result = runner.invoke(cli, [], env={"MYPATH": ""}) 163 assert result.exit_code == 0 164 assert result.output == "mypath: None\n" 165 166 167def test_multiple_envvar(runner): 168 @click.command() 169 @click.option("--arg", multiple=True) 170 def cmd(arg): 171 click.echo("|".join(arg)) 172 173 result = runner.invoke( 174 cmd, [], auto_envvar_prefix="TEST", env={"TEST_ARG": "foo bar baz"} 175 ) 176 assert not result.exception 177 assert result.output == "foo|bar|baz\n" 178 179 @click.command() 180 @click.option("--arg", multiple=True, envvar="X") 181 def cmd(arg): 182 click.echo("|".join(arg)) 183 184 result = runner.invoke(cmd, [], env={"X": "foo bar baz"}) 185 assert not result.exception 186 assert result.output == "foo|bar|baz\n" 187 188 @click.command() 189 @click.option("--arg", multiple=True, type=click.Path()) 190 def cmd(arg): 191 click.echo("|".join(arg)) 192 193 result = runner.invoke( 194 cmd, 195 [], 196 auto_envvar_prefix="TEST", 197 env={"TEST_ARG": f"foo{os.path.pathsep}bar"}, 198 ) 199 assert not result.exception 200 assert result.output == "foo|bar\n" 201 202 203def test_trailing_blanks_boolean_envvar(runner): 204 @click.command() 205 @click.option("--shout/--no-shout", envvar="SHOUT") 206 def cli(shout): 207 click.echo(f"shout: {shout!r}") 208 209 result = runner.invoke(cli, [], env={"SHOUT": " true "}) 210 assert result.exit_code == 0 211 assert result.output == "shout: True\n" 212 213 214def test_multiple_default_help(runner): 215 @click.command() 216 @click.option("--arg1", multiple=True, default=("foo", "bar"), show_default=True) 217 @click.option("--arg2", multiple=True, default=(1, 2), type=int, show_default=True) 218 def cmd(arg, arg2): 219 pass 220 221 result = runner.invoke(cmd, ["--help"]) 222 assert not result.exception 223 assert "foo, bar" in result.output 224 assert "1, 2" in result.output 225 226 227def test_show_default_default_map(runner): 228 @click.command() 229 @click.option("--arg", default="a", show_default=True) 230 def cmd(arg): 231 click.echo(arg) 232 233 result = runner.invoke(cmd, ["--help"], default_map={"arg": "b"}) 234 235 assert not result.exception 236 assert "[default: b]" in result.output 237 238 239def test_multiple_default_type(): 240 opt = click.Option(["-a"], multiple=True, default=(1, 2)) 241 assert opt.nargs == 1 242 assert opt.multiple 243 assert opt.type is click.INT 244 ctx = click.Context(click.Command("test")) 245 assert opt.get_default(ctx) == (1, 2) 246 247 248def test_multiple_default_composite_type(): 249 opt = click.Option(["-a"], multiple=True, default=[(1, "a")]) 250 assert opt.nargs == 2 251 assert opt.multiple 252 assert isinstance(opt.type, click.Tuple) 253 assert opt.type.types == [click.INT, click.STRING] 254 ctx = click.Context(click.Command("test")) 255 assert opt.type_cast_value(ctx, opt.get_default(ctx)) == ((1, "a"),) 256 257 258def test_parse_multiple_default_composite_type(runner): 259 @click.command() 260 @click.option("-a", multiple=True, default=("a", "b")) 261 @click.option("-b", multiple=True, default=[(1, "a")]) 262 def cmd(a, b): 263 click.echo(a) 264 click.echo(b) 265 266 # result = runner.invoke(cmd, "-a c -a 1 -a d -b 2 two -b 4 four".split()) 267 # assert result.output == "('c', '1', 'd')\n((2, 'two'), (4, 'four'))\n" 268 result = runner.invoke(cmd) 269 assert result.output == "('a', 'b')\n((1, 'a'),)\n" 270 271 272def test_dynamic_default_help_unset(runner): 273 @click.command() 274 @click.option( 275 "--username", 276 prompt=True, 277 default=lambda: os.environ.get("USER", ""), 278 show_default=True, 279 ) 280 def cmd(username): 281 print("Hello,", username) 282 283 result = runner.invoke(cmd, ["--help"]) 284 assert result.exit_code == 0 285 assert "--username" in result.output 286 assert "lambda" not in result.output 287 assert "(dynamic)" in result.output 288 289 290def test_dynamic_default_help_text(runner): 291 @click.command() 292 @click.option( 293 "--username", 294 prompt=True, 295 default=lambda: os.environ.get("USER", ""), 296 show_default="current user", 297 ) 298 def cmd(username): 299 print("Hello,", username) 300 301 result = runner.invoke(cmd, ["--help"]) 302 assert result.exit_code == 0 303 assert "--username" in result.output 304 assert "lambda" not in result.output 305 assert "(current user)" in result.output 306 307 308@pytest.mark.parametrize( 309 ("type", "expect"), 310 [ 311 (click.IntRange(1, 32), "1<=x<=32"), 312 (click.IntRange(1, 32, min_open=True, max_open=True), "1<x<32"), 313 (click.IntRange(1), "x>=1"), 314 (click.IntRange(max=32), "x<=32"), 315 ], 316) 317def test_intrange_default_help_text(type, expect): 318 option = click.Option(["--num"], type=type, show_default=True, default=2) 319 context = click.Context(click.Command("test")) 320 result = option.get_help_record(context)[1] 321 assert expect in result 322 323 324def test_count_default_type_help(): 325 """A count option with the default type should not show >=0 in help.""" 326 option = click.Option(["--count"], count=True, help="some words") 327 context = click.Context(click.Command("test")) 328 result = option.get_help_record(context)[1] 329 assert result == "some words" 330 331 332def test_file_type_help_default(): 333 """The default for a File type is a filename string. The string 334 should be displayed in help, not an open file object. 335 336 Type casting is only applied to defaults in processing, not when 337 getting the default value. 338 """ 339 option = click.Option( 340 ["--in"], type=click.File(), default=__file__, show_default=True 341 ) 342 context = click.Context(click.Command("test")) 343 result = option.get_help_record(context)[1] 344 assert __file__ in result 345 346 347def test_toupper_envvar_prefix(runner): 348 @click.command() 349 @click.option("--arg") 350 def cmd(arg): 351 click.echo(arg) 352 353 result = runner.invoke(cmd, [], auto_envvar_prefix="test", env={"TEST_ARG": "foo"}) 354 assert not result.exception 355 assert result.output == "foo\n" 356 357 358def test_nargs_envvar(runner): 359 @click.command() 360 @click.option("--arg", nargs=2) 361 def cmd(arg): 362 click.echo("|".join(arg)) 363 364 result = runner.invoke( 365 cmd, [], auto_envvar_prefix="TEST", env={"TEST_ARG": "foo bar"} 366 ) 367 assert not result.exception 368 assert result.output == "foo|bar\n" 369 370 @click.command() 371 @click.option("--arg", nargs=2, multiple=True) 372 def cmd(arg): 373 for item in arg: 374 click.echo("|".join(item)) 375 376 result = runner.invoke( 377 cmd, [], auto_envvar_prefix="TEST", env={"TEST_ARG": "x 1 y 2"} 378 ) 379 assert not result.exception 380 assert result.output == "x|1\ny|2\n" 381 382 383def test_show_envvar(runner): 384 @click.command() 385 @click.option("--arg1", envvar="ARG1", show_envvar=True) 386 def cmd(arg): 387 pass 388 389 result = runner.invoke(cmd, ["--help"]) 390 assert not result.exception 391 assert "ARG1" in result.output 392 393 394def test_show_envvar_auto_prefix(runner): 395 @click.command() 396 @click.option("--arg1", show_envvar=True) 397 def cmd(arg): 398 pass 399 400 result = runner.invoke(cmd, ["--help"], auto_envvar_prefix="TEST") 401 assert not result.exception 402 assert "TEST_ARG1" in result.output 403 404 405def test_show_envvar_auto_prefix_dash_in_command(runner): 406 @click.group() 407 def cli(): 408 pass 409 410 @cli.command() 411 @click.option("--baz", show_envvar=True) 412 def foo_bar(baz): 413 pass 414 415 result = runner.invoke(cli, ["foo-bar", "--help"], auto_envvar_prefix="TEST") 416 assert not result.exception 417 assert "TEST_FOO_BAR_BAZ" in result.output 418 419 420def test_custom_validation(runner): 421 def validate_pos_int(ctx, param, value): 422 if value < 0: 423 raise click.BadParameter("Value needs to be positive") 424 return value 425 426 @click.command() 427 @click.option("--foo", callback=validate_pos_int, default=1) 428 def cmd(foo): 429 click.echo(foo) 430 431 result = runner.invoke(cmd, ["--foo", "-1"]) 432 assert "Invalid value for '--foo': Value needs to be positive" in result.output 433 434 result = runner.invoke(cmd, ["--foo", "42"]) 435 assert result.output == "42\n" 436 437 438def test_callback_validates_prompt(runner, monkeypatch): 439 def validate(ctx, param, value): 440 if value < 0: 441 raise click.BadParameter("should be positive") 442 443 return value 444 445 @click.command() 446 @click.option("-a", type=int, callback=validate, prompt=True) 447 def cli(a): 448 click.echo(a) 449 450 result = runner.invoke(cli, input="-12\n60\n") 451 assert result.output == "A: -12\nError: should be positive\nA: 60\n60\n" 452 453 454def test_winstyle_options(runner): 455 @click.command() 456 @click.option("/debug;/no-debug", help="Enables or disables debug mode.") 457 def cmd(debug): 458 click.echo(debug) 459 460 result = runner.invoke(cmd, ["/debug"], help_option_names=["/?"]) 461 assert result.output == "True\n" 462 result = runner.invoke(cmd, ["/no-debug"], help_option_names=["/?"]) 463 assert result.output == "False\n" 464 result = runner.invoke(cmd, [], help_option_names=["/?"]) 465 assert result.output == "False\n" 466 result = runner.invoke(cmd, ["/?"], help_option_names=["/?"]) 467 assert "/debug; /no-debug Enables or disables debug mode." in result.output 468 assert "/? Show this message and exit." in result.output 469 470 471def test_legacy_options(runner): 472 @click.command() 473 @click.option("-whatever") 474 def cmd(whatever): 475 click.echo(whatever) 476 477 result = runner.invoke(cmd, ["-whatever", "42"]) 478 assert result.output == "42\n" 479 result = runner.invoke(cmd, ["-whatever=23"]) 480 assert result.output == "23\n" 481 482 483def test_missing_option_string_cast(): 484 ctx = click.Context(click.Command("")) 485 486 with pytest.raises(click.MissingParameter) as excinfo: 487 click.Option(["-a"], required=True).process_value(ctx, None) 488 489 assert str(excinfo.value) == "Missing parameter: a" 490 491 492def test_missing_choice(runner): 493 @click.command() 494 @click.option("--foo", type=click.Choice(["foo", "bar"]), required=True) 495 def cmd(foo): 496 click.echo(foo) 497 498 result = runner.invoke(cmd) 499 assert result.exit_code == 2 500 error, separator, choices = result.output.partition("Choose from") 501 assert "Error: Missing option '--foo'. " in error 502 assert "Choose from" in separator 503 assert "foo" in choices 504 assert "bar" in choices 505 506 507def test_case_insensitive_choice(runner): 508 @click.command() 509 @click.option("--foo", type=click.Choice(["Orange", "Apple"], case_sensitive=False)) 510 def cmd(foo): 511 click.echo(foo) 512 513 result = runner.invoke(cmd, ["--foo", "apple"]) 514 assert result.exit_code == 0 515 assert result.output == "Apple\n" 516 517 result = runner.invoke(cmd, ["--foo", "oRANGe"]) 518 assert result.exit_code == 0 519 assert result.output == "Orange\n" 520 521 result = runner.invoke(cmd, ["--foo", "Apple"]) 522 assert result.exit_code == 0 523 assert result.output == "Apple\n" 524 525 @click.command() 526 @click.option("--foo", type=click.Choice(["Orange", "Apple"])) 527 def cmd2(foo): 528 click.echo(foo) 529 530 result = runner.invoke(cmd2, ["--foo", "apple"]) 531 assert result.exit_code == 2 532 533 result = runner.invoke(cmd2, ["--foo", "oRANGe"]) 534 assert result.exit_code == 2 535 536 result = runner.invoke(cmd2, ["--foo", "Apple"]) 537 assert result.exit_code == 0 538 539 540def test_case_insensitive_choice_returned_exactly(runner): 541 @click.command() 542 @click.option("--foo", type=click.Choice(["Orange", "Apple"], case_sensitive=False)) 543 def cmd(foo): 544 click.echo(foo) 545 546 result = runner.invoke(cmd, ["--foo", "apple"]) 547 assert result.exit_code == 0 548 assert result.output == "Apple\n" 549 550 551def test_option_help_preserve_paragraphs(runner): 552 @click.command() 553 @click.option( 554 "-C", 555 "--config", 556 type=click.Path(), 557 help="""Configuration file to use. 558 559 If not given, the environment variable CONFIG_FILE is consulted 560 and used if set. If neither are given, a default configuration 561 file is loaded.""", 562 ) 563 def cmd(config): 564 pass 565 566 result = runner.invoke(cmd, ["--help"]) 567 assert result.exit_code == 0 568 i = " " * 21 569 assert ( 570 " -C, --config PATH Configuration file to use.\n" 571 f"{i}\n" 572 f"{i}If not given, the environment variable CONFIG_FILE is\n" 573 f"{i}consulted and used if set. If neither are given, a default\n" 574 f"{i}configuration file is loaded." 575 ) in result.output 576 577 578def test_argument_custom_class(runner): 579 class CustomArgument(click.Argument): 580 def get_default(self, ctx, call=True): 581 """a dumb override of a default value for testing""" 582 return "I am a default" 583 584 @click.command() 585 @click.argument("testarg", cls=CustomArgument, default="you wont see me") 586 def cmd(testarg): 587 click.echo(testarg) 588 589 result = runner.invoke(cmd) 590 assert "I am a default" in result.output 591 assert "you wont see me" not in result.output 592 593 594def test_option_custom_class(runner): 595 class CustomOption(click.Option): 596 def get_help_record(self, ctx): 597 """a dumb override of a help text for testing""" 598 return ("--help", "I am a help text") 599 600 @click.command() 601 @click.option("--testoption", cls=CustomOption, help="you wont see me") 602 def cmd(testoption): 603 click.echo(testoption) 604 605 result = runner.invoke(cmd, ["--help"]) 606 assert "I am a help text" in result.output 607 assert "you wont see me" not in result.output 608 609 610def test_option_custom_class_reusable(runner): 611 """Ensure we can reuse a custom class option. See Issue #926""" 612 613 class CustomOption(click.Option): 614 def get_help_record(self, ctx): 615 """a dumb override of a help text for testing""" 616 return ("--help", "I am a help text") 617 618 # Assign to a variable to re-use the decorator. 619 testoption = click.option("--testoption", cls=CustomOption, help="you wont see me") 620 621 @click.command() 622 @testoption 623 def cmd1(testoption): 624 click.echo(testoption) 625 626 @click.command() 627 @testoption 628 def cmd2(testoption): 629 click.echo(testoption) 630 631 # Both of the commands should have the --help option now. 632 for cmd in (cmd1, cmd2): 633 634 result = runner.invoke(cmd, ["--help"]) 635 assert "I am a help text" in result.output 636 assert "you wont see me" not in result.output 637 638 639def test_bool_flag_with_type(runner): 640 @click.command() 641 @click.option("--shout/--no-shout", default=False, type=bool) 642 def cmd(shout): 643 pass 644 645 result = runner.invoke(cmd) 646 assert not result.exception 647 648 649def test_aliases_for_flags(runner): 650 @click.command() 651 @click.option("--warnings/--no-warnings", " /-W", default=True) 652 def cli(warnings): 653 click.echo(warnings) 654 655 result = runner.invoke(cli, ["--warnings"]) 656 assert result.output == "True\n" 657 result = runner.invoke(cli, ["--no-warnings"]) 658 assert result.output == "False\n" 659 result = runner.invoke(cli, ["-W"]) 660 assert result.output == "False\n" 661 662 @click.command() 663 @click.option("--warnings/--no-warnings", "-w", default=True) 664 def cli_alt(warnings): 665 click.echo(warnings) 666 667 result = runner.invoke(cli_alt, ["--warnings"]) 668 assert result.output == "True\n" 669 result = runner.invoke(cli_alt, ["--no-warnings"]) 670 assert result.output == "False\n" 671 result = runner.invoke(cli_alt, ["-w"]) 672 assert result.output == "True\n" 673 674 675@pytest.mark.parametrize( 676 "option_args,expected", 677 [ 678 (["--aggressive", "--all", "-a"], "aggressive"), 679 (["--first", "--second", "--third", "-a", "-b", "-c"], "first"), 680 (["--apple", "--banana", "--cantaloupe", "-a", "-b", "-c"], "apple"), 681 (["--cantaloupe", "--banana", "--apple", "-c", "-b", "-a"], "cantaloupe"), 682 (["-a", "-b", "-c"], "a"), 683 (["-c", "-b", "-a"], "c"), 684 (["-a", "--apple", "-b", "--banana", "-c", "--cantaloupe"], "apple"), 685 (["-c", "-a", "--cantaloupe", "-b", "--banana", "--apple"], "cantaloupe"), 686 (["--from", "-f", "_from"], "_from"), 687 (["--return", "-r", "_ret"], "_ret"), 688 ], 689) 690def test_option_names(runner, option_args, expected): 691 @click.command() 692 @click.option(*option_args, is_flag=True) 693 def cmd(**kwargs): 694 click.echo(str(kwargs[expected])) 695 696 assert cmd.params[0].name == expected 697 698 for form in option_args: 699 if form.startswith("-"): 700 result = runner.invoke(cmd, [form]) 701 assert result.output == "True\n" 702 703 704def test_flag_duplicate_names(runner): 705 with pytest.raises(ValueError, match="cannot use the same flag for true/false"): 706 click.Option(["--foo/--foo"], default=False) 707 708 709@pytest.mark.parametrize(("default", "expect"), [(False, "no-cache"), (True, "cache")]) 710def test_show_default_boolean_flag_name(runner, default, expect): 711 """When a boolean flag has distinct True/False opts, it should show 712 the default opt name instead of the default value. It should only 713 show one name even if multiple are declared. 714 """ 715 opt = click.Option( 716 ("--cache/--no-cache", "--c/--nc"), 717 default=default, 718 show_default=True, 719 help="Enable/Disable the cache.", 720 ) 721 ctx = click.Context(click.Command("test")) 722 message = opt.get_help_record(ctx)[1] 723 assert f"[default: {expect}]" in message 724 725 726def test_show_default_boolean_flag_value(runner): 727 """When a boolean flag only has one opt, it will show the default 728 value, not the opt name. 729 """ 730 opt = click.Option( 731 ("--cache",), is_flag=True, show_default=True, help="Enable the cache." 732 ) 733 ctx = click.Context(click.Command("test")) 734 message = opt.get_help_record(ctx)[1] 735 assert "[default: False]" in message 736 737 738def test_show_default_string(runner): 739 """When show_default is a string show that value as default.""" 740 opt = click.Option(["--limit"], show_default="unlimited") 741 ctx = click.Context(click.Command("cli")) 742 message = opt.get_help_record(ctx)[1] 743 assert "[default: (unlimited)]" in message 744 745 746def test_do_not_show_no_default(runner): 747 """When show_default is True and no default is set do not show None.""" 748 opt = click.Option(["--limit"], show_default=True) 749 ctx = click.Context(click.Command("cli")) 750 message = opt.get_help_record(ctx)[1] 751 assert "[default: None]" not in message 752 753 754def test_do_not_show_default_empty_multiple(): 755 """When show_default is True and multiple=True is set, it should not 756 print empty default value in --help output. 757 """ 758 opt = click.Option(["-a"], multiple=True, help="values", show_default=True) 759 ctx = click.Context(click.Command("cli")) 760 message = opt.get_help_record(ctx)[1] 761 assert message == "values" 762 763 764@pytest.mark.parametrize( 765 ("args", "expect"), 766 [ 767 (None, (None, None, ())), 768 (["--opt"], ("flag", None, ())), 769 (["--opt", "-a", 42], ("flag", "42", ())), 770 (["--opt", "test", "-a", 42], ("test", "42", ())), 771 (["--opt=test", "-a", 42], ("test", "42", ())), 772 (["-o"], ("flag", None, ())), 773 (["-o", "-a", 42], ("flag", "42", ())), 774 (["-o", "test", "-a", 42], ("test", "42", ())), 775 (["-otest", "-a", 42], ("test", "42", ())), 776 (["a", "b", "c"], (None, None, ("a", "b", "c"))), 777 (["--opt", "a", "b", "c"], ("a", None, ("b", "c"))), 778 (["--opt", "test"], ("test", None, ())), 779 (["-otest", "a", "b", "c"], ("test", None, ("a", "b", "c"))), 780 (["--opt=test", "a", "b", "c"], ("test", None, ("a", "b", "c"))), 781 ], 782) 783def test_option_with_optional_value(runner, args, expect): 784 @click.command() 785 @click.option("-o", "--opt", is_flag=False, flag_value="flag") 786 @click.option("-a") 787 @click.argument("b", nargs=-1) 788 def cli(opt, a, b): 789 return opt, a, b 790 791 result = runner.invoke(cli, args, standalone_mode=False, catch_exceptions=False) 792 assert result.return_value == expect 793 794 795def test_multiple_option_with_optional_value(runner): 796 cli = click.Command( 797 "cli", 798 params=[ 799 click.Option(["-f"], is_flag=False, flag_value="flag", multiple=True), 800 click.Option(["-a"]), 801 click.Argument(["b"], nargs=-1), 802 ], 803 callback=lambda **kwargs: kwargs, 804 ) 805 result = runner.invoke( 806 cli, 807 ["-f", "-f", "other", "-f", "-a", "1", "a", "b"], 808 standalone_mode=False, 809 catch_exceptions=False, 810 ) 811 assert result.return_value == { 812 "f": ("flag", "other", "flag"), 813 "a": "1", 814 "b": ("a", "b"), 815 } 816 817 818def test_type_from_flag_value(): 819 param = click.Option(["-a", "x"], default=True, flag_value=4) 820 assert param.type is click.INT 821 param = click.Option(["-b", "x"], flag_value=8) 822 assert param.type is click.INT 823 824 825@pytest.mark.parametrize( 826 ("option", "expected"), 827 [ 828 # Not boolean flags 829 pytest.param(Option(["-a"], type=int), False, id="int option"), 830 pytest.param(Option(["-a"], type=bool), False, id="bool non-flag [None]"), 831 pytest.param(Option(["-a"], default=True), False, id="bool non-flag [True]"), 832 pytest.param(Option(["-a"], default=False), False, id="bool non-flag [False]"), 833 pytest.param(Option(["-a"], flag_value=1), False, id="non-bool flag_value"), 834 # Boolean flags 835 pytest.param(Option(["-a"], is_flag=True), True, id="is_flag=True"), 836 pytest.param(Option(["-a/-A"]), True, id="secondary option [implicit flag]"), 837 pytest.param(Option(["-a"], flag_value=True), True, id="bool flag_value"), 838 ], 839) 840def test_is_bool_flag_is_correctly_set(option, expected): 841 assert option.is_bool_flag is expected 842