Releases: janluke/cloup
Release list
v4.0.0
v4.0.0
This release realigns Cloup with Click 8.5, removes the compatibility layers accumulated across the Click 8.1-8.4 line, and hands a few long-standing Cloup features back to Click now that Click implements them itself. The headline addition is that Cloup now re-exports Click's public top-level namespace, so most applications can import from cloup alone.
Cloup follows semantic versioning rigorously: any known breaking change causes a major-version bump, even when it is unlikely to affect most users. Apart from the new minimum Python and Click versions, most projects should be able to upgrade from Cloup 3.x without changing their code.
This release also includes a comprehensive overhaul of the development tooling. See the final section if you contribute to Cloup.
Dropped support
- Python >= 3.10 is now required. Python 3.9 reached end-of-life in October 2025 and has been dropped. Cloup is tested on Python 3.10-3.14, with Python 3.14 newly added. (#213)
- Click >= 8.5.0, < 9.0 is now required (previously
>= 8.1.0, < 9.0). The compatibility code for older Click versions has been removed. (#219)
Breaking changes
Apart from the new minimum Python and Click versions, no supported public calling interface has been removed or narrowed. The intentional API-level compatibility changes affect subclasses and method overrides:
Command.deprecatedandGroup.deprecatednow acceptbool | str. Subclasses that narrow this attribute or the corresponding constructor argument toboolshould widen their annotation. (#212)HelpFormatter.write_dl()now accepts anIterableof rows instead of aSequence, matching Click. Callers are unaffected because everySequenceis anIterable, but an override annotated withSequenceis now narrower than the base method. Such overrides should acceptIterableand materialize it first if they need to traverse the rows more than once. (#214)HelpTheme.dark()andHelpTheme.light()are now class methods rather than static methods, andHelpTheme.with_()returnsSelf. Existing calls are unaffected. Subclasses overriding a preset should use@classmethodto satisfy type checkers and preserve the subclass type.
Some uncommon patterns can observe additional compatibility changes even though no supported function or class calling convention is broken:
cloup.Argumentis now an alias ofclick.Argument, soisinstance(arg, cloup.Argument)matches every Click argument. Code that used this check to distinguish Cloup's former argument subclass from a plain Click argument should be updated.from cloup import *no longer binds_versionorwarnings. They remain available ascloup._versionandcloup.warnings, andcloup.__version__is unchanged.- The private alias
cloup._params.GroupedOptionhas been removed. It has not been available ascloup.GroupedOptionsince v0.14.0 and was never visible to type checkers; usecloup.Option. - Command deprecation labels in help output now use Click's uppercase suffix form,
(DEPRECATED), instead of the former(Deprecated)prefix. Tests that assert exact help output may need updating.
New Features and enhancements
-
Cloup now re-exports Click's public top-level namespace. Public Click core classes, decorators, exceptions, parameter types, terminal helpers, and utilities are available from
cloupand included incloup.__all__, while Cloup's enhanced implementations intentionally replace the corresponding Click symbols. Click's lazily exposed deprecated names (BaseCommand,MultiCommand,OptionParser,get_binary_stream, andget_text_stream) are mirrored without emitting warnings merely from importing Cloup. -
Command decorators work without parentheses.
@cloup.commandis now equivalent to@cloup.command(), matching Click, and the same applies tocloup.group,Group.command, andGroup.group.@cloup.command def cli(): ...
-
The
clsargument can be passed positionally to command and group decorators, matching Click:@cloup.command("cli", CustomCommand) def cli(): ...
-
Commands, groups, and arguments accept a string for
deprecated. A string produces a custom label such as(DEPRECATED: use new-command instead), whiledeprecated=Truecontinues to produce(DEPRECATED). (#212, #223) -
cloup.Argumentnow delegates to Click. It is an alias ofclick.Argument, which natively supports argument help and deprecation in Click 8.5. The@cloup.argumentdecorator remains available with more detailed annotations, and Cloup's positional-argument help section continues to work as before. -
Subcommand suggestions now use Click's
NoSuchCommand. Aliases participate in the suggestions, the exception exposes the possible matches, and message formatting follows Click. -
A wrapped option-group constraint is now separated from the option list that follows it by a blank line. (#208)
-
ArgumentKwargsandOptionKwargsare new publicTypedDicts. They document the standard arguments accepted by@argumentand@option, improve IDE completion, and allow type checkers to report misspelled or invalid keyword arguments. Custom parameter classes can still accept their own additional keywords. -
HelpTheme.dark(),HelpTheme.light(), andHelpTheme.with_()now preserve subclasses.with_()also forwards additional keyword arguments todataclasses.replace(), allowing subclasses to define and replace their own fields.
Fixes
- Positional arguments implemented with
click.Argumentor a custom subclass no longer lose their help text in Cloup's argument help section. (#210, #223)
The following fixes are mostly refinements to Cloup's public typing contract and alignment with Click 8.5. They are unlikely to affect most users:
Styleremains hashable after it has been called. Previously, its internal cache mutated a frozen dataclass field into an unhashable dictionary. The cache and its private field have been removed. (#224)HelpFormatter.write_dl()signature was aligned to Click one and now accepts anyIterableof rows, not necessarily aSequenceof rows. (#214)- Option groups now honor custom
Option.get_help_record()implementations that returnNone, allowing an option subclass to omit itself from help output for reasons other than being hidden. HelpFormatter.write()now matches Click's keyword-compatiblewrite(string="", *strings)signature while preserving Cloup's multi-string convenience.- The annotations for
Style.fgandStyle.bgnow accept Click's complete color specification:int | tuple[int, int, int] | str | None. This fixes false type errors for 256-color and truecolor values. (#229) - Command and group decorator overloads now infer concrete return types when a custom
clsis supplied and expose the complete set of Cloup-specific group options. Theformatter_settingsannotation no longer advertises a mutable default. - Parameter decorators preserve the decorated callback's signature, and
Option.groupis now visible to static type checkers. get_current_context()now mirrors Click'sLiteraloverload, sosilent=Falseproduces a non-optionalContext;pass_context()also preserves the wrapped callback's parameter and return types.- Section, constraint, formatter, parameter, and command annotations have been aligned more closely with Click 8.5 and with Cloup's actual runtime checks.
- Types for
_params.pyare now inline rather than stored in a shadowing_params.pyi. This ensures the implementation itself is type-checked and makes its complete public surface visible to type checkers and generated API documentation.
For contributors
Most of the changes in this release are not user-visible: the development, packaging, documentation, and CI infrastructure has been extensively modernized. CONTRIBUTING.rst has been rewritten for the new workflow and is the best starting point for an existing checkout or open pull request.
Project layout and packaging
- The package has moved from
cloup/to asrc/cloup/layout. Open pull requests that modify package sources will need to be rebased accordingly. (#220) - The build backend has migrated from setuptools to Hatchling, with hatch-vcs replacing setuptools-scm for versioning and a small metadata hook generating the PyPI README.
setup.pyandsetup.cfghave been removed; project metadata and build configuration now live inpyproject.toml. (#206) - Development dependencies are scoped to the Hatch environments that use them. Reproducibility-sensitive linting, documentation, and package-checking environments use committed PEP 751 lockfiles:
pylock.code.toml,pylock.docs.toml, andpylock.package.toml. _params.pyihas been removed in favor of inline types so static checkers and AutoAPI inspect the same implementation.
Development commands: Tox and Make to Hatch and Task
- Tox has been replaced by Hatch environments in
hatch.toml, and the Makefile has been replaced by Task configuration intaskfile.yml. Runtask --listto list the available commands. - The test matrix covers Python 3.10-3.14 with the latest supported Click, plus a pinned
click == 8.5.*environment on Python 3.14.task test-envs:upgrade-clickrefreshes Click in existing environments. - Aggregate test, typing, coverage, and QA tasks run independent environments concurrently, and each test or typing run prints the resolved Click version.
- uv is used as the installer for Hatch environments.
Linting and formatting
- Flake8 has been replaced by Ruff, and the codebase has been reformatted with Ruff.
- Imports are now sorted and formatted consistently.
- Ruff's Pyupgrade rules modernized collection, union, and optional annotations; removed deprecated
typingimports and unnecessary quote...
v3.1.0
What's Changed
- Drop support for Click < 8.1.0. Previous Cloup versions have a bug with path parameter types in Click < 8.1.0, since they pass the
executablekwarg toclick.Path, a kwarg added in Click 8.1. - Fix type error related to checking if Command is a MultiCommand (deprecated since Click 8.2) (see #202).
- Fix ParamType typing for Click 8.4 by @adamtheturtle in #201
New Contributors
- @adamtheturtle made their first contribution in #201
Full Changelog: v3.0.9...v3.1.0
v3.0.9
v3.0.8
What's Changed
Other Changes
- Add show_subcommand_aliases to overloads for group by @ntessman-capsule in #191
New Contributors
- @ntessman-capsule made their first contribution in #191
Full Changelog: v3.0.7...v3.0.8
v3.0.7
What's Changed
Other Changes
- Fix: constraints not working with options having more than 2 names by @tylerduffin and @janluke in #188
New Contributors
- @tylerduffin made their first contribution in #188
Full Changelog: v3.0.6...v3.0.7
v3.0.6
What's Changed
Other Changes
- Remove support for EOL Python version (3.7) by @ulgens in #179
- Add Python 3.12 to tests and supported versions list by @ulgens in #181
- click 8.2:
make_metavar()takes a context parameter now by @ppentchev in #186
New Contributors
- @ulgens made their first contribution in #179
- @ppentchev made their first contribution in #186
Full Changelog: v3.0.5...v3.0.6
v3.0.5
v3.0.4
v3.0.3
What's Changed
New features and enhancements
- Redefine
click.pass_contextandclick.get_current_contextto usecloup.Contextin place ofclick.Contextby @oeko2002 in #171
New Contributors
Full Changelog: v3.0.2...v3.0.3