Skip to content
Closed
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 @@ -4,6 +4,8 @@ Unreleased

- Fix handling of ``flag_value`` when ``is_flag=False`` to allow such options to be
used without an explicit value. :issue:`3084`
- Fix error message for unrecognized multicharacter short options (e.g. ``-dbg``)
to report the full option name instead of just the first character. :issue:`2779`

Version 8.3.1
--------------
Expand Down
12 changes: 11 additions & 1 deletion src/click/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,17 @@ def _process_opts(self, arg: str, state: _ParsingState) -> None:
# short option code and will instead raise the no option
# error.
if arg[:2] not in self._opt_prefixes:
self._match_short_opt(arg, state)
try:
self._match_short_opt(arg, state)
except NoSuchOption as e:
# If the short option parser fails on the very first
# character, the original argument is likely a
# multicharacter short option (e.g. ``-dbg``) that
# wasn't found, so report the full original option
# name instead of just the first character.
if e.option_name == f"{arg[0]}{arg[1]}":
raise NoSuchOption(long_opt, ctx=self.ctx) from None
raise
return

if not self.ignore_unknown_options:
Expand Down
24 changes: 24 additions & 0 deletions tests/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,27 @@ def test_parser_collects_prefixes():
click.Option("+p", is_flag=True).add_to_parser(parser, ctx)
click.Option("!e", is_flag=True).add_to_parser(parser, ctx)
assert parser._opt_prefixes == {"-", "--", "+", "!"}


def test_multichar_short_option_error_message():
"""A multicharacter short option that isn't found should report the
full option name in the error, not just the first character. See #2779."""

@click.command()
@click.option("-dbg", is_flag=True)
def cli(dbg):
pass

runner = click.testing.CliRunner()

# Passing the registered option should work.
result = runner.invoke(cli, ["-dbg"])
assert result.exit_code == 0

# Passing an unrecognized variant should report the full option name,
# not just the first character of the option.
result = runner.invoke(cli, ["-dbgwrong"])
assert result.exit_code != 0
assert "No such option: -dbgwrong" in result.output
# The error should NOT be truncated to just the first character "-d".
assert result.output.rstrip().endswith("No such option: -dbgwrong")
Loading