Todo-list

Todo

Close the click-extra-side gaps the sections below identify:

  • a @persistent_option decorator (or a persistent=True kwarg on @option) registering an option on a group and injecting it into every subcommand at decoration time, covering the inherited-flags family Click has consistently declined;

  • populate ctx.params during shell completion, so a completion callback can depend on parameter values already typed on the command line;

  • native NushellComplete and PowerShellComplete classes, for users without the carapace binary the Carapace spec needs;

  • a @completion_option bundling multi-shell detection and auto-install, matching Typer’s --install-completion.

(The original entry is located in /home/runner/work/click-extra/click-extra/docs/benchmark.md, line 71.)

Todo

Mark a required flag group once carapace-sh/carapace-spec#437 ships a Command key for cobra’s MarkFlagsOneRequired. It closes two gaps at once: a required boolean pair (--foo / --no-foo) reaches the spec unmarked, since ! marks one flag at a time and either spelling satisfies Click; and Cloup’s RequireAtLeast constraint is dropped for want of anywhere to put it.

(The original entry is located in /home/runner/work/click-extra/click-extra/click_extra/carapace.py:docstring of click_extra.carapace, line 36.)

Todo

Propose addition of meta keyword upstream to Click.

(The original entry is located in /home/runner/work/click-extra/click-extra/click_extra/context.py:docstring of click_extra.context.Context, line 20.)

Todo

Re-scope this override once cloup ships the fix for janluke/cloup#225. Cloup’s own with_() collects its arguments from a fixed locals(), so it rejects every slot this subclass adds. That reason expires with the fix; what stays is returning the same instance when a style is unchanged, and the message naming an unrecognized argument. The same issue also fixes dark() and light(), shadowed below.

(The original entry is located in /home/runner/work/click-extra/click-extra/click_extra/theme.py:docstring of click_extra.theme.HelpTheme.with_, line 5.)

Todo

Delete this method and light(), along with _from_cloup(), once cloup ships the fix for janluke/cloup#225. Cloup declares both as static methods naming HelpTheme in their return expression, so the inherited ones hand back a bare cloup.HelpTheme carrying none of the slots below.

(The original entry is located in /home/runner/work/click-extra/click-extra/click_extra/theme.py:docstring of click_extra.theme.HelpTheme.dark, line 6.)

Todo

Re-scope __eq__() and __hash__() once cloup ships the fix for janluke/cloup#224. Cloup declares its lazy _style_kwargs cache without compare=False, so a cloup.Style stops comparing equal to its twin, and hash() raises TypeError, from its first call onwards. Both methods stay after that fix, for the cross-class comparison against cloup.Style the generated ones refuse: only their cache rationale expires.

(The original entry is located in /home/runner/work/click-extra/click-extra/click_extra/styling.py:docstring of click_extra.styling.Style, line 7.)

Todo

Drop the # type: ignore[assignment] on the fg and bg re-declarations below once cloup widens its own annotations. It types both Optional[str], while click.style takes int | tuple[int, int, int] | str | None, so covering a palette index and an RGB tuple here reads as an incompatible override. Asked upstream at janluke/cloup#222.

(The original entry is located in /home/runner/work/click-extra/click-extra/click_extra/styling.py:docstring of click_extra.styling.Style, line 18.)

Todo

Support case-folding, so we can have the Straße string matching the Strasse content.

This could be tricky as it messes with string length and characters index, which our logic relies on.

Danger

Danger

Roundtrip through lower-casing/upper-casing is a can of worms, because some characters change length when their case is changed:

(The original entry is located in /home/runner/work/click-extra/click-extra/click_extra/highlight.py:docstring of click_extra.highlight.highlight, line 6.)

Todo

Let the -v/-q counter reach beyond the current LogLevel range, as sketched by the -vvvv (trace) and -q (silence everything) notes that used to live on _VerbosityOption:

  • a TRACE pseudo-level below LogLevel.DEBUG (numeric value 5, mirroring logging.DEBUG - 5) so repeated -v can surface finer-grained tracing past DEBUG;

  • a SILENT pseudo-level above LogLevel.CRITICAL (any value above logging.CRITICAL) so repeated -q can suppress every record, including LogLevel.CRITICAL.

Both require extending LogLevel, which ripples into the --verbosity EnumChoice, the Formatter level-name color lookup and the level-ordering tests. They are intentionally left out of the symmetric-counter change that introduced -q, where the counter simply clamps at DEBUG/CRITICAL.

(The original entry is located in /home/runner/work/click-extra/click-extra/click_extra/logging.py:docstring of click_extra.logging, line 3.)

Todo

Drop click_extra._utils.memoize_enums() and both its call sites, here and in click_extra.version.VersionOption.__deepcopy__(), once this package’s Click floor reaches the release carrying pallets/click#3805. That pull request gives Sentinel its own __copy__, __deepcopy__ and __reduce_ex__, so a member survives a copy unaided and the memo seeding buys nothing. It is slated for Click 8.5.1, against a floor of 8.4.1 here.

(The original entry is located in /home/runner/work/click-extra/click-extra/click_extra/multicall.py:docstring of click_extra.multicall, line 73.)

Todo

Upstream pallets/click#3827 asks whether Option should keep the case of an identifier declaration, as it does today, or fold it the way Argument already does. Should Click fold both, no parameter can be named Foo_Bar any more: drop the caution above, and with it the ambiguity branch of _merge_into_template in click_extra.config.schema, which exists only to tell two such names apart.

(The original entry is located in /home/runner/work/click-extra/click-extra/click_extra/parameters.py:docstring of click_extra.parameters.canonical_param_name, line 29.)

Todo

Upstream pallets/click#3821 splits the spec half of Option.get_help_record() into its own get_help_spec() method, which returns it even for hidden options. Once a Click release ships it, drop the monkey-patching below and call that method directly.

(The original entry is located in /home/runner/work/click-extra/click-extra/click_extra/parameters.py:docstring of click_extra.parameters.get_param_spec, line 10.)

Todo

Read a full-screen program through pyte behind an optional extra, should picturing one ever be asked for. The screen below would stay the default, so the common case keeps costing no dependency.

(The original entry is located in /home/runner/work/click-extra/click-extra/click_extra/recording.py:docstring of click_extra.recording, line 20.)

Todo

Split the documentation stack into a standalone extra-docs project, should it keep growing away from the CLI framework hosting it. It would take this package, click_extra.pygments, click_extra.mkdocs, the MyST converters in click_extra.myst_converter and click_extra.rst_to_myst, and the marker-region primitives repomatic keeps in its own docs/docs_update.py. The open question is the click:source and click:run directives: they invoke a Click CLI, so either they stay here, or extra-docs depends on click-extra.

(The original entry is located in /home/runner/work/click-extra/click-extra/click_extra/sphinx/__init__.py:docstring of click_extra.sphinx, line 9.)

Todo

Remove this module entirely once click-extra drops Python 3.10. myst-parser 5.0 requires Python 3.11, and that is what holds the test dependency group at myst-parser>=4, resolved to 4.0.1 below Python 3.11 and to 5.1.0 above it; the docs group already sits at >=5.1.

Moving that floor is not enough on its own, because none of this repository’s floors reaches a consumer: nothing declares myst-parser, so a project still on myst-parser 4.x would lose its alert rendering with no error to show for it. Declare myst-parser>=5.1 in the sphinx extra in the same change, then delete this module, the setup-time version gate (click_extra.sphinx.MYST_NATIVE_ALERTS_VERSION) and the optional myst_parser import it reads, the log message pointing projects at the upstream extension, and the MYST_HAS_NATIVE_ALERTS switch the Sphinx test suite branches on.

(The original entry is located in /home/runner/work/click-extra/click-extra/click_extra/sphinx/alerts.py:docstring of click_extra.sphinx.alerts, line 16.)

Todo

Retire the MyST content_offset workaround once the pinned myst-parser floor rises past the release carrying the fix:

  • delete _myst_content_offset_inflation() and this constant, and collapse ClickDirective.abs_content_offset onto its rST branch (content_offset verbatim);

  • drop the directive.content fallback in click_extra.sphinx._base.directive_source(), which stays off block_text only because that attribute is body-only in myst-parser <= 5.1.0 (#1164 is merged but unreleased). A released block_text anchors a robust line-number computation and retires the workaround from both sides.

The single-trailing-blank-line case documented on _myst_content_offset_inflation() stays off by one until then, and no local fix can reach it: the round-trip consumes that line without a trace.

(The original entry is located in /home/runner/work/click-extra/click-extra/click_extra/sphinx/click.py:docstring of click_extra.sphinx.click.MYST_CONTENT_OFFSET_INFLATED_MAX, line 16.)

Todo

Declare TerminatedEchoingStdin instead of rewriting the __class__ of the instance Click already built. That needs Click to make EchoingStdin overridable: an echo_stdin_class attribute on click.testing.CliRunner, say, that isolation() instantiates rather than hard-coding. Worth proposing upstream.

(The original entry is located in /home/runner/work/click-extra/click-extra/click_extra/sphinx/click.py:docstring of click_extra.sphinx.click.ClickRunner.isolation, line 3.)

Todo

Propose the deduplication upstream, as a sphinx.ext.todo feature rather than a third-party hook.

The repetition is a property of how autodoc renders a docstring, not of how a project writes one, so every autodoc project documenting a module twice hits it and none of them can fix it in their own source: :no-index: reads like the cure and is not. sphinx.ext.todo.TodoListProcessor already flattens the whole domain in one place, which is where a todo_deduplicate config value would apply; the two helpers this module needed (todo_identity() and is_reexport()) are the whole of the logic.

Should it land, keep this module as a shim for the Sphinx releases below that floor, then drop it once the floor moves past them.

(The original entry is located in /home/runner/work/click-extra/click-extra/click_extra/sphinx/todos.py:docstring of click_extra.sphinx.todos, line 28.)

Todo

Open a narrowly-scoped Click issue and PR for a public click.Context accessor returning a parameter’s resolved (value, ParameterSource) after parsing, without re-firing eager callbacks. That is the forward resolution --params actually consumes, and landing it retires the re-parse this constant exists to feed.

Reference pallets/click#1279 as related, not as home: it asks for the inverse direction (reconstructing a normalized argv from a Context), was scoped with about a dozen normalization rules, flagged underdefined, and has stalled since 2023. Tracked in docs/upstream.md under “Normalized arguments”; the developer note below records why the cheaper alternatives do not work.

(The original entry is located in /home/runner/work/click-extra/click-extra/click_extra/context.py:docstring of click_extra.context.RAW_ARGS, line 9.)