Repository: pallets/click

Issue:
Flags with `multiple=True` trigger `TypeError`
Adding an option with `is_flag=True` and `multiple=True` will trigger a `TypeError` in `Parameter.consume_value()` due to bad default value.

```py
import click

@click.command()
@click.option("-v", is_flag=True, multiple=True)
def main(v):
    click.echo(v)

if __name__ == "__main__":
    main()
```

When run without the flag, a TypeError is raised:

```
(venv39) jreese@butterfree ~/scratch » python foo.py
Traceback (most recent call last):
  File "/Users/jreese/scratch/foo.py", line 25, in <module>
    main()
  File "/Users/jreese/scratch/venv39/lib/python3.9/site-packages/click/core.py", line 1130, in __call__
    return self.main(*args, **kwargs)
  File "/Users/jreese/scratch/venv39/lib/python3.9/site-packages/click/core.py", line 1054, in main
    with self.make_context(prog_name, args, **extra) as ctx:
  File "/Users/jreese/scratch/venv39/lib/python3.9/site-packages/click/core.py", line 920, in make_context
    self.parse_args(ctx, args)
  File "/Users/jreese/scratch/venv39/lib/python3.9/site-packages/click/core.py", line 1378, in parse_args
    value, args = param.handle_parse_result(ctx, opts, args)
  File "/Users/jreese/scratch/venv39/lib/python3.9/site-packages/click/core.py", line 2356, in handle_parse_result
    value, source = self.consume_value(ctx, opts)
  File "/Users/jreese/scratch/venv39/lib/python3.9/site-packages/click/core.py", line 2903, in consume_value
    and any(v is _flag_needs_value for v in value)
TypeError: 'bool' object is not iterable
```

Manually setting a better default value, like `default=()`, avoids the type error, but this used to work on 7.x without setting the default value.

Environment:

- Python version: tested on 3.8.6 and 3.9.3
- Click version: worked on 7.1.2; errors on 8.0.4 and 8.1.2



Discussion:

Resolving patch:
diff --git a/src/click/core.py b/src/click/core.py
--- a/src/click/core.py
+++ b/src/click/core.py
@@ -2580,6 +2580,9 @@ 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'.")
+
     def to_info_dict(self) -> t.Dict[str, t.Any]:
         info_dict = super().to_info_dict()
         info_dict.update(


Test patch:
diff --git a/tests/test_options.py b/tests/test_options.py
--- a/tests/test_options.py
+++ b/tests/test_options.py
@@ -904,3 +904,21 @@ def test_type_from_flag_value():
 )
 def test_is_bool_flag_is_correctly_set(option, expected):
     assert option.is_bool_flag is expected
+
+
+@pytest.mark.parametrize(
+    ("kwargs", "message"),
+    [
+        ({"count": True, "multiple": True}, "'count' is not valid with 'multiple'."),
+        ({"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'.",
+        ),
+    ],
+)
+def test_invalid_flag_combinations(runner, kwargs, message):
+    with pytest.raises(TypeError) as e:
+        click.Option(["-a"], **kwargs)
+
+    assert message in str(e.value)