Skip to content
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
5 changes: 5 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ Unreleased
- A command's short help no longer stops at an abbreviation such as `vs.` or `e.g.`
inside the first sentence. A period ends the sentence only when it closes the text
or the next word does not start in lowercase. {pr}`3865`
- With `show_envvar=True`, an option's error message now prints all variables of an
`envvar` sequence instead of the `repr` of the sequence. {pr}`3884`
- An empty `envvar`, such as `[]` or `""`, shows the same help and error message as
no `envvar` when `show_envvar=True`. The help screen used to show `[env var: ]`.
{pr}`3884`

## Version 8.5.0

Expand Down
23 changes: 13 additions & 10 deletions src/click/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -3270,8 +3270,12 @@ def get_default(

def get_error_hint(self, ctx: Context | None) -> str:
result = super().get_error_hint(ctx)
if self.show_envvar and self.envvar is not None:
result += f" (env var: '{self.envvar}')"
if self.show_envvar and self.envvar:
envvars = [self.envvar] if isinstance(self.envvar, str) else self.envvar
# An environment variable is allowed to feature a space or a comma on all
# platforms, hence the quotes around each.
var_str = ", ".join(f"'{var}'" for var in envvars)
result += f" (env var: {var_str})"
return result

def _parse_decls(
Expand Down Expand Up @@ -3439,15 +3443,14 @@ def get_help_extra(self, ctx: Context) -> types.OptionHelpExtra:
if self.show_envvar:
envvar = self.envvar

if envvar is None:
if (
self.allow_from_autoenv
and ctx.auto_envvar_prefix is not None
and self.name
):
envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}"
if not envvar and (
self.allow_from_autoenv
and ctx.auto_envvar_prefix is not None
and self.name
):
envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}"

if envvar is not None:
if envvar:
if isinstance(envvar, str):
extra["envvars"] = (envvar,)
else:
Expand Down
36 changes: 36 additions & 0 deletions tests/test_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,23 @@ def foo_bar(baz):
assert "TEST_FOO_BAR_BAZ" in result.output


@pytest.mark.parametrize("envvar", [None, [], ""])
@pytest.mark.parametrize(
("auto_envvar_prefix", "expected"),
[
(None, " --units TEXT\n"),
("WEATHER", " --units TEXT [env var: WEATHER_UNITS]\n"),
],
)
def test_show_envvar_empty(runner, envvar, auto_envvar_prefix, expected):
"""An empty ``envvar`` shows the same help as no ``envvar``."""
option = click.Option(["--units"], envvar=envvar, show_envvar=True)
cli = click.Command("cli", params=[option])
result = runner.invoke(cli, ["--help"], auto_envvar_prefix=auto_envvar_prefix)
assert not result.exception
assert expected in result.output


def test_custom_validation(runner):
def validate_pos_int(ctx, param, value):
if value < 0:
Expand Down Expand Up @@ -1018,6 +1035,25 @@ def test_missing_envvar(runner):
assert "Error: Missing option '--foo'." in result.output


@pytest.mark.parametrize(
("envvar", "expected"),
[
("UNITS", "'--units' (env var: 'UNITS')"),
(["UNITS"], "'--units' (env var: 'UNITS')"),
(["WEATHER_UNITS", "UNITS"], "'--units' (env var: 'WEATHER_UNITS', 'UNITS')"),
(("WEATHER_UNITS", "UNITS"), "'--units' (env var: 'WEATHER_UNITS', 'UNITS')"),
([], "'--units'"),
("", "'--units'"),
],
)
def test_missing_envvar_sequence(runner, envvar, expected):
"""The error hint names each variable of a sequence ``envvar``."""
option = click.Option(["--units"], envvar=envvar, show_envvar=True, required=True)
result = runner.invoke(click.Command("cli", params=[option]))
assert result.exit_code == 2
assert f"Error: Missing option {expected}." in result.output


def test_case_insensitive_choice(runner):
@click.command()
@click.option("--foo", type=click.Choice(["Orange", "Apple"], case_sensitive=False))
Expand Down
Loading