From 05319a0ee40b85f414c37beefe029b78409f716e Mon Sep 17 00:00:00 2001 From: polusV <12313034@mail.sustech.edu.cn> Date: Sat, 18 Apr 2026 11:11:48 +0800 Subject: [PATCH 1/2] Fix negative bool flag default=True with flag_value=False --- src/click/core.py | 6 +++++- tests/test_options.py | 17 ++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/click/core.py b/src/click/core.py index f0a624be3..844d48b8b 100644 --- a/src/click/core.py +++ b/src/click/core.py @@ -2897,7 +2897,11 @@ def get_default( # (instead of eagerly in __init__) prevents callable flag_values # (like classes) from being instantiated by the callable check below. # https://github.com/pallets/click/issues/3121 - if value is True and self.is_flag: + if ( + value is True + and self.is_flag + and not (self.is_bool_flag and self.flag_value is False) + ): value = self.flag_value elif call and callable(value): value = value() diff --git a/tests/test_options.py b/tests/test_options.py index e335f3c1a..78ccb318c 100644 --- a/tests/test_options.py +++ b/tests/test_options.py @@ -1435,7 +1435,7 @@ def test_type_from_flag_value(): # Not passing --foo returns the default value as-is, in its Python type, then # converted by the option type. ({"type": bool, "default": True, "flag_value": True}, [], True), - ({"type": bool, "default": True, "flag_value": False}, [], False), + ({"type": bool, "default": True, "flag_value": False}, [], True), ({"type": bool, "default": False, "flag_value": True}, [], False), ({"type": bool, "default": False, "flag_value": False}, [], False), ({"type": bool, "default": None, "flag_value": True}, [], None), @@ -1480,6 +1480,21 @@ def cmd(foo): assert result.output == repr(expected) +def test_negative_boolean_flag_value_preserves_true_default(runner): + @click.command() + @click.option( + "--without-xyz", "enable_xyz", is_flag=True, flag_value=False, default=True + ) + def cmd(enable_xyz): + click.echo(repr(enable_xyz), nl=False) + + result = runner.invoke(cmd, []) + assert result.output == "True" + + result = runner.invoke(cmd, ["--without-xyz"]) + assert result.output == "False" + + @pytest.mark.parametrize( ("args", "opts"), [ From 902c27625f21a0d584edaebc16f80e95fcee86a9 Mon Sep 17 00:00:00 2001 From: polusV <12313034@mail.sustech.edu.cn> Date: Sat, 18 Apr 2026 11:19:35 +0800 Subject: [PATCH 2/2] Update prompt expectations for bool flag default=True and flag_value=False --- tests/test_termui.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_termui.py b/tests/test_termui.py index 8220431bb..7f0881779 100644 --- a/tests/test_termui.py +++ b/tests/test_termui.py @@ -604,9 +604,9 @@ def cmd(arg1): ({"prompt": True, "default": True, "flag_value": True}, [], "[Y/n]", "", True), ({"prompt": True, "default": True, "flag_value": True}, [], "[Y/n]", "y", True), ({"prompt": True, "default": True, "flag_value": True}, [], "[Y/n]", "n", False), - ({"prompt": True, "default": True, "flag_value": False}, [], "[y/N]", "", False), - ({"prompt": True, "default": True, "flag_value": False}, [], "[y/N]", "y", True), - ({"prompt": True, "default": True, "flag_value": False}, [], "[y/N]", "n", False), + ({"prompt": True, "default": True, "flag_value": False}, [], "[Y/n]", "", True), + ({"prompt": True, "default": True, "flag_value": False}, [], "[Y/n]", "y", True), + ({"prompt": True, "default": True, "flag_value": False}, [], "[Y/n]", "n", False), # default=False ({"prompt": True, "default": False, "flag_value": True}, [], "[y/N]", "", False), ({"prompt": True, "default": False, "flag_value": True}, [], "[y/N]", "y", True),