Skip to content

Commit

Permalink
Allow is_flag=True, multiple=True with non-bool flag_value
Browse files Browse the repository at this point in the history
Allow creating options with is_flag=True and multiple=True if
flag_value is also set to a non-bool value.

Fixes #2292
  • Loading branch information
Brandon Carpenter committed May 24, 2022
1 parent 9c6f4c8 commit d05e68f
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 3 deletions.
10 changes: 10 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
.. currentmodule:: click

Version 8.1.4
-------------

Released TBD

- Do not show an error when attempting to create an option with
``multiple=True, is_flag=True`` if ``flag_value`` is not a ``bool``.
:issue:`2292`


Version 8.1.3
-------------

Expand Down
8 changes: 6 additions & 2 deletions src/click/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -2580,8 +2580,12 @@ def __init__(
if self.is_flag:
raise TypeError("'count' is not valid with 'is_flag'.")

if self.multiple and self.is_flag:
raise TypeError("'multiple' is not valid with 'is_flag', use 'count'.")
if self.multiple and self.is_flag and isinstance(self.flag_value, bool):
raise TypeError(
"'multiple' is not valid with 'is_flag' and a bool "
"'flag_value'; use 'count' or set 'flag_value' to a "
"non-bool value."
)

def to_info_dict(self) -> t.Dict[str, t.Any]:
info_dict = super().to_info_dict()
Expand Down
35 changes: 34 additions & 1 deletion tests/test_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -913,7 +913,7 @@ def test_is_bool_flag_is_correctly_set(option, expected):
({"count": True, "is_flag": True}, "'count' is not valid with 'is_flag'."),
(
{"multiple": True, "is_flag": True},
"'multiple' is not valid with 'is_flag', use 'count'.",
"'multiple' is not valid with 'is_flag' and a bool 'flag_value'",
),
],
)
Expand All @@ -922,3 +922,36 @@ def test_invalid_flag_combinations(runner, kwargs, message):
click.Option(["-a"], **kwargs)

assert message in str(e.value)


@pytest.mark.parametrize(
("args", "expected"),
[
([], "tags= verbosity=0"),
(["--foo", "--bar", "--baz"], "tags=foo,bar,baz verbosity=0"),
(["--bar", "--foo", "-vv"], "tags=bar,foo verbosity=2"),
(["--verbose"], "tags= verbosity=1"),
(["--quiet"], "tags= verbosity=-1"),
(["--bar", "--foo", "-m", "foo", "-vvvq"], "tags=bar,foo,foo verbosity=2"),
],
)
def test_multi_value_flags(runner, args, expected):
@click.command()
@click.option("--bar", "tags", flag_value="bar", multiple=True)
@click.option("--baz", "tags", flag_value="baz", multiple=True)
@click.option("--foo", "tags", flag_value="foo", multiple=True)
@click.option(
"-m", "--tag", "tags", type=click.Choice(["foo", "bar", "baz"]), multiple=True
)
@click.option(
"-q", "--quiet", "verbosity", is_flag=True, flag_value=-1, multiple=True
)
@click.option(
"-v", "--verbose", "verbosity", is_flag=True, flag_value=1, multiple=True
)
def cli(tags, verbosity):
click.echo(f"tags={','.join(tags)} verbosity={sum(verbosity)}", nl=False)

result = runner.invoke(cli, args)
assert not result.exception
assert result.output == expected

0 comments on commit d05e68f

Please sign in to comment.