diff --git a/CHANGES.rst b/CHANGES.rst index f9f33ec0f1..7a27ca01d5 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,7 @@ Version 8.2.1 ------------- +- Fixes flag default handling for boolean options. :issue:`2897` :pr:`2912` Version 8.2.0 ------------- diff --git a/src/click/core.py b/src/click/core.py index 4745b533e8..6ddb928431 100644 --- a/src/click/core.py +++ b/src/click/core.py @@ -2618,6 +2618,12 @@ def __init__( # default. self.type = types.convert_type(None, flag_value) + if is_flag and isinstance(self.type, types.BoolParamType): + # If the type is a boolean, we need to set the flag_value + # to True or False depending on the default. + if flag_value is None: + flag_value = not self.default + self.is_flag: bool = is_flag self.is_bool_flag: bool = is_flag and isinstance(self.type, types.BoolParamType) self.flag_value: t.Any = flag_value diff --git a/tests/test_defaults.py b/tests/test_defaults.py index 3f293e8816..f11c0dc77b 100644 --- a/tests/test_defaults.py +++ b/tests/test_defaults.py @@ -84,3 +84,43 @@ def foo(name): result = runner.invoke(cli, ["foo", "--help"], default_map={"foo": {"name": True}}) assert "default: name" in result.output + + +def test_flag_default_bool(runner): + """test flag with default bool""" + + @click.command() + @click.option("--foo", is_flag=True, default=False, type=click.BOOL) + @click.option("--bar", is_flag=True, default=False, type=bool) + @click.option("--baz", is_flag=True, default=True, type=click.BOOL) + @click.option("--qux", is_flag=True, default=True, type=bool) + def cli(foo, bar, baz, qux): + assert isinstance(foo, bool) + assert isinstance(bar, bool) + assert isinstance(baz, bool) + assert isinstance(qux, bool) + click.echo(f"foo: {foo}, bar: {bar}, baz: {baz}, qux: {qux}") + + result = runner.invoke(cli, []) + assert not result.exception + assert "foo: False, bar: False, baz: True, qux: True" in result.output + + result = runner.invoke(cli, ["--foo"]) + assert not result.exception + assert "foo: True, bar: False, baz: True, qux: True" in result.output + + result = runner.invoke(cli, ["--bar"]) + assert not result.exception + assert "foo: False, bar: True, baz: True, qux: True" in result.output + + result = runner.invoke(cli, ["--baz"]) + assert not result.exception + assert "foo: False, bar: False, baz: False, qux: True" in result.output + + result = runner.invoke(cli, ["--qux"]) + assert not result.exception + assert "foo: False, bar: False, baz: True, qux: False" in result.output + + result = runner.invoke(cli, ["--foo", "--bar", "--baz", "--qux"]) + assert not result.exception + assert "foo: True, bar: True, baz: False, qux: False" in result.output