diff options
author | David Lord <davidism@gmail.com> | 2021-07-04 07:56:52 -0700 |
---|---|---|
committer | David Lord <davidism@gmail.com> | 2021-07-04 07:56:52 -0700 |
commit | ce152bd7215038f762ee918e6e181a5309ad57c9 (patch) | |
tree | 7235fa389357c4cdcbf707018877a8ca6ed0f152 | |
parent | ff72d7d50cae327786b73415f0f4dc348996215c (diff) | |
parent | 76cc18d1f0bd5854f14526e3ccbce631d8344cce (diff) | |
download | click-ce152bd7215038f762ee918e6e181a5309ad57c9.tar.gz |
Merge remote-tracking branch 'origin/8.0.x'
-rw-r--r-- | CHANGES.rst | 8 | ||||
-rw-r--r-- | CONTRIBUTING.rst | 10 | ||||
-rw-r--r-- | docs/api.rst | 2 | ||||
-rw-r--r-- | docs/utils.rst | 9 | ||||
-rw-r--r-- | examples/completion/README | 6 | ||||
-rw-r--r-- | examples/completion/completion.py | 5 | ||||
-rw-r--r-- | src/click/core.py | 13 | ||||
-rw-r--r-- | src/click/shell_completion.py | 13 | ||||
-rw-r--r-- | tests/test_options.py | 30 | ||||
-rw-r--r-- | tests/test_shell_completion.py | 8 |
10 files changed, 87 insertions, 17 deletions
diff --git a/CHANGES.rst b/CHANGES.rst index 3723346..3efa72a 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -11,6 +11,14 @@ Version 8.0.2 Unreleased +- ``is_bool_flag`` is not set to ``True`` if ``is_flag`` is ``False``. + :issue:`1925` +- Bash version detection is locale independent. :issue:`1940` +- Empty ``default`` value is not shown for ``multiple=True``. + :issue:`1969` +- Fix shell completion for arguments that start with a forward slash + such as absolute file paths. :issue:`1929` + Version 8.0.1 ------------- diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 80273eb..1ad3bdc 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -92,7 +92,7 @@ First time setup .. code-block:: text - git remote add fork https://github.com/{username}/click + $ git remote add fork https://github.com/{username}/click - Create a virtualenv. @@ -107,6 +107,12 @@ First time setup > env\Scripts\activate +- Upgrade pip and setuptools. + + .. code-block:: text + + $ python -m pip install --upgrade pip setuptools + - Install the development dependencies, then install Click in editable mode. @@ -138,7 +144,7 @@ Start coding .. code-block:: text $ git fetch origin - $ git checkout -b your-branch-name origin/7.x + $ git checkout -b your-branch-name origin/8.0.x If you're submitting a feature addition or change, branch off of the "main" branch. diff --git a/docs/api.rst b/docs/api.rst index 23d6af3..5133085 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -141,6 +141,8 @@ Types .. autoclass:: FloatRange +.. autoclass:: DateTime + .. autoclass:: Tuple .. autoclass:: ParamType diff --git a/docs/utils.rst b/docs/utils.rst index fb7d3c2..995c171 100644 --- a/docs/utils.rst +++ b/docs/utils.rst @@ -339,13 +339,18 @@ Example usage:: Showing Progress Bars --------------------- -.. versionadded:: 2.0 - Sometimes, you have command line scripts that need to process a lot of data, but you want to quickly show the user some progress about how long that will take. Click supports simple progress bar rendering for that through the :func:`progressbar` function. +.. note:: + + If you find that you have requirements beyond what Click's progress + bar supports, try using `tqdm`_. + + .. _tqdm: https://tqdm.github.io/ + The basic usage is very simple: the idea is that you have an iterable that you want to operate on. For each item in the iterable it might take some time to do processing. So say you have a loop like this:: diff --git a/examples/completion/README b/examples/completion/README index f15654e..372b1d4 100644 --- a/examples/completion/README +++ b/examples/completion/README @@ -11,18 +11,18 @@ For Bash: .. code-block:: bash - eval "$(_COMPLETION_COMPLETE=source_bash completion)" + eval "$(_COMPLETION_COMPLETE=bash_source completion)" For Zsh: .. code-block:: zsh - eval "$(_COMPLETION_COMPLETE=source_zsh completion)" + eval "$(_COMPLETION_COMPLETE=zsh_source completion)" For Fish: .. code-block:: fish - eval (env _COMPLETION_COMPLETE=source_fish completion) + eval (env _COMPLETION_COMPLETE=fish_source completion) Now press tab (maybe twice) after typing something to see completions. diff --git a/examples/completion/completion.py b/examples/completion/completion.py index abd09c0..6c45a1f 100644 --- a/examples/completion/completion.py +++ b/examples/completion/completion.py @@ -38,10 +38,13 @@ def list_users(ctx, param, incomplete): # of CompletionItem. You can match on whatever you want, including # the help. items = [("bob", "butcher"), ("alice", "baker"), ("jerry", "candlestick maker")] + out = [] for value, help in items: if incomplete in value or incomplete in help: - yield CompletionItem(value, help=help) + out.append(CompletionItem(value, help=help)) + + return out @group.command(help="Choose a user") diff --git a/src/click/core.py b/src/click/core.py index e2ccf59..34fae4b 100644 --- a/src/click/core.py +++ b/src/click/core.py @@ -2197,7 +2197,7 @@ class Parameter: self, ctx: Context, call: bool = True ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: """Get the default for the parameter. Tries - :meth:`Context.lookup_value` first, then the local default. + :meth:`Context.lookup_default` first, then the local default. :param ctx: Current context. :param call: If the default is a callable, call it. Disable to @@ -2461,7 +2461,7 @@ class Option(Parameter): def __init__( self, param_decls: t.Optional[t.Sequence[str]] = None, - show_default: bool = False, + show_default: t.Union[bool, str] = False, prompt: t.Union[bool, str] = False, confirmation_prompt: t.Union[bool, str] = False, prompt_required: bool = True, @@ -2528,7 +2528,7 @@ class Option(Parameter): self.type = types.convert_type(None, flag_value) self.is_flag: bool = is_flag - self.is_bool_flag = isinstance(self.type, types.BoolParamType) + self.is_bool_flag = is_flag and isinstance(self.type, types.BoolParamType) self.flag_value: t.Any = flag_value # Counting @@ -2590,7 +2590,7 @@ class Option(Parameter): for decl in decls: if decl.isidentifier(): if name is not None: - raise TypeError("Name defined twice") + raise TypeError(f"Name '{name}' defined twice") name = decl else: split_char = ";" if decl[:1] == "/" else "/" @@ -2748,7 +2748,8 @@ class Option(Parameter): else: default_string = str(default_value) - extra.append(_("default: {default}").format(default=default_string)) + if default_string: + extra.append(_("default: {default}").format(default=default_string)) if isinstance(self.type, types._NumberRangeBase): range_str = self.type._describe_range() @@ -2760,7 +2761,7 @@ class Option(Parameter): extra.append(_("required")) if extra: - extra_str = ";".join(extra) + extra_str = "; ".join(extra) help = f"{help} [{extra_str}]" if help else f"[{extra_str}]" return ("; " if any_prefix_is_slash else " / ").join(rv), help diff --git a/src/click/shell_completion.py b/src/click/shell_completion.py index 706fb69..cad080d 100644 --- a/src/click/shell_completion.py +++ b/src/click/shell_completion.py @@ -302,8 +302,10 @@ class BashComplete(ShellComplete): def _check_version(self) -> None: import subprocess - output = subprocess.run(["bash", "--version"], stdout=subprocess.PIPE) - match = re.search(r"version (\d)\.(\d)\.\d", output.stdout.decode()) + output = subprocess.run( + ["bash", "-c", "echo ${BASH_VERSION}"], stdout=subprocess.PIPE + ) + match = re.search(r"^(\d+)\.(\d+)\.\d+", output.stdout.decode()) if match is not None: major, minor = match.groups() @@ -448,7 +450,12 @@ def _is_incomplete_argument(ctx: Context, param: Parameter) -> bool: def _start_of_option(value: str) -> bool: """Check if the value looks like the start of an option.""" - return not value[0].isalnum() if value else False + if not value: + return False + + c = value[0] + # Allow "/" since that starts a path. + return not c.isalnum() and c != "/" def _is_incomplete_option(args: t.List[str], param: Parameter) -> bool: diff --git a/tests/test_options.py b/tests/test_options.py index 323d1c4..53179e8 100644 --- a/tests/test_options.py +++ b/tests/test_options.py @@ -4,6 +4,7 @@ import re import pytest import click +from click import Option def test_prefixes(runner): @@ -727,6 +728,16 @@ def test_do_not_show_no_default(runner): assert "[default: None]" not in message +def test_do_not_show_default_empty_multiple(): + """When show_default is True and multiple=True is set, it should not + print empty default value in --help output. + """ + opt = click.Option(["-a"], multiple=True, help="values", show_default=True) + ctx = click.Context(click.Command("cli")) + message = opt.get_help_record(ctx)[1] + assert message == "values" + + @pytest.mark.parametrize( ("args", "expect"), [ @@ -763,3 +774,22 @@ def test_type_from_flag_value(): assert param.type is click.INT param = click.Option(["-b", "x"], flag_value=8) assert param.type is click.INT + + +@pytest.mark.parametrize( + ("option", "expected"), + [ + # Not boolean flags + pytest.param(Option(["-a"], type=int), False, id="int option"), + pytest.param(Option(["-a"], type=bool), False, id="bool non-flag [None]"), + pytest.param(Option(["-a"], default=True), False, id="bool non-flag [True]"), + pytest.param(Option(["-a"], default=False), False, id="bool non-flag [False]"), + pytest.param(Option(["-a"], flag_value=1), False, id="non-bool flag_value"), + # Boolean flags + pytest.param(Option(["-a"], is_flag=True), True, id="is_flag=True"), + pytest.param(Option(["-a/-A"]), True, id="secondary option [implicit flag]"), + pytest.param(Option(["-a"], flag_value=True), True, id="bool flag_value"), + ], +) +def test_is_bool_flag_is_correctly_set(option, expected): + assert option.is_bool_flag is expected diff --git a/tests/test_shell_completion.py b/tests/test_shell_completion.py index ac34ef9..46143c0 100644 --- a/tests/test_shell_completion.py +++ b/tests/test_shell_completion.py @@ -125,6 +125,14 @@ def test_path_types(type, expect): assert c.type == expect +def test_absolute_path(): + cli = Command("cli", params=[Option(["-f"], type=Path())]) + out = _get_completions(cli, ["-f"], "/ab") + assert len(out) == 1 + c = out[0] + assert c.value == "/ab" + + def test_option_flag(): cli = Command( "cli", |