# -*- coding: utf-8 -*- import os import re import pytest import click from click._compat import text_type def test_prefixes(runner): @click.command() @click.option("++foo", is_flag=True, help="das foo") @click.option("--bar", is_flag=True, help="das bar") def cli(foo, bar): click.echo("foo={} bar={}".format(foo, bar)) result = runner.invoke(cli, ["++foo", "--bar"]) assert not result.exception assert result.output == "foo=True bar=True\n" result = runner.invoke(cli, ["--help"]) assert re.search(r"\+\+foo\s+das foo", result.output) is not None assert re.search(r"--bar\s+das bar", result.output) is not None def test_invalid_option(runner): with pytest.raises(TypeError, match="name was passed"): @click.command() @click.option("foo") def cli(foo): pass def test_invalid_nargs(runner): with pytest.raises(TypeError, match="nargs < 0"): @click.command() @click.option("--foo", nargs=-1) def cli(foo): pass def test_nargs_tup_composite_mult(runner): @click.command() @click.option("--item", type=(str, int), multiple=True) def copy(item): for item in item: click.echo("name={0[0]} id={0[1]:d}".format(item)) result = runner.invoke(copy, ["--item", "peter", "1", "--item", "max", "2"]) assert not result.exception assert result.output.splitlines() == ["name=peter id=1", "name=max id=2"] def test_counting(runner): @click.command() @click.option("-v", count=True, help="Verbosity", type=click.IntRange(0, 3)) def cli(v): click.echo("verbosity={:d}".format(v)) result = runner.invoke(cli, ["-vvv"]) assert not result.exception assert result.output == "verbosity=3\n" result = runner.invoke(cli, ["-vvvv"]) assert result.exception assert ( "Invalid value for '-v': 4 is not in the valid range of 0 to 3." in result.output ) result = runner.invoke(cli, []) assert not result.exception assert result.output == "verbosity=0\n" result = runner.invoke(cli, ["--help"]) assert re.search(r"-v\s+Verbosity", result.output) is not None @pytest.mark.parametrize("unknown_flag", ["--foo", "-f"]) def test_unknown_options(runner, unknown_flag): @click.command() def cli(): pass result = runner.invoke(cli, [unknown_flag]) assert result.exception assert "no such option: {}".format(unknown_flag) in result.output def test_multiple_required(runner): @click.command() @click.option("-m", "--message", multiple=True, required=True) def cli(message): click.echo("\n".join(message)) result = runner.invoke(cli, ["-m", "foo", "-mbar"]) assert not result.exception assert result.output == "foo\nbar\n" result = runner.invoke(cli, []) assert result.exception assert "Error: Missing option '-m' / '--message'." in result.output def test_empty_envvar(runner): @click.command() @click.option("--mypath", type=click.Path(exists=True), envvar="MYPATH") def cli(mypath): click.echo("mypath: {}".format(mypath)) result = runner.invoke(cli, [], env={"MYPATH": ""}) assert result.exit_code == 0 assert result.output == "mypath: None\n" def test_multiple_envvar(runner): @click.command() @click.option("--arg", multiple=True) def cmd(arg): click.echo("|".join(arg)) result = runner.invoke( cmd, [], auto_envvar_prefix="TEST", env={"TEST_ARG": "foo bar baz"} ) assert not result.exception assert result.output == "foo|bar|baz\n" @click.command() @click.option("--arg", multiple=True, envvar="X") def cmd(arg): click.echo("|".join(arg)) result = runner.invoke(cmd, [], env={"X": "foo bar baz"}) assert not result.exception assert result.output == "foo|bar|baz\n" @click.command() @click.option("--arg", multiple=True, type=click.Path()) def cmd(arg): click.echo("|".join(arg)) result = runner.invoke( cmd, [], auto_envvar_prefix="TEST", env={"TEST_ARG": "foo{}bar".format(os.path.pathsep)}, ) assert not result.exception assert result.output == "foo|bar\n" def test_multiple_default_help(runner): @click.command() @click.option("--arg1", multiple=True, default=("foo", "bar"), show_default=True) @click.option("--arg2", multiple=True, default=(1, 2), type=int, show_default=True) def cmd(arg, arg2): pass result = runner.invoke(cmd, ["--help"]) assert not result.exception assert "foo, bar" in result.output assert "1, 2" in result.output def test_multiple_default_type(runner): @click.command() @click.option("--arg1", multiple=True, default=("foo", "bar")) @click.option("--arg2", multiple=True, default=(1, "a")) def cmd(arg1, arg2): assert all(isinstance(e[0], text_type) for e in arg1) assert all(isinstance(e[1], text_type) for e in arg1) assert all(isinstance(e[0], int) for e in arg2) assert all(isinstance(e[1], text_type) for e in arg2) result = runner.invoke( cmd, "--arg1 a b --arg1 test 1 --arg2 2 two --arg2 4 four".split() ) assert not result.exception def test_dynamic_default_help_unset(runner): @click.command() @click.option( "--username", prompt=True, default=lambda: os.environ.get("USER", ""), show_default=True, ) def cmd(username): print("Hello,", username) result = runner.invoke(cmd, ["--help"]) assert result.exit_code == 0 assert "--username" in result.output assert "lambda" not in result.output assert "(dynamic)" in result.output def test_dynamic_default_help_text(runner): @click.command() @click.option( "--username", prompt=True, default=lambda: os.environ.get("USER", ""), show_default="current user", ) def cmd(username): print("Hello,", username) result = runner.invoke(cmd, ["--help"]) assert result.exit_code == 0 assert "--username" in result.output assert "lambda" not in result.output assert "(current user)" in result.output def test_toupper_envvar_prefix(runner): @click.command() @click.option("--arg") def cmd(arg): click.echo(arg) result = runner.invoke(cmd, [], auto_envvar_prefix="test", env={"TEST_ARG": "foo"}) assert not result.exception assert result.output == "foo\n" def test_nargs_envvar(runner): @click.command() @click.option("--arg", nargs=2) def cmd(arg): click.echo("|".join(arg)) result = runner.invoke( cmd, [], auto_envvar_prefix="TEST", env={"TEST_ARG": "foo bar"} ) assert not result.exception assert result.output == "foo|bar\n" @click.command() @click.option("--arg", nargs=2, multiple=True) def cmd(arg): for item in arg: click.echo("|".join(item)) result = runner.invoke( cmd, [], auto_envvar_prefix="TEST", env={"TEST_ARG": "x 1 y 2"} ) assert not result.exception assert result.output == "x|1\ny|2\n" def test_show_envvar(runner): @click.command() @click.option("--arg1", envvar="ARG1", show_envvar=True) def cmd(arg): pass result = runner.invoke(cmd, ["--help"]) assert not result.exception assert "ARG1" in result.output def test_show_envvar_auto_prefix(runner): @click.command() @click.option("--arg1", show_envvar=True) def cmd(arg): pass result = runner.invoke(cmd, ["--help"], auto_envvar_prefix="TEST") assert not result.exception assert "TEST_ARG1" in result.output def test_show_envvar_auto_prefix_dash_in_command(runner): @click.group() def cli(): pass @cli.command() @click.option("--baz", show_envvar=True) def foo_bar(baz): pass result = runner.invoke(cli, ["foo-bar", "--help"], auto_envvar_prefix="TEST") assert not result.exception assert "TEST_FOO_BAR_BAZ" in result.output def test_custom_validation(runner): def validate_pos_int(ctx, param, value): if value < 0: raise click.BadParameter("Value needs to be positive") return value @click.command() @click.option("--foo", callback=validate_pos_int, default=1) def cmd(foo): click.echo(foo) result = runner.invoke(cmd, ["--foo", "-1"]) assert "Invalid value for '--foo': Value needs to be positive" in result.output result = runner.invoke(cmd, ["--foo", "42"]) assert result.output == "42\n" def test_winstyle_options(runner): @click.command() @click.option("/debug;/no-debug", help="Enables or disables debug mode.") def cmd(debug): click.echo(debug) result = runner.invoke(cmd, ["/debug"], help_option_names=["/?"]) assert result.output == "True\n" result = runner.invoke(cmd, ["/no-debug"], help_option_names=["/?"]) assert result.output == "False\n" result = runner.invoke(cmd, [], help_option_names=["/?"]) assert result.output == "False\n" result = runner.invoke(cmd, ["/?"], help_option_names=["/?"]) assert "/debug; /no-debug Enables or disables debug mode." in result.output assert "/? Show this message and exit." in result.output def test_legacy_options(runner): @click.command() @click.option("-whatever") def cmd(whatever): click.echo(whatever) result = runner.invoke(cmd, ["-whatever", "42"]) assert result.output == "42\n" result = runner.invoke(cmd, ["-whatever=23"]) assert result.output == "23\n" def test_missing_option_string_cast(): ctx = click.Context(click.Command("")) with pytest.raises(click.MissingParameter) as excinfo: click.Option(["-a"], required=True).full_process_value(ctx, None) assert str(excinfo.value) == "missing parameter: a" def test_missing_choice(runner): @click.command() @click.option("--foo", type=click.Choice(["foo", "bar"]), required=True) def cmd(foo): click.echo(foo) result = runner.invoke(cmd) assert result.exit_code == 2 error, separator, choices = result.output.partition("Choose from") assert "Error: Missing option '--foo'. " in error assert "Choose from" in separator assert "foo" in choices assert "bar" in choices def test_case_insensitive_choice(runner): @click.command() @click.option("--foo", type=click.Choice(["Orange", "Apple"], case_sensitive=False)) def cmd(foo): click.echo(foo) result = runner.invoke(cmd, ["--foo", "apple"]) assert result.exit_code == 0 assert result.output == "Apple\n" result = runner.invoke(cmd, ["--foo", "oRANGe"]) assert result.exit_code == 0 assert result.output == "Orange\n" result = runner.invoke(cmd, ["--foo", "Apple"]) assert result.exit_code == 0 assert result.output == "Apple\n" @click.command() @click.option("--foo", type=click.Choice(["Orange", "Apple"])) def cmd2(foo): click.echo(foo) result = runner.invoke(cmd2, ["--foo", "apple"]) assert result.exit_code == 2 result = runner.invoke(cmd2, ["--foo", "oRANGe"]) assert result.exit_code == 2 result = runner.invoke(cmd2, ["--foo", "Apple"]) assert result.exit_code == 0 def test_case_insensitive_choice_returned_exactly(runner): @click.command() @click.option("--foo", type=click.Choice(["Orange", "Apple"], case_sensitive=False)) def cmd(foo): click.echo(foo) result = runner.invoke(cmd, ["--foo", "apple"]) assert result.exit_code == 0 assert result.output == "Apple\n" def test_option_help_preserve_paragraphs(runner): @click.command() @click.option( "-C", "--config", type=click.Path(), help="""Configuration file to use. If not given, the environment variable CONFIG_FILE is consulted and used if set. If neither are given, a default configuration file is loaded.""", ) def cmd(config): pass result = runner.invoke(cmd, ["--help"],) assert result.exit_code == 0 assert ( " -C, --config PATH Configuration file to use.\n" "{i}\n" "{i}If not given, the environment variable CONFIG_FILE is\n" "{i}consulted and used if set. If neither are given, a default\n" "{i}configuration file is loaded.".format(i=" " * 21) ) in result.output def test_argument_custom_class(runner): class CustomArgument(click.Argument): def get_default(self, ctx): """a dumb override of a default value for testing""" return "I am a default" @click.command() @click.argument("testarg", cls=CustomArgument, default="you wont see me") def cmd(testarg): click.echo(testarg) result = runner.invoke(cmd) assert "I am a default" in result.output assert "you wont see me" not in result.output def test_option_custom_class(runner): class CustomOption(click.Option): def get_help_record(self, ctx): """a dumb override of a help text for testing""" return ("--help", "I am a help text") @click.command() @click.option("--testoption", cls=CustomOption, help="you wont see me") def cmd(testoption): click.echo(testoption) result = runner.invoke(cmd, ["--help"]) assert "I am a help text" in result.output assert "you wont see me" not in result.output def test_option_custom_class_reusable(runner): """Ensure we can reuse a custom class option. See Issue #926""" class CustomOption(click.Option): def get_help_record(self, ctx): """a dumb override of a help text for testing""" return ("--help", "I am a help text") # Assign to a variable to re-use the decorator. testoption = click.option("--testoption", cls=CustomOption, help="you wont see me") @click.command() @testoption def cmd1(testoption): click.echo(testoption) @click.command() @testoption def cmd2(testoption): click.echo(testoption) # Both of the commands should have the --help option now. for cmd in (cmd1, cmd2): result = runner.invoke(cmd, ["--help"]) assert "I am a help text" in result.output assert "you wont see me" not in result.output def test_bool_flag_with_type(runner): @click.command() @click.option("--shout/--no-shout", default=False, type=bool) def cmd(shout): pass result = runner.invoke(cmd) assert not result.exception def test_aliases_for_flags(runner): @click.command() @click.option("--warnings/--no-warnings", " /-W", default=True) def cli(warnings): click.echo(warnings) result = runner.invoke(cli, ["--warnings"]) assert result.output == "True\n" result = runner.invoke(cli, ["--no-warnings"]) assert result.output == "False\n" result = runner.invoke(cli, ["-W"]) assert result.output == "False\n" @click.command() @click.option("--warnings/--no-warnings", "-w", default=True) def cli_alt(warnings): click.echo(warnings) result = runner.invoke(cli_alt, ["--warnings"]) assert result.output == "True\n" result = runner.invoke(cli_alt, ["--no-warnings"]) assert result.output == "False\n" result = runner.invoke(cli_alt, ["-w"]) assert result.output == "True\n" @pytest.mark.parametrize( "option_args,expected", [ (["--aggressive", "--all", "-a"], "aggressive"), (["--first", "--second", "--third", "-a", "-b", "-c"], "first"), (["--apple", "--banana", "--cantaloupe", "-a", "-b", "-c"], "apple"), (["--cantaloupe", "--banana", "--apple", "-c", "-b", "-a"], "cantaloupe"), (["-a", "-b", "-c"], "a"), (["-c", "-b", "-a"], "c"), (["-a", "--apple", "-b", "--banana", "-c", "--cantaloupe"], "apple"), (["-c", "-a", "--cantaloupe", "-b", "--banana", "--apple"], "cantaloupe"), (["--from", "-f", "_from"], "_from"), (["--return", "-r", "_ret"], "_ret"), ], ) def test_option_names(runner, option_args, expected): @click.command() @click.option(*option_args, is_flag=True) def cmd(**kwargs): click.echo(str(kwargs[expected])) assert cmd.params[0].name == expected for form in option_args: if form.startswith("-"): result = runner.invoke(cmd, [form]) assert result.output == "True\n"