Skip to content

Don't break usage option tokens at internal hyphens#3388

Closed
charlieleith wants to merge 1 commit intopallets:mainfrom
charlieleith:fix-3362-usage-hyphen-wrap
Closed

Don't break usage option tokens at internal hyphens#3388
charlieleith wants to merge 1 commit intopallets:mainfrom
charlieleith:fix-3362-usage-hyphen-wrap

Conversation

@charlieleith
Copy link
Copy Markdown

fixes #3362

Summary

HelpFormatter.write_usage runs its argument string through wrap_text, which builds a textwrap.TextWrapper. TextWrapper.break_on_hyphens defaults to True — the right call for prose, the wrong call here, where each usage token is a long flag like --enable-verbose-logging and an internal hyphen is part of the token, not a natural break point.

Repro from the issue:

```python
import click

options = [
"--enable-verbose-logging",
"--output-file-path",
"--max-retry-count",
"--disable-cache-mode",
"--config-file-location",
"--user-auth-token",
"--auto-update-interval",
"--force-overwrite-existing",
"--network-timeout-seconds",
"--debug-trace-enabled",
]
f = click.HelpFormatter(width=65)
f.write_usage("program", " ".join(options))
print(f.getvalue())
```

Today, this prints fragmented tokens:

```
Usage: program --enable-verbose-logging --output-file-path --max-
retry-count --disable-cache-mode --config-file-
location --user-auth-token --auto-update-interval
--force-overwrite-existing --network-timeout-
seconds --debug-trace-enabled
```

After the fix, hyphenated tokens stay whole and only break on whitespace:

```
Usage: program --enable-verbose-logging --output-file-path
--max-retry-count --disable-cache-mode
--config-file-location --user-auth-token
--auto-update-interval --force-overwrite-existing
--network-timeout-seconds --debug-trace-enabled
```

Change

  • wrap_text gains a break_on_hyphens: bool = True keyword so callers can opt out without going through TextWrapper directly. Default keeps existing behaviour.
  • Both write_usage paths (prefix-fits and prefix-overflows) pass break_on_hyphens=False.
  • No other callers (write_text, write_dl, write_epilog) change — prose is still allowed to break at hyphens.

Mirrors the precedent of :class:textwrap.TextWrapper exposing the same flag.

Tests

tests/test_formatting.py::test_usage_does_not_break_options_at_internal_hyphen is new and asserts:

  1. No line in the output ends with a dangling hyphen.
  2. Every input token survives intact in the rendered string.

Without the fix the test fails on assertion (1) with the very first wrapped line. Full suite: 1437 passed, 23 skipped, 1 xfailed.

Checklist

  • Add tests that demonstrate the correct behavior of the change. Tests fail without the change.
  • Add or update relevant docs, in the docs folder and in code (`wrap_text` docstring + `versionchanged`).
  • Add an entry in `CHANGES.rst` summarizing the change and linking to the issue.
  • Add `versionchanged` entries in any relevant code docs.

Note

There was an earlier PR (#3385) that took the same general approach and was closed without merge — by the author, not by a maintainer, with no review comments — so reopening with my own write-up.

``HelpFormatter.write_usage`` runs its argument string through
:func:`wrap_text`, which delegates to :class:`textwrap.TextWrapper`.
``TextWrapper.break_on_hyphens`` defaults to ``True``, which is the
right call for prose ("very-much-needed") but the wrong call for
usage args, where ``--enable-verbose-logging`` is a single token.
Today a borderline-length usage line ends up shaped like

    Usage: program --enable-verbose-logging --output-file-path --max-
                   retry-count --disable-cache-mode --config-file-
                   ...

which fragments tokens at arbitrary internal hyphens.

Plumb ``break_on_hyphens`` through ``wrap_text`` and pass
``break_on_hyphens=False`` from both ``write_usage`` paths
(prefix-fits and prefix-overflows). Hyphenated tokens stay intact
and the wrapper still breaks on whitespace between tokens, so they
move to the next line as a unit when they don't fit.

Closes pallets#3362
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

HelpFormatter.write_usage breaks options at a hyphen

2 participants