Skip to content

refactor(cli): migrate argument parsing from Click to Cyclopts - #1901

Merged
wowi42 merged 3 commits into
pyinfra-dev:3.xfrom
noirbizarre:refactor/cyclopts
Aug 25, 2026
Merged

refactor(cli): migrate argument parsing from Click to Cyclopts#1901
wowi42 merged 3 commits into
pyinfra-dev:3.xfrom
noirbizarre:refactor/cyclopts

Conversation

@noirbizarre

@noirbizarre noirbizarre commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Part 1 of 3 of the CLI modernisation, split out of #1894 as requested for easier review.

This migrates command-line argument parsing from Click to Cyclopts, while keeping the existing command syntax, flags and behaviour. Output styling still goes through the pluggable pyinfra.api.output layer wired to Click (set_formatter(click.style) / set_echo(click.echo)), so this change is scoped to parsing only — Rich output arrives in the next PR.

  • cli.py becomes a Cyclopts App with @app.default and typed Annotated parameters; PYINFRA_YES keeps its lenient boolean parsing (empty / on / off).
  • --support is now a Cyclopts command; the previously duplicated support parameter and its unreachable handler are removed.
  • main.py invokes the Cyclopts app and surfaces click.ClickException (CliError etc.) via their .show() / exit code.
  • The confirmation prompt and all human output remain Click-based.
  • pyproject: add cyclopts, keep click (now used for output only).
  • Tests invoke the Cyclopts app through the updated run_cli helper.

📚 Stacked PR series

This is one of three PRs that split the combined CLI refactor (originally #1894) into reviewable layers. They are stacked and land in order; Parts 2 & 3 stay in draft until the preceding part is merged.

Original combined PR (kept for reference/discussion): #1894

Related issue: #785 (use Rich) — referenced, not closed.


🎥 Showcase

Those are here to show what visually changed and what did not (yet).

In this PR, click is still in charge of rendering/styling, rich is not yet introduced.

Help

help

Inventory

inventory current

Success

success current

Error and diff

diff

Multiline output

multiline

@Fizzadar Fizzadar left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple of tweaks then we're good to go I think!

Comment thread src/pyinfra_cli/cli.py Outdated
Comment thread src/pyinfra_cli/cli.py
Comment thread src/pyinfra_cli/cli.py Outdated
Replace Click with Cyclopts for command-line argument parsing while keeping the
existing command syntax, flags and behaviour. Output styling continues to go
through the pluggable `pyinfra.api.output` layer wired to Click
(`set_formatter(click.style)` / `set_echo(click.echo)`), so this change is
scoped to parsing only — Rich output is introduced in a follow-up.

- `cli.py` becomes a Cyclopts `App` with `@app.default` and typed `Annotated`
  parameters; `PYINFRA_YES` keeps its lenient boolean parsing (empty/on/off).
- `--help` groups the options into Execution / Inventory & Data / Privilege
  Escalation / SSH Connection / Debugging & Output sections, and the noisy
  auto-generated `--no-*` negation flags are suppressed.
- `--support` is a Cyclopts command; the previously duplicated `support`
  parameter and its unreachable handler are removed.
- `main.py` invokes the Cyclopts app and surfaces `click.ClickException`
  (CliError etc.) via their `.show()` / exit code.
- The confirmation prompt and all human output remain Click-based.
- pyproject: add `cyclopts`, keep `click` (now used for output only).
- Tests invoke the Cyclopts app through the updated `run_cli` helper.
@noirbizarre

Copy link
Copy Markdown
Contributor Author

All comments addressed. 👍🏼

The version is now iso to the current format:
image

(Good to know for #1753, I love pathlib!)

@wowi42 wowi42 added the CLI CLI mode specific issues. label Aug 16, 2026

@wowi42 wowi42 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed after the requested changes. Approving.

Verified locally on this branch (Python 3.13):

  • pyinfra --version prints pyinfra: v... again (matches the old Click output).
  • pyinfra --support works and exits 0.
  • -vvv bundling still works for verbosity.
  • Missing operations exits 1 with a clear usage message (Click exited 2 with a usage error; the new message is more helpful, fine by me).
  • Unknown option exits 1 (was 2 under Click; acceptable).
  • PYINFRA_YES=off lenient parsing works.
  • Full unit suite passes (1951 passed), ruff and mypy clean.

Two cosmetic Cyclopts differences in --help, not blockers:

  • Flags now show [default: False] and repeatable options show [default: ()], which adds noise Click did not have. If Cyclopts has a flag to hide defaults for these, worth using in Part 2 or 3.
  • The usage line reads pyinfra COMMAND [OPTIONS] INVENTORY [ARGS...]; the COMMAND token is a bit misleading for the default command.

The scripts/pyinfra-complete.{sh,zsh} removal is a breaking change for anyone sourcing those files, but it was requested in the earlier review and --install-completion (which also adds fish support) is a good replacement.

@wowi42
wowi42 merged commit 2062899 into pyinfra-dev:3.x Aug 25, 2026
30 checks passed
yagarea pushed a commit to yagarea/pyinfra that referenced this pull request Aug 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLI CLI mode specific issues.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants