Upstream¶
Click Extra was born as a collection of patches for unmaintained or slow-moving click-contrib addons. Over time, many of these fixes were contributed back upstream, and the project grew into a significant contributor to the Click ecosystem.
This page tracks click-extra’s relationship with its upstream dependencies: code contributed back, issues reported, workarounds provided, and features that upstream declined.
Code contributed upstream¶
PRs authored by the click-extra maintainer and merged into upstream projects.
click¶
Default and sentinel handling:
#3137- Provide altered context to callbacks to hideUNSETvalues asNone#3068- HideSentinel.UNSETvalues for defaults processed byContext.invoke#3030- Differentiate absence of value and value of absence fordefaultandflag_valuearguments#2956- Fix reconciliation of envvar withdefault,flag_valueandtypeparameters for flag options#3225- Fix callableflag_valuebeing instantiated when used as a default#3224- HideSentinel.UNSETvalues asNoneinlookup_default()#3239- Reconcile default value passing and default activation
Help system:
#2811- Fix eagerness of help option generated byhelp_option_names#2840- Move--helpoption defaults from its class to its decorator
Enum and Choice support:
Testing:
Documentation:
python-tabulate¶
pygments¶
furo¶
cloup¶
pallets-sphinx-themes¶
click-contrib/sphinx-click¶
click-contrib/click-log¶
boltons¶
Upstreamed from click-extra¶
Issues that click-extra solved with local workarounds first. The fix was later contributed upstream and the workaround removed from click-extra.
boltons¶
#461-ecoutils.get_profile(scrub=True)resolved the user, host and working directory before replacing all four values with-. The host lookups stall on a machine with no reverse DNS, andos.getcwd()raises once the working directory is gone. click-extra patched the two host lookups out; the fix shipped inboltons26.2.0, whose floor retired that patch.
click¶
#2811/#2563- Help option eagerness and deduplication. click-extra had customHelpOptionfixes; contributed upstream and removed locally.#2680- Callbacks not properly closed on CLI exit. click-extra patchedOptionto force-close; contributed the fix upstream.#2523/#2522-CliRunnerconflating stderr and stdout. click-extra backported the fix before the Click release.#2516/#2517- Help extra items (default, envvar, required) were computed and rendered together. click-extra needed them split to inject colorization; contributed the refactor upstream.
cloup¶
Addressed by click-extra¶
Issues that remain open or unfixed upstream. click-extra provides the solution.
Color and terminal support¶
click-extra implements NO_COLOR, FORCE_COLOR, and full help colorization with a theme system, addressing long-standing Click gaps:
Terminal captures as SVG¶
click-extra’s screenshot command renders a captured terminal to SVG with its own click_extra.screenshot.render_svg(). It was built on Rich’s Console.export_svg() until 9.0.0, which is where these came from: everything below is a defect the exported image carried, reported upstream and still unfixed.
Open upstream, and fixed here:
rich#3576- Underscores on the last captured line are clipped when exporting to SVG: the terminal’s clip path is computed as(rows + 1) × line_height - 1, one pixel shorter than the text it contains, which cuts the descenders off the last line. Reported in 2024 with the fix named in the report;rich#3611andrich#3991both propose it and neither is merged, sorich15.0.0still ships it.render_svgsizes the clip to the text block.rich#2742- SVG font rendering too narrow for Asian language: a run’stextLengthis computed from its character count while its column advance uses terminal cells, so the two disagree for every glyph that is not exactly one cell. The error runs both ways. Chinese, Japanese, Hangul, fullwidth Latin and emoji are drawn at half their width and stack on each other; a decomposedéàüis drawn at twice its width, and a zero-width-joiner family emoji at two and a half times. Open since 2023.render_svgsizes both fromclick_extra.layout.cell_width().rich#2536- Can we fix this render glitch in SVG output?: box-drawing characters do not meet cleanly, which the maintainer answers would need replacing them with drawn shapes. Open since 2022, and not addressed here either: click-extra’s tables and command tree draw the same characters.rich#2279- Misaligned box boundaries in SVG generated from a rich-click help dialogue: closed as not planned in 2022, the maintainer answering that a font whose glyphs are not all one width is the font’s problem and a browser rendering difference is not Rich’s to fix. Distinct fromrich#2536above: that one is glyphs failing to meet at their seams, this one is column drift accumulating across a whole line wherever a renderer resolves font metrics differently, which is why the same file looked right in one browser and broken in another. Starting every run on its own column with a cell-sizedtextLengthleaves drift nowhere to accumulate, whatever font a viewer substitutes.
The same defect reaches the tools built on that exporter:
rich-codex#34- emojis break alignments: an emoji-heavy capture comes out misaligned, the reporter guessing at “two different widths of emojis used during placement and then rendering”. The maintainer answered that it would be difficult or impossible to fix and suspected an upstream cause, without naming one. It isrich#2742above, reported a year later and still open.ewels/rich-codexdraws its images with Rich’sConsole.save_svg(), so it inherits the whole list; its own captures are otherwise actively maintained, and it solved the committed-image churn below on its own terms, deriving a stable id from the output path rather than leaving it to the caller.
Committing an exported image to a repository is its own problem, and Rich answers it with a parameter where render_svg answers it by construction:
rich#2537, merged in 2022, adds an opt-inunique_id=, filed because “As part of Airflow’s CI we save SVG images of our command’s help output and commit them to git. Without this change a single character change in the help text results in every single class and id in the SVG changing, which makes the diffs unreadable.” It is a parameter a caller has to remember and keep unique per image by hand; forget it and the diff is noise again, reuse it and two images collide on one page.rich#3928- Add@generatedcomment to HTML and SVG exports: the same “I commit this output” need from another angle, open since January 2026 with no reply.
Also carried by every image Rich exports, and dropped here:
The exported document references one
clipPathper line but defines them for every line but the last, so the final row of every capture carries a dangling reference. A browser ignores it, a strict SVG 1.1 renderer drops the element it is attached to.render_svgclips the text block as a whole, which removes the per-line paths and the failure mode with them.The stylesheet carries two
@font-facerules pointing at a CDN, so a committed image reaches the network to render as intended, and falls back silently offline or behind a content-security policy.render_svgnames a font stack and embeds nothing, which is also whatrich#2153andrich#2526run into from the other end.A run’s inter-column padding is written inside its
<text>element, so a column only lands correctly where the renderer both honorstextLengthand resolves the font:librsvg, and through itrsvg-convertand ImageMagick, does neither (rich#2153,rich#3034).render_svgstarts each run on its own column.Every line ends with a
<text>element containing only a newline, drawing nothing. Removing those, the per-line clip paths and the webfont rules together took this documentation’s 21 committed captures from 451 KB to 247 KB.
Source code as SVG¶
click-extra’s snippet command highlights source code with Pygments, then draws it with the same renderer the captures above use rather than with Pygments’ own SvgFormatter. That formatter has called itself “still experimental” in its own docstring since version 0.9, released in 2007, and its last functional change was pygments@3097984 in December 2019; every commit to the module since is a copyright bump, a lint fix or a pyupgrade pass. Three gaps make its output unusable on the surfaces a snippet is meant for, and none of the three is reported upstream:
It never reads
style.background_color, so it paints no background at all, and a dark style draws near-white text on whatever surface the viewer supplies. Of the formatters Pygments ships, onlyhtmlandimgread the value. The same gap is filed against the RTF formatter, and the answer there explains the silence:pygments#1208- RTF formatter does not set background color from style, open since 2019, where the maintainer replies that this is “by design”, a token’s background being the background of that token. Read it as a design position rather than an oversight, and expect the SVG behavior to stand.Its root element carries no
viewBox, nowidthand noheight, so the image states no size for a page to embed it at.It emits no
textLengthand opens every line atx="0", so nothing pins the character grid and the alignment holds only where the renderer resolves the exact font. This is the same failurerender_svgwas written around for terminal captures.
The pattern behind all three is that a capability lands on the HTML and image formatters and never travels: line highlighting was asked of img in 2009 (pygments#236) and is there today, background color reached it in 2021 (pygments#1374), and neither has reached SVG. hl_lines today works in html, img and rtf and nowhere else, which is what pygments#894 - Add line highlighting to latex formatter has asked about since 2019. No terminal formatter has ever had it and nobody has asked, so the bands snippet draws behind emphasized rows in terminal output answer no upstream request at all.
Two things click-extra routes around without fixing, so a Pygments user gains nothing from either: pygments#3236, an open attribute-injection hole in SvgFormatter’s own fontfamily and fontsize options; and pygments#2667 with pygments#1430, which ask a terminal formatter to detect whether it draws on a light or a dark terminal. snippet paints its own window, so the picture never faces that question, and its terminal output still lands on whatever surface the user’s terminal supplies.
Note
The nearest prior art is ewels/rich-codex, by rich-click’s maintainer, which highlights a snippet with Rich’s Syntax and writes it with Console.save_svg(). It reaches the same goal through the exporter whose defects the previous section lists, and inherits them.
Color on a pipe¶
--output - prints a capture’s escape sequences through click.echo, so they are dropped when the destination turns out to be a pipe and kept when --color=always says so. Pygments settled on that same contract and has not shipped it:
pygments#2491- Pygmentize ansi escape sequences are stripped from Windows pipes: open since 2023,pygmentize | lesslosing every color on Windows.pygments#2492: closed in 2024 as too breaking a change of default, but not before the maintainer settled the design: “Add an option to strip escapes (off/auto/on), make it work on all platforms, and default it to off everywhere. (Where ‘auto’ means the usualisattycheck).”pygments#2802: a contributor built that flag in November 2024. Still open, still unreviewed, no comments.
NO_COLOR has never been asked of Pygments at all, which makes click-extra’s support for it a first rather than a fix.
Screenshots in a project’s own documentation¶
click#3081- Add Screenshot workflow: open since 2025, asking for a way to picture an annotated help screen in Click’s own documentation. Four requirements settled in the thread: it runs locally for doc generation, it runs in CI, it adds only pip-installable dependencies (no ImageMagick, no external service), and the image is not blurry. Thescreenshotcommand and the:screenshot:directive option meet all four, drawing vector SVG from a live invocation at build time. The labelled boxes the report also asks for are not covered: click-extra bands whole rows and draws no callouts. Click settled for hand-annotating one screenshot inclick#3472, merged in 2026, and the general request stays open.
Themes and palettes¶
click-extra ships seven built-in themes (dark, light, dracula, monokai, nord, solarized-dark, plus a monochrome manpage) and covers every surface an end user reaches for: the --theme flag, a machine-wide CLICK_EXTRA_THEME variable next to the per-CLI <CLI>_THEME, and — alone in the Click ecosystem — new themes and overrides declared in the CLI’s own --config file ([tool.<cli>.themes.<name>]). Click itself has no theme system (formatter-level customization is still WIP upstream), and the competing rich help layers stop at Python and environment variables:
click#561- Add Custom Formatter System: open since 2016, the prerequisite for a Click-native theme API.click#3097- WIP: Help page customization low level api: open PR exploring the formatter customization Click maintainers committed to in#561.rich-click#219- Colour themes (can be set by user): closed completed in August 2025; rich-click now ships themes settable viaRICH_CLICK_THEMEenv var or Python config, but not via the wrapped CLI’s own configuration file.CLICK_EXTRA_THEMEfollows the same shape, one variable for every CLI in the ecosystem.rich-click#311- Using themes outside click: open follow-up showing the demand for richer theme reuse.
Configuration validation¶
click-extra’s config pipeline exposes an extension hook (ConfigValidator) so apps can validate data-keyed sub-tables ([tool.<cli>.managers.<id>], [tool.<cli>.plugins], …) inside the same strict-check pipeline that polices CLI-flag-bound keys. --validate-config collects every error before exiting and surfaces all failures with the same rooted ValidationError shape.
This is click-extra’s own design rather than an answer to a documented upstream issue: Click maintainers have closed every config-file proposal as out of scope (#1753, #386, #42, #971) and deferred to external packages, so validation of those external config files lives wherever each package chose to put it. The closest related upstream gap is click_config_file#11 (listed under Configuration files above): “warn when providing unsupported options in the config file?”: open since 2018 in an unmaintained package.
Configuration files¶
click-extra’s config module replaces the functionality of several unmaintained packages, with multi-format support (TOML, YAML, JSON, INI, XML), pyproject.toml integration, and a precedence chain:
Version option¶
click-extra’s VersionOption adds template variables for git metadata (branch, hash, date, tag), environment info, and Python/OS details, with pre-baking for compiled binaries:
Environment variables¶
click-extra auto-generates environment variables for all options and adds show_envvar as a global context setting:
Logging¶
click-extra’s logging module replaces the unmaintained click-log package:
Progress spinner¶
click-extra’s Spinner is a thread-animated, indeterminate progress spinner for blocking work of unknown duration. It supersedes the click-spinner package, last released in 2020 and openly looking for a maintainer since 2022. By design it resolves the issues and pull requests still open or rejected against that package:
click-spinner#27- Support for more spinner types and custom spinners: closed unmerged over maintenance concerns; click-extra accepts anyframessequence and ships a 90-entrySPINNERScatalog ported from cli-spinners (a superset of the molovo/revolver set this PR proposed), including the multi-character animations the PR had to drop because click-spinner’s backspace renderer could not erase them.click-spinner#33- PEP 561 compatible (inline types): closed unmerged and redirected to typeshed; click-extra is fully annotated and shipspy.typed.click-spinner#34- Feature request: spin clockwise /click-spinner#35: open issue, abandoned PR; click-extra exposes areverseflag.click-spinner#36- Spinner left last symbol on cmd /click-spinner#37: open; click-extra erases the line with\r\x1b[Krather than a non-destructive\bthat lingers in terminals like VS Code.click-spinner#41- Fix output in corner cases: open; click-extra always erases on exit and never rings the bell on a disabled or redirected stream.click-spinner#42- Printing to stdout breaks the spinner: open; click-extra defaults tostderrsostdoutdata stays clean, and itsecho()method prints above the animation without corrupting it.
Progress bar¶
click-extra’s progressbar is a drop-in for click.progressbar that also repairs a rendering bug in Click’s own bar:
click#3571-click.progressbardoesn’t show full completion when usingshow_pos=Truecombined withupdate_min_steps: open; a bar whose length is not a multiple ofupdate_min_stepsfreezes its position below completion (14/20instead of20/20). click-extra’s wrapper flushes the trailing sub-threshold steps on finish so the final position renders.
Option parsing¶
Multi-value options¶
click-extra’s MultiChoice type parses a single comma-separated token into a tuple of validated values: the pick-many counterpart to click.Choice, with a rendered [a,b,c] metavar that mirrors click.Choice’s [a|b|c] and per-value highlighting in the help colorizer. The canonical Click idiom for this is multiple=True + Choice, which requires the flag to be repeated (--tag a --tag b --tag c); SQL SELECT a, b, c-style syntax has been requested upstream multiple times and not shipped:
click#2771- Allownargs=-1in options with a non-whitespace separator: open, exactly the same feature request.click#2537- Allownargs=-1forclick.option: closed as not planned, the earlier space-separated variant.
The --columns option on --params is the headline consumer; the type is also exposed at the package root for arbitrary tags / categories / modes use cases in downstream CLIs.
Normalized arguments¶
Command tree¶
click-extra ships a --tree flag on every command, the standalone @tree_option decorator, and a wrap --tree mode that prints the subcommand hierarchy of any Click CLI without running it. That covers the whole checklist of the tree view rich-click has planned for its 1.10 release:
rich-click#269---treeintegration: open since August 2025, filed by rich-click’s maintainer after the author of the treeclick plugin reached out. It proposes a@click.tree_option()decorator, arich-click --tree [cmd]mode working on any Click CLI, and opt-in wiring throughcontext_settings: the same three surfaces click-extra exposes (default option, standalone decorator, CLI wrapper).rich-click#270- Subcommand tree view: first-concept PR from treeclick’s author, self-described as rough and stalled since September 2025.rich-click#275- 1.10 roadmap: tracks the--treeoption and@click.tree_option()among the planned features.
One design difference: treeclick embeds the tree inside --help output through custom TreeGroup/TreeCommand classes, and the older click-command-tree package registers a tree subcommand on the user’s group, walking the static commands mapping. click-extra renders the view behind a dedicated eager flag, like --man and --params: the plain help screen stays untouched, a flag cannot collide with the user’s own subcommand namespace, and the live-context walk includes lazily-registered commands.
Machine-readable help¶
click-extra renders any command as json, json-full, markdown, markdown-full, man or carapace through --help-format, and wrap --help-format applies the same renderings to a foreign Click CLI without running it. rich-click is designing that surface now, and arrived at nearly the same one:
rich-click#337- Machine-readable CLI help: v2: a draft PR by rich-click’s maintainer, open since August 2026, proposing--helpwith an optional format argument takingmarkdown,markdown-full,json,json-fullandcarapace. That is click-extra’s list minusman, reached independently: the PR describes finding Carapace through its own research, and adds a command-examples syntax taking a description and a command, which is the shape of click-extra’sexamples=[("description", "command")]argument.rich-click#335- rich-click CLI:--output jsonfor machine-readable help: the same idea one layer out, putting the JSON rendering on rich-click’s own wrapper CLI so any Click CLI reaches it, which is whatwrap --help-formatdoes. Draft, stacked on a PR since superseded by#337.
The two answer different questions, and only one of them is click-extra’s. wrap --help-format renders a rich-click CLI today (verified against rich-click 1.9.8), but it is a tool installed beside the target, so it serves whoever wants the output now. A flag on rich-click’s own commands is what reaches the end users of a CLI already shipped, and no external renderer can supply that.
Shell completion¶
click-extra exports any Click command tree to a Carapace completion spec, so a CLI gets native completion across every shell Carapace supports (Bash, Zsh, Fish, Nushell, PowerShell, Elvish, and more) from a single generated file. Click’s built-in completion is wired per shell and installed by hand; one spec covers them all:
click#3188- Native Carapace Support: closed as out of scope for the core framework, with the maintainers suggesting a separately-released project. click-extra is that project.
ANSI rendering in documentation¶
click-extra provides ANSI-capable session lexers, an HTML formatter, and Sphinx integration for rendering ANSI-colored CLI output in documentation, with 24-bit true-color rendering enabled by default:
pygments#1148- Can’t format console/shell-session output that includes ANSI colorspygments#477- Support ANSI (ECMA-48) color-coded text inputpygments-ansi-color#33-AnsiHtmlFormatterand ANSI-aware shell-like lexerspygments-ansi-color#35- AddAnsiHtmlFormatter(open, unmaintained upstream)nbsphinx#852- Sphinx & Pygments integration for ANSI rendering
Sphinx click:source and click:run directives¶
click-extra maintains and extends the click:source/click:run directives originally from pallets-sphinx-themes:
Sphinx python:* directives and live document rendering¶
The python:source, python:run, python:render, python:render-myst, and python:render-rst directives extend the click:* family to arbitrary Python (no Click CLI required). The render* variants parse the captured stdout as live document content: generated tables, headings, admonitions, and cross-references become first-class document nodes rather than a code block. This replaces the docs_update.py regenerator + marker-region pattern that many downstream click-extra-consuming projects use (Meta Package Manager, Mail Deduplicate, …), so the rendered HTML is always current at build time without a separate generation step. And when the generated Markdown should also live in the committed source (rendering on GitHub, reviewable in diffs), the :mirror: flag keeps a marker-delimited copy below the fence, refreshed by click-extra refresh-directives alongside the {matrix} blocks.
The click:* half of this story remains stuck upstream: the directives still live in pallets-sphinx-themes despite open requests to move them where Click users actually look for them, and MyST integration is unfinished:
sphinx-click#158- Move custom directives into Sphinx-click: open, acknowledged by maintainers.sphinx-click#127- Support myst-parser: open, click directives still RST-only upstream.pallets-sphinx-themes#61: open since 2022, also flagged in the previous section.
The python:* half (rendering executed Python output as live document content) is click-extra’s own design rather than an answer to a single upstream ticket.
Declined by upstream¶
PRs and features rejected by upstream maintainers. click-extra provides the functionality regardless.
python-tabulate¶
click-extra maintains a local patch for GitHub-Flavored Markdown table alignment.
dbcli/cli_helpers¶
Open upstream¶
PRs and issues still pending upstream.
carapace-sh/carapace-spec¶
click¶
cloup¶
#225-HelpThemesubclasses cannot usewith_(),dark()orlight()#224-Stylebecomes unhashable and stops comparing equal after its first call#222(comment) -Style.fg/bgare typedOptional[str]whileclick.styletakes a wider union#211- A deprecated command’s help drops the reason and uses Click’s pre-8.2 label#210-cloup.Argumentandclick.Argumentdiverge now that Click 8.5.0 has argumenthelp#209- Arguments page describes a Click limitation that 8.5.0 removed
pygments¶
python-tabulate¶
executablebooks/MyST-Parser¶
content_offsetis inflated by one when a directive’s option block precedes a body ending in blank line(s), shifting the reported source line of every body element. Worked around formyst-parser <= 5.1.0inclick_extra.sphinx.click._myst_content_offset_inflation; a root-cause fix is prepared upstream. (The earlierblock_textalignment#1048was superseded and merged as#1164, still unreleased.)The open rework
#1175makes a directive’scontent_offsetdocument-relative (the rST convention) but does not fix the inflation above. When it releases,click_extra.sphinx.click.ClickDirective.abs_content_offsetcollapses onto its rST branch (content_offsetverbatim) instead of addingdirective.lineno; see the sketch tracked for that migration.