Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

detect type from flag_value #1913

Merged
merged 1 commit into from
May 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ Unreleased
3.8. :issue:`1889`
- Arguments with ``nargs=-1`` only use env var value if no command
line values are given. :issue:`1903`
- Flag options guess their type from ``flag_value`` if given, like
regular options do from ``default``. :issue:`1886`


Version 8.0.0
Expand Down
32 changes: 15 additions & 17 deletions src/click/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from gettext import ngettext
from itertools import repeat

from . import types
from ._unicodefun import _verify_python_env
from .exceptions import Abort
from .exceptions import BadParameter
Expand All @@ -30,11 +31,6 @@
from .termui import confirm
from .termui import prompt
from .termui import style
from .types import _NumberRangeBase
from .types import BOOL
from .types import convert_type
from .types import IntRange
from .types import ParamType
from .utils import _detect_program_name
from .utils import _expand_args
from .utils import echo
Expand Down Expand Up @@ -2010,7 +2006,7 @@ class Parameter:
def __init__(
self,
param_decls: t.Optional[t.Sequence[str]] = None,
type: t.Optional[t.Union["ParamType", t.Any]] = None,
type: t.Optional[t.Union[types.ParamType, t.Any]] = None,
required: bool = False,
default: t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]] = None,
callback: t.Optional[t.Callable[[Context, "Parameter", t.Any], t.Any]] = None,
Expand All @@ -2035,8 +2031,7 @@ def __init__(
self.name, self.opts, self.secondary_opts = self._parse_decls(
param_decls or (), expose_value
)

self.type = convert_type(type, default)
self.type = types.convert_type(type, default)

# Default nargs to what the type tells us if we have that
# information available.
Expand Down Expand Up @@ -2439,6 +2434,9 @@ class Option(Parameter):
context.
:param help: the help string.
:param hidden: hide this option from help outputs.

.. versionchanged:: 8.0.1
``type`` is detected from ``flag_value`` if given.
"""

param_type_name = "option"
Expand All @@ -2456,7 +2454,7 @@ def __init__(
multiple: bool = False,
count: bool = False,
allow_from_autoenv: bool = True,
type: t.Optional[t.Union["ParamType", t.Any]] = None,
type: t.Optional[t.Union[types.ParamType, t.Any]] = None,
help: t.Optional[str] = None,
hidden: bool = False,
show_choices: bool = True,
Expand Down Expand Up @@ -2507,20 +2505,20 @@ def __init__(
if flag_value is None:
flag_value = not self.default

if is_flag and type is None:
# Re-guess the type from the flag value instead of the
# default.
self.type = types.convert_type(None, flag_value)

self.is_flag: bool = is_flag
self.is_bool_flag = isinstance(self.type, types.BoolParamType)
self.flag_value: t.Any = flag_value

if self.is_flag and isinstance(self.flag_value, bool) and type in [None, bool]:
self.type: "ParamType" = BOOL
self.is_bool_flag = True
else:
self.is_bool_flag = False

# Counting
self.count = count
if count:
if type is None:
self.type = IntRange(min=0)
self.type = types.IntRange(min=0)
if default_is_missing:
self.default = 0

Expand Down Expand Up @@ -2725,7 +2723,7 @@ def _write_opts(opts: t.Sequence[str]) -> str:

extra.append(_("default: {default}").format(default=default_string))

if isinstance(self.type, _NumberRangeBase):
if isinstance(self.type, types._NumberRangeBase):
range_str = self.type._describe_range()

if range_str:
Expand Down
5 changes: 1 addition & 4 deletions src/click/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -1000,10 +1000,7 @@ def convert_type(ty: t.Optional[t.Any], default: t.Optional[t.Any] = None) -> Pa
if ty is float:
return FLOAT

# Booleans are only okay if not guessed. For is_flag options with
# flag_value, default=True indicates which flag_value is the
# default.
if ty is bool and not guessed_type:
if ty is bool:
return BOOL

if guessed_type:
Expand Down
7 changes: 7 additions & 0 deletions tests/test_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -756,3 +756,10 @@ def cli(opt, a, b):

result = runner.invoke(cli, args, standalone_mode=False, catch_exceptions=False)
assert result.return_value == expect


def test_type_from_flag_value():
param = click.Option(["-a", "x"], default=True, flag_value=4)
assert param.type is click.INT
param = click.Option(["-b", "x"], flag_value=8)
assert param.type is click.INT