Skip to content

Switch from docopt to click#176

Open
jsf9k wants to merge 17 commits intodevelopfrom
improvement/switch-from-docopt-to-click
Open

Switch from docopt to click#176
jsf9k wants to merge 17 commits intodevelopfrom
improvement/switch-from-docopt-to-click

Conversation

@jsf9k
Copy link
Copy Markdown
Member

@jsf9k jsf9k commented Jan 7, 2026

🗣 Description

This pull request switches from docopt to click for our CLI argument-parsing needs.

💭 Motivation and context

I noticed that installing docopt generates warnings because it still uses a setup.py file versus a pyproject.toml file. Investigating further, I found that docopt hasn't seen any updates since 2014. I figured now would be a good time to start looking at alternatives.

The main two alternatives right now are click and typer. The former is a bit more verbose, whereas the latter is a bit more opinionated but also more Pythonic. typer uses click under the hood and leverages type hints to do some work for you. Both can generate tab completion for various shells.

See also #175 for the typer implementation. We can choose which one we like better.

🧪 Testing

All automated tests pass.

✅ Pre-approval checklist

  • This PR has an informative and human-readable title.
  • Changes are limited to a single goal - eschew scope creep!
  • All relevant type-of-change labels have been added.
  • I have read the CONTRIBUTING document.
  • These code changes follow cisagov code standards.
  • Tests have been added and/or modified to cover the changes in this PR.
  • All new and existing tests pass.
  • Bump major, minor, patch, pre-release, and/or build versions as appropriate via the bump_version script if this repository is versioned and the changes in this PR warrant a version bump.
  • Create a pre-release (necessary if and only if the pre-release version was bumped).

✅ Pre-merge checklist

  • Finalize version.

✅ Post-merge checklist

  • Create a release (necessary if and only if the version was bumped).

@jsf9k jsf9k self-assigned this Jan 7, 2026
@jsf9k jsf9k added the kraken 🐙 This pull request is ready to merge during the next Lineage Kraken release label Jan 7, 2026
@jsf9k jsf9k moved this to In Progress in Next Kraken Jan 7, 2026
@jsf9k jsf9k changed the title Improvement/switch from docopt to click Switch from docopt to click Jan 7, 2026
@github-actions github-actions Bot added dependencies Pull requests that update a dependency file python Pull requests that update Python code test This issue or pull request adds or otherwise modifies test code labels Jan 7, 2026
@jsf9k jsf9k mentioned this pull request Jan 7, 2026
11 tasks
@jsf9k
Copy link
Copy Markdown
Member Author

jsf9k commented Jan 7, 2026

I personally like this implementation better. You have to repeat yourself a little because click doesn't pick up the CLI argument types from the type hints, but I think the intent is clearer.

Copy link
Copy Markdown
Member

@dav3r dav3r left a comment

Choose a reason for hiding this comment

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

I think both typer and click seem fine for our needs. I asked Google about it and here's what it came back with:

Pick Typer if: You want a modern developer experience with excellent autocompletion and less boilerplate. It is ideal if you are already using FastAPI, as it shares the same design philosophy.

Pick Click if: You need a proven, stable tool with maximum control and minimal dependencies. It is often preferred for very large, complex CLI structures where explicit control over every parameter is necessary.

Hybrid Approach: Because Typer is built on Click, you can use Typer for your main interface but drop down into Click for advanced features that Typer might not yet support.

That being said, I have no major preference here and will gladly go with whichever one our team feels more strongly about.

Additional info from Google:
Image

Comment thread src/example/example.py Outdated
@github-actions github-actions Bot added the version bump This issue or pull request increments the version number label Jan 7, 2026
@jsf9k
Copy link
Copy Markdown
Member Author

jsf9k commented Jan 7, 2026

Any thoughts, @felddy and @mcdonnnj? I'm leaning toward this implementation and @dav3r is OK with either.

@jsf9k jsf9k moved this from In progress to Review in progress in Skeleton Maintenance Jan 7, 2026
@jsf9k jsf9k marked this pull request as ready for review January 7, 2026 19:27
@jsf9k jsf9k requested review from felddy and mcdonnnj as code owners January 7, 2026 19:27
Copy link
Copy Markdown
Contributor

@felddy felddy left a comment

Choose a reason for hiding this comment

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

Nice conversion. I like this over #175. I agree that it is more Pythonic.

@github-project-automation github-project-automation Bot moved this from Review in progress to Reviewer approved in Skeleton Maintenance Jan 8, 2026
@jsf9k jsf9k requested a review from Copilot March 2, 2026 19:03
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR migrates the project CLI from docopt/schema to click, updating the entrypoints and tests accordingly.

Changes:

  • Replaced docopt-based parsing/validation with a click command (setup_logging_and_divide) and parameter validation callbacks.
  • Updated tests to use click.testing.CliRunner and adjusted expectations for Click-style exit codes/output.
  • Updated packaging metadata: dependencies, console script target, and pre-commit mypy deps.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/test_example.py Switches CLI tests to invoke the Click command via CliRunner and updates assertions.
src/example/example.py Replaces docopt CLI with Click command/options/arguments and adds Rich logging handler + callbacks.
src/example/_version.py Bumps project version for the prerelease.
src/example/main.py Updates module entrypoint to run the Click command.
src/example/init.py Updates exported API names to match renamed functions/command.
pyproject.toml Swaps dependencies and changes the console script entrypoint to the Click command.
.pre-commit-config.yaml Removes mypy stub dependency associated with docopt.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/example/example.py Outdated
Comment thread src/example/example.py Outdated
Comment thread src/example/example.py Outdated
Comment thread tests/test_example.py Outdated
Copy link
Copy Markdown
Member

@dav3r dav3r left a comment

Choose a reason for hiding this comment

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

🎉

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/example/example.py Outdated
Comment thread src/example/example.py Outdated
Comment thread tests/test_example.py
Comment thread tests/test_example.py Outdated
@jsf9k jsf9k force-pushed the improvement/switch-from-docopt-to-click branch from e1e3049 to 56584a2 Compare April 13, 2026 18:10
jsf9k and others added 16 commits April 13, 2026 14:56
docopt hasn't been updated in years and is still built using setup.py.
click is a more modern CLI library.

For more information see:
- https://click.palletsprojects.com/en/stable/
- https://github.com/pallets/click
Also update the version tests to deal with the version output that
click generates by default.
This is a little cleaner and results in clightly more readable code
than patching argv.
Also go ahead and export the setup_logging_and_divide function as part
of the module.
Co-authored-by: dav3r <david.redmin@gwe.cisa.dhs.gov>
This will allow the feature gate to remain valid across major versions.

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
The new docstring describes more fully what the function does.

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This is what docopt used to do, so to avoid an unexpected change we should do the same.

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
capsys is no longer used in test_stdout_version after switching to CliRunner.

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This is possible now that `--version` returns only the version string.
This gets rid of the following warning from our flake8 pre-commit
hook:
C408 Unnecessary list call - rewrite as a literal
This gets rid of the following warning from our flake8 pre-commit
hook:
C408 Unnecessary dict call - rewrite as a literal.
This gets rid of the following error from our flake8 pre-commit hook:
B950 line too long (94 > 80 characters)
@jsf9k jsf9k force-pushed the improvement/switch-from-docopt-to-click branch from 168013e to bf8d49d Compare April 13, 2026 18:56
These dependencies will be commonly used with this type of repo, so we
may as well handle them at the skeleton level.
@jsf9k jsf9k requested review from rm-sbin-sh and removed request for rm-sbin-sh April 14, 2026 15:16
Copy link
Copy Markdown

@rm-sbin-sh rm-sbin-sh left a comment

Choose a reason for hiding this comment

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

See comments. Looks good.

Comment thread src/example/example.py
Copy link
Copy Markdown
Member

@mcdonnnj mcdonnnj left a comment

Choose a reason for hiding this comment

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

So I have a more general issue after reviewing this PR and looking up more about Click. I know that it is a popular options, however I am not a fan of the forced decorator approach and how functions get wrapped to start. The use of either the .callback() attribute or using invoke() make reusability of functions difficult. You are forced to either use the above methods or create CLI command wrapper functions that in turn access the real functionality.

I much prefer the ability to use dataclasses or dataclass-like objects (Pydantic models/dataclasses for example) to define command behavior using Python type hints. There are a number of options out there so I'm not trying to sell a particular one, but as an example I migrated the library to cappa in the improvement/switch_to_cappa_from_docopt branch just for an example of what that would look like. There are other options in a similar vein such as Clypi and tyro.

@github-project-automation github-project-automation Bot moved this from Reviewer approved to Review in progress in Skeleton Maintenance Apr 24, 2026
@jsf9k
Copy link
Copy Markdown
Member Author

jsf9k commented Apr 24, 2026

as an example I migrated the library to cappa in the improvement/switch_to_cappa_from_docopt branch just for an example of what that would look like. There are other options in a similar vein such as Clypi and tyro.

Those libraries are nicer but appear to mostly depend on the work of a single developer. One advantage of click is that it's widely used and hence less likely to become unsupported on us.

That said, I think @cisagov/vm-dev should come to a consensus and we should go with whatever is decided.

@dav3r
Copy link
Copy Markdown
Member

dav3r commented Apr 27, 2026

as an example I migrated the library to cappa in the improvement/switch_to_cappa_from_docopt branch just for an example of what that would look like. There are other options in a similar vein such as Clypi and tyro.

Those libraries are nicer but appear to mostly depend on the work of a single developer. One advantage of click is that it's widely used and hence less likely to become unsupported on us.

That said, I think @cisagov/vm-dev should come to a consensus and we should go with whatever is decided.

I agree with @jsf9k that a widely-used library like click with a lot of contributors outweighs the benefits of those other libraries pointed out by @mcdonnnj. In the future, if one of those other libraries gains momentum and more dev support, we could switch again. Until then, I'm on team click.

@felddy
Copy link
Copy Markdown
Contributor

felddy commented Apr 28, 2026

I think click is the way to go. It's got a healthy base of contributors, and seems to have good momentum. The maintainers are communicative and seem to be disciplined. I appreciate that they're planning in the open:

I would like to see some of the typing features the other libraries support, and hope they appear in the future.

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

Labels

dependencies Pull requests that update a dependency file kraken 🐙 This pull request is ready to merge during the next Lineage Kraken release python Pull requests that update Python code test This issue or pull request adds or otherwise modifies test code version bump This issue or pull request increments the version number

Projects

Status: In Progress
Status: Review in progress

Development

Successfully merging this pull request may close these issues.

6 participants