Skip to content

Releases: janluke/cloup

v4.0.0

Choose a tag to compare

@janluke janluke released this 12 Sep 03:22

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.deprecated and Group.deprecated now accept bool | str. Subclasses that narrow this attribute or the corresponding constructor argument to bool should widen their annotation. (#212)
  • HelpFormatter.write_dl() now accepts an Iterable of rows instead of a Sequence, matching Click. Callers are unaffected because every Sequence is an Iterable, but an override annotated with Sequence is now narrower than the base method. Such overrides should accept Iterable and materialize it first if they need to traverse the rows more than once. (#214)
  • HelpTheme.dark() and HelpTheme.light() are now class methods rather than static methods, and HelpTheme.with_() returns Self. Existing calls are unaffected. Subclasses overriding a preset should use @classmethod to 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.Argument is now an alias of click.Argument, so isinstance(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 _version or warnings. They remain available as cloup._version and cloup.warnings, and cloup.__version__ is unchanged.
  • The private alias cloup._params.GroupedOption has been removed. It has not been available as cloup.GroupedOption since v0.14.0 and was never visible to type checkers; use cloup.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 cloup and included in cloup.__all__, while Cloup's enhanced implementations intentionally replace the corresponding Click symbols. Click's lazily exposed deprecated names (BaseCommand, MultiCommand, OptionParser, get_binary_stream, and get_text_stream) are mirrored without emitting warnings merely from importing Cloup.

  • Command decorators work without parentheses. @cloup.command is now equivalent to @cloup.command(), matching Click, and the same applies to cloup.group, Group.command, and Group.group.

    @cloup.command
    def cli():
        ...
  • The cls argument 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), while deprecated=True continues to produce (DEPRECATED). (#212, #223)

  • cloup.Argument now delegates to Click. It is an alias of click.Argument, which natively supports argument help and deprecation in Click 8.5. The @cloup.argument decorator 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)

  • ArgumentKwargs and OptionKwargs are new public TypedDicts. They document the standard arguments accepted by @argument and @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(), and HelpTheme.with_() now preserve subclasses. with_() also forwards additional keyword arguments to dataclasses.replace(), allowing subclasses to define and replace their own fields.

Fixes

  • Positional arguments implemented with click.Argument or 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:

  • Style remains 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 any Iterable of rows, not necessarily a Sequence of rows. (#214)
  • Option groups now honor custom Option.get_help_record() implementations that return None, allowing an option subclass to omit itself from help output for reasons other than being hidden.
  • HelpFormatter.write() now matches Click's keyword-compatible write(string="", *strings) signature while preserving Cloup's multi-string convenience.
  • The annotations for Style.fg and Style.bg now 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 cls is supplied and expose the complete set of Cloup-specific group options. The formatter_settings annotation no longer advertises a mutable default.
  • Parameter decorators preserve the decorated callback's signature, and Option.group is now visible to static type checkers.
  • get_current_context() now mirrors Click's Literal overload, so silent=False produces a non-optional Context; 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.py are 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 a src/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.py and setup.cfg have been removed; project metadata and build configuration now live in pyproject.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, and pylock.package.toml.
  • _params.pyi has 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 in taskfile.yml. Run task --list to 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-click refreshes 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 typing imports and unnecessary quote...
Read more

v3.1.0

Choose a tag to compare

@janluke janluke released this 26 May 02:46

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 executable kwarg to click.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

Full Changelog: v3.0.9...v3.1.0

v3.0.9

Choose a tag to compare

@janluke janluke released this 04 Apr 03:54
d53d04f

What's Changed

Other Changes

  • fix for the click.version deprecation warning by @cav71 in #192
  • Pin setuptools<81 in docs requirements by @janluke in #197
  • Pin setuptools_scm<10 in setup_requires by @janluke in #198

New Contributors

Full Changelog: v3.0.8...v3.0.9

v3.0.8

Choose a tag to compare

@janluke janluke released this 05 Aug 02:26
fcd99fd

What's Changed

Other Changes

New Contributors

Full Changelog: v3.0.7...v3.0.8

v3.0.7

Choose a tag to compare

@janluke janluke released this 15 Mar 00:38
2bf1372

What's Changed

Other Changes

New Contributors

Full Changelog: v3.0.6...v3.0.7

v3.0.6

Choose a tag to compare

@janluke janluke released this 15 Mar 00:38

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

Full Changelog: v3.0.5...v3.0.6

v3.0.5

Choose a tag to compare

@janluke janluke released this 04 Mar 12:34
c375313

What's Changed

Other Changes

  • Allow to set dunder attributes in FrozenSpaceMeta to solve incompatibility with autodoc by @janluke in #178

Full Changelog: v3.0.4...v3.0.5

v3.0.4

Choose a tag to compare

@janluke janluke released this 29 Dec 20:24
3899fbe

What's Changed

Bug fixes

  • Fix: command alias were ignored when subcommands were passed as argument to Group() by @janluke in #173

Other Changes

New Contributors

Full Changelog: v3.0.3...v3.0.4

v3.0.3

Choose a tag to compare

@janluke janluke released this 13 Nov 02:31

What's Changed

New features and enhancements

  • Redefine click.pass_context and click.get_current_context to use cloup.Context in place of click.Context by @oeko2002 in #171

New Contributors

Full Changelog: v3.0.2...v3.0.3

v3.0.2

Choose a tag to compare

@janluke janluke released this 07 Sep 21:58
71e44c6

What's Changed

Bug fixes

  • Truncate help text to the first form feed, like click by @pocek in #169

Full Changelog: v3.0.1...v3.0.2