diff --git a/CHANGES.rst b/CHANGES.rst index 4438c1525..7f103d054 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -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 diff --git a/src/click/core.py b/src/click/core.py index ab325b2eb..63506bab9 100644 --- a/src/click/core.py +++ b/src/click/core.py @@ -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 @@ -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 @@ -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, @@ -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. @@ -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" @@ -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, @@ -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 @@ -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: diff --git a/src/click/types.py b/src/click/types.py index 86aaae090..21f0e4f77 100644 --- a/src/click/types.py +++ b/src/click/types.py @@ -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: diff --git a/tests/test_options.py b/tests/test_options.py index 94e5eb8d7..50e6b5ab0 100644 --- a/tests/test_options.py +++ b/tests/test_options.py @@ -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