Skip to content

Commit ebcd548

Browse files
authored
Fix handling of options which set both is_flag=False and flag_value=... (pallets#3152)
2 parents 7f7bbe4 + 955ca49 commit ebcd548

File tree

3 files changed

+55
-3
lines changed

3 files changed

+55
-3
lines changed

CHANGES.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
.. currentmodule:: click
22

3+
Unreleased
4+
5+
- Fix handling of ``flag_value`` when ``is_flag=False`` to allow such options to be
6+
used without an explicit value. :issue:`3084`
7+
38
Version 8.3.1
49
--------------
510

src/click/core.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2777,10 +2777,13 @@ def __init__(
27772777
# Implicitly a flag because secondary options names were given.
27782778
elif self.secondary_opts:
27792779
is_flag = True
2780-
# The option is explicitly not a flag. But we do not know yet if it needs a
2781-
# value or not. So we look at the default value to determine it.
2780+
2781+
# The option is explicitly not a flag, but to determine whether or not it needs
2782+
# value, we need to check if `flag_value` or `default` was set. Either one is
2783+
# sufficient.
2784+
# Ref: https://github.com/pallets/click/issues/3084
27822785
elif is_flag is False and not self._flag_needs_value:
2783-
self._flag_needs_value = self.default is UNSET
2786+
self._flag_needs_value = flag_value is not UNSET or self.default is UNSET
27842787

27852788
if is_flag:
27862789
# Set missing default for flags if not explicitly required or prompted.

tests/test_options.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from click import Option
1616
from click import UNPROCESSED
1717
from click._utils import UNSET
18+
from click.testing import CliRunner
1819

1920

2021
def test_prefixes(runner):
@@ -2275,3 +2276,46 @@ def rcli(scm_ignore_files):
22752276
result = runner.invoke(rcli, ["--without-scm-ignore-files"])
22762277
assert result.stdout == "frozenset()"
22772278
assert result.exit_code == 0
2279+
2280+
2281+
@pytest.mark.parametrize(
2282+
("flag_type", "args", "expect_output"),
2283+
[
2284+
(str, [], "Default\n"),
2285+
(str, ["--theflag"], "FlagValue\n"),
2286+
(str, ["--theflag", "value"], "value\n"),
2287+
(int, [], "0\n"),
2288+
(int, ["--theflag"], "1\n"),
2289+
(int, ["--theflag", "2"], "2\n"),
2290+
],
2291+
)
2292+
def test_flag_value_on_option_with_zero_or_one_args(flag_type, args, expect_output):
2293+
"""An option with flag_value and is_flag=False can be
2294+
omitted or used with 0 or 1 args.
2295+
2296+
Regression test for https://github.com/pallets/click/issues/3084
2297+
"""
2298+
if flag_type is str:
2299+
flagopt = click.option(
2300+
"--theflag",
2301+
type=str,
2302+
is_flag=False,
2303+
flag_value="FlagValue",
2304+
default="Default",
2305+
)
2306+
elif flag_type is int:
2307+
flagopt = click.option(
2308+
"--theflag", type=int, is_flag=False, flag_value=1, default=0
2309+
)
2310+
else:
2311+
raise NotImplementedError(flag_type)
2312+
2313+
@click.command()
2314+
@flagopt
2315+
def cmd(theflag):
2316+
click.echo(theflag)
2317+
2318+
runner = CliRunner()
2319+
result = runner.invoke(cmd, args)
2320+
assert result.exit_code == 0
2321+
assert result.output == expect_output

0 commit comments

Comments
 (0)