Sphinx¶
Sphinx is the best way to document your Python CLI. Click Extra provides several utilities to improve the quality of life of maintainers.
Important
For these helpers to work, you need to install click_extraâs additional dependencies from the sphinx extra group:
$ pip install click_extra[sphinx]
See also
To capture a CLIâs output as a static image for a README, slide, or any surface that cannot run Sphinx, see CLI screenshots.
Setup¶
Once Click Extra is installed, you can enable its extensions in your Sphinxâs conf.py:
conf.py¶extensions = [
...
"click_extra.sphinx",
]
This unlocks the always-on features: the ANSI-capable Pygments HTML formatter, the GitHub-flavored alert (> [!NOTE], > [!WARNING], âŠ) â MyST/reST admonition converter, and todo-list deduplication. The click:* and python:* directive families are disabled by default and require an explicit opt-in described below.
Danger
Build-time code execution. Every click:* and python:* directive runs its body with the same privileges as the Sphinx process: full filesystem access, full network access, and full access to the build environmentâs secrets (GITHUB_TOKEN, READTHEDOCS_TOKEN, etc.). The runner namespace is unrestricted: there is no sandbox.
This is intentional: build-time execution is the whole point of those directives. But it means the same trust boundary Iâd apply to a Makefile or conftest.py applies here:
Only run
sphinx-buildagainst source I trust.Do not auto-build documentation from unverified pull requests in CI without an isolated, secret-free environment.
Treat any
printcall insidepython:render*whose output incorporates untrusted data as a content-injection sink. reST in particular allows.. raw:: htmland.. include:: /path/to/file, both of which can read local files or inject HTML into the rendered page.
The risk profile is identical to other build-time-execution extensions like jupyter-sphinx, myst-nb, and sphinx-exec-code.
Important
Opt-in required. Both directive families are disabled by default. A project that adds click_extra.sphinx to its extensions list gets the always-on features automatically, but does not gain build-time code execution unless the maintainer explicitly turns it on. Add this to conf.py:
click_extra_enable_exec_directives = True
Without it, click:source, click:run, python:source, python:run, python:render, python:render-myst, and python:render-rst are not registered with Sphinx. Documents that reference them get an âUnknown directiveâ warning and the directive body is never executed. This way a transitive import of click_extra.sphinx, or a maintainer who installs the extension purely for ANSI-aware code blocks, cannot be tricked into running attacker-supplied Python by a doc-only pull request.
Tip
I recommend using one of these themes, which works well with Click Extra:
Furo - Which has been fixed to support Click Extra as of
2023.05.20.Shibuya - Which is explicitly supporting Click Extra as of
2025.9.22.
See also
Using MkDocs instead of Sphinx? See the MkDocs integration.
click:* directives¶
Click Extra adds two new directives:
Directive |
Purpose |
|---|---|
|
Define and show the source code of a Click CLI in Sphinx. |
|
Invoke the CLI defined above, and display the results as if it was executed in a terminal session. |
Thanks to these, you can directly demonstrate the usage of your CLI in your documentation. You no longer have to maintain screenshots of you CLIs. Or copy and paste their outputs to keep them in sync with the latest revision. Click Extra will do that job for you.
These directives supports both MyST Markdown and reStructuredText syntax.
Usage¶
Here is how to define a simple Click-based CLI with the click:source directive:
```{click:source}
from click_extra import echo, command, option, style
@command
@option("--name", prompt="Your name", help="The person to greet.")
def hello_world(name):
"""Simple program that greets NAME."""
echo(f"Hello, {style(name, fg='red')}!")
```
.. click:source::
from click_extra import echo, command, option, style
@command
@option("--name", prompt="Your name", help="The person to greet.")
def hello_world(name):
"""Simple program that greets NAME."""
echo(f"Hello, {style(name, fg='red')}!")
After defining the CLI source code in the click:source directive above, you can invoke it with the click:run directive.
The click:run directive expects a Python code block that uses the invoke function. This function is specifically designed to run Click-based CLIs and handle their execution and output.
Here is how we invoke our example with a --help option:
```{click:run}
invoke(hello_world, args=["--help"])
```
.. click:run::
invoke(hello_world, args=["--help"])
Placed in your Sphinx documentation, the two blocks above renders to:
from click_extra import echo, command, option, style
@command
@option("--name", prompt="Your name", help="The person to greet.")
def hello_world(name):
"""Simple program that greets NAME."""
echo(f"Hello, {style(name, fg='red')}!")
$ hello-world --help
Usage: hello-world [OPTIONS]
Simple program that greets NAME.
Options:
--name TEXT The person to greet.
--time / --no-time Measure and print elapsed execution time.
[default: no-time]
--config CONFIG_PATH Location of the configuration file. Supports
local path with glob patterns or remote URL.
[default: ~/.config/hello-world/{*.toml,*.yaml,*.
yml,*.json,*.json5,*.jsonc,*.hjson,*.ini,*.xml,*.
plist,*.sqlite,*.sqlite3,*.conf,pyproject.toml}]
--no-config Ignore all configuration files and only use
command line parameters and environment
variables.
--validate-config FILE Validate the configuration file and exit.
--export-config FORMAT Export the configuration in the selected format
to <stdout>, then exit.
--accessible Accessibility mode: disable colors and render
tables in a borderless, screen-reader-friendly
format.
--color [auto|always|never] Colorize the output. A bare --color is the same
as --color=always. [default: auto]
--no-color Disable colorization (alias of --color=never).
--progress / --no-progress Show progress indicators during long operations.
Disabled for non-interactive output (pipes, dumb
terminals, CI) and by --accessible. [default:
progress]
--theme [auto|dark|dracula|light|manpage|monokai|nord|solarized_dark]
Color theme used for help screens. [default:
dark]
--params Show all CLI parameters, their provenance,
defaults and value, then exit.
--table-format [aligned|asciidoc|colon-grid|csv|csv-excel|csv-excel-tab|csv-unix|double-grid|double-outline|fancy-grid|fancy-outline|github|grid|heavy-grid|heavy-outline|hjson|html|jira|json|json5|jsonc|latex|latex-booktabs|latex-longtable|latex-raw|mediawiki|mixed-grid|mixed-outline|moinmoin|orgtbl|outline|pipe|plain|presto|pretty|psql|rounded-grid|rounded-outline|rst|simple|simple-grid|simple-outline|textile|toml|tsv|unsafehtml|vertical|xml|yaml|youtrack]
Rendering style of tables. [default: rounded-
outline]
--verbosity LEVEL Either CRITICAL, ERROR, WARNING, INFO, DEBUG.
[default: WARNING]
-v, --verbose Increase the default WARNING verbosity by one
level for each additional repetition of the
option. [default: 0]
-q, --quiet Decrease the default WARNING verbosity by one
level for each additional repetition of the
option. [default: 0]
--tree Show the tree of nested subcommands and exit.
--man Read the command's manual page and exit.
--help-format [carapace|json|json-full|man|markdown|markdown-full]
Render the command in the given format and exit.
--version Show the version and exit.
-h, --help Show this message and exit.
This is perfect for documentation, as it shows both the source code of the CLI and its results.
Notice how the CLI code is properly rendered as a Python code block with syntax highlighting. And how the invocation of that CLI renders into a terminal session with ANSI coloring of output.
You can then invoke that CLI again with its --name option:
```{click:run}
invoke(hello_world, args=["--name", "Joe"])
```
.. click:run::
invoke(hello_world, args=["--name", "Joe"])
Which renders in Sphinx as if it was executed in a terminal code block:
$ hello-world --name Joe
Hello, Joe!
Hint
click:source and click:run directives works well with standard vanilla click-based CLIs.
In the example above, we choose to import our CLI primitives from the click-extra module instead, to demonstrate the coloring of terminal session outputs, as click-extra provides fancy coloring of help screens by default.
Tip
Need to run arbitrary Python that isnât a Click CLI? See python:run and the rest of the python:* family for general-purpose build-time execution and live-content generation.
See also
Click Extraâs own documentation extensively use click:source and click:run directives. Look around in its Markdown source files for advanced examples and inspiration.
Options¶
You can pass options to both the click:source and click:run directives to customize their behavior:
Option |
Description |
Example |
|---|---|---|
Display line numbers. |
|
|
Specify the starting line number. |
|
|
Highlight specific lines in the source block. |
|
|
|
Highlight specific lines in the captured output block. Same syntax as |
|
Ignore minor errors on highlighting. |
|
|
Set a caption for the code block. |
|
|
Set a name for the code block (useful for cross-referencing). |
|
|
Set a CSS class for the code block. |
|
|
Specify the number of spaces to remove from the beginning of each line. |
|
|
Specify the programming language for syntax highlighting. This can be used as an alternative to passing the language as an argument. |
|
|
|
Flags to force the source code within the directive to be rendered or not. |
|
|
Flags to force the results of the CLI invocation to be rendered or not. Only applies to |
|
|
Flags to force the shell prompt drawn above the results to be rendered or not. Only applies to |
|
code-block options¶
Because the click:source and click:run directives produces code blocks, they inherits the same options as the Sphinx code-block directive.
For example, you can highlight some lines of with the :emphasize-lines: option, display line numbers with the :linenos: option, and set a caption with the :caption: option:
```{click:source}
:caption: A magnificent âš Hello World CLI!
:linenos:
:emphasize-lines: 4,7
from click_extra import echo, command, option, style
@command
@option("--name", prompt="Your name", help="The person to greet.")
def hello_world(name):
"""Simple program that greets NAME."""
echo(f"Hello, {style(name, fg='red')}!")
```
.. click:source::
:caption: A magnificent âš Hello World CLI!
:linenos:
:emphasize-lines: 4,7
from click_extra import echo, command, option, style
@command
@option("--name", prompt="Your name", help="The person to greet.")
def hello_world(name):
"""Simple program that greets NAME."""
echo(f"Hello, {style(name, fg='red')}!")
Which renders to:
1from click_extra import echo, command, option, style
2
3@command
4@option("--name", prompt="Your name", help="The person to greet.")
5def hello_world(name):
6 """Simple program that greets NAME."""
7 echo(f"Hello, {style(name, fg='red')}!")
Display options¶
You can also control the display of the source code and the results of the CLI invocation with the :show-source:/:hide-source: and :show-results:/:hide-results: options.
By default:
click:sourcedisplays the source code of the CLI. Because its content is not executed, no results are displayed. This is equivalent to having both:show-source:and:hide-results:options.click:rundisplays the results of the CLI invocation, but does not display the source code. This is equivalent to having both:hide-source:and:show-results:options.
But you can override this behavior by explicitly setting the options. Letâs say you only want to display the result of the CLI invocation, without showing the source code defining that CLI. Then you can add :hide-source: to the click:source directive:
```{click:source}
:hide-source:
from click_extra import echo, command, style
@command
def simple_print():
echo(f"Just a {style('string', fg='blue')} to print.")
```
```{click:run}
invoke(simple_print)
```
.. click:source::
:hide-source:
from click_extra import echo, command, style
@command
def simple_print():
echo(f"Just a {style('string', fg='blue')} to print.")
.. click:run::
invoke(simple_print)
Which only renders the click:run directive, as the click:source doesnât display anything:
$ simple-print
Just a string to print.
If you want to display the source code used to invoke the CLI in addition to its results, you can add the :show-source: option to the click:run directive:
```{click:run}
:show-source:
result = invoke(simple_print)
# Some inline tests.
assert result.exit_code == 0, "CLI execution failed"
assert not result.stderr, "Found error messages in <stderr>"
```
.. click:run::
:show-source:
result = invoke(simple_print)
# Some inline tests.
assert result.exit_code == 0, "CLI execution failed"
assert not result.stderr, "Found error messages in <stderr>"
In this particular mode the click:run produced two code blocks, one for the source code, and one for the results of the invocation:
result = invoke(simple_print)
# Some inline tests.
assert result.exit_code == 0, "CLI execution failed"
assert not result.stderr, "Found error messages in <stderr>"
$ simple-print
Just a string to print.
Caution
:show-results:/:hide-results: options have no effect on the click:source directive and will be ignored. Thatâs because this directive does not execute the CLI: it only displays its source code.
Hiding the prompt¶
A click:run block opens its results with the invocation that produced them, drawn as a shell prompt:
$ simple-print
Just a string to print.
:hide-prompt: drops that line and leaves the output on its own. Reach for it when the surrounding prose already names the command, or when the block exists to show what a CLI prints rather than how to call it:
Just a string to print.
Environment variables handed to invoke() are rendered as assignments on the invocation itself, which is how the runner applies them: they are scoped to that one call and are not inherited by the blocks below.
$ WEATHER_UNITS=fahrenheit forecast
Paris: 18 fahrenheit.
The prompt is one line prepended to the captured output, so it is part of the results rather than a block of its own. It is rendered by format_cli_prompt(), the same helper that draws it onto an SVG capture: a block combining :hide-prompt: with :screenshot: therefore writes an image without one either.
Committed captures¶
Inside these pages a click:run block renders live, so none of them needs a screenshot. A README on GitHub or PyPI, a slide, or a social post cannot run code, and those surfaces need a captured image. Two options let a block maintain one without giving up its live rendering:
:screenshot: <name>writes the blockâs output to<name>.svg, in the directory theclick_extra_screenshot_dirconf.pyvalue names (assetsby default, relative to the documentation source root). Nothing about the page changes: the results code block stays, being selectable, searchable and theme-aware where an image is none of those. The file is rewritten on every build, so it cannot drift from the CLI.:mirror:puts the image on the page as well, by keeping a Markdown link to it in the source.md, between<!-- screenshot -->and<!-- screenshot-end -->markers directly below the fence. That region is refreshed byclick-extra refresh-directives. Holding a link rather than generated data, it goes stale only when the capture is renamed.:screenshot-columns:lays the capture out at a width of its own, or atautofor the one its longest line asks for. Click wraps the CLIâs own text at its fixed width either way: what this decides is the picture, so a line the CLI never wrapped (a prompt, a wide table, a machine-readable dump) stops folding mid-word. See width for the CLI-side flag.:screenshot-border:,:screenshot-border-width:,:screenshot-radius:,:screenshot-shadow:,:screenshot-backdrop:,:screenshot-margin:,:screenshot-opacity:,:screenshot-padding:and:screenshot-title:restate the window the capture is drawn in, each mirroring the command-line option of the same name. Left out, each takes what the chrome asks for. See the window itself.:screenshot-line-numbers:is a flag, numbering the blockâs captured lines in a gutter. Quote a hex color (:screenshot-backdrop: "#1f6feb"): a directiveâs options are read as YAML, where an unquoted#opens a comment and leaves the option empty.:screenshot-preset:draws the capture as a named terminal, from its window decorations down to the sigil its shell prompts with. A block naming none falls back to theclick_extra_screenshot_presetconf.pyvalue, so a project drawing every capture as the same desktop states it once. See terminal presets.:screenshot-watermark:credits the image in its bottom-right corner, with:screenshot-watermark-color:for the ink. Unlike thescreenshotcommand, which credits click-extra on every image it writes, a block draws none unless asked: its image is rewritten and committed on every build, so a mark naming a release would rewrite every asset the day that release changes.click_extra_screenshot_watermarkinconf.pyturns it on for the whole project. See the credit line.:screenshot-background: lightdraws the capture on white chrome, with the ANSI palette to match, for a block rendering a light-background theme. Defaults todark, what a terminal and this packageâs default theme both look like. Only the image answers to it: the pageâs own results block is styled by the siteâs stylesheet and follows the readerâs theme either way. See light and dark chrome for the CLI-side flag.
```{click:run}
:screenshot: greet-screen
:mirror:
result = invoke(greet)
```
Which, once refreshed, keeps this below the fence, so the capture shows wherever the raw Markdown is read:
<!-- screenshot -->

<!-- screenshot-end -->
The name doubles as the imageâs alt text, so pick one that reads as a description.
Both renderings, side by side¶
The two come from one execution, so a tab set can hold them together. Take a CLI leaning on color:
from click_extra import command, echo, style
@command
def forecast():
"""Report tomorrow's weather."""
echo(f"Lisbon {style('22°C', fg='yellow')} {style('sunny', fg='bright_yellow')}")
echo(f"Bergen {style('11°C', fg='cyan')} {style('rain', fg='blue')}")
echo(f"Nairobi {style('26°C', fg='red')} {style('clear', fg='bright_yellow')}")
$ forecast
Lisbon 22°C sunny
Bergen 11°C rain
Nairobi 26°C clear
The first tab is what the page renders on its own: real text, selectable and searchable, its colors resolved by the siteâs stylesheet, so it follows a reader switching to the light theme. The second is the file that same run wrote, framed in terminal chrome and identical wherever it is embedded, theme included. That immutability is the point on a surface that cannot run code, and the drawback on one that can.
Note
:mirror: puts its region directly below the fence, which inside a tab set means inside that tab. A layout like this one references the image by hand instead: the link only ever changes when the capture is renamed, and Sphinx warns if the two fall out of step.
Tip
The two are independent on purpose. :screenshot: alone maintains an image some other surface embeds, which is how the before/after screens opening this projectâs readme are produced: they come from the tutorialâs own blocks, and no one has to remember to reshoot them.
Caution
Captures are written into the documentation source tree, not the build output, since that is where a README finds them. A build therefore leaves them refreshed in your working copy: commit what changed. See screenshots for the surfaces this serves, and for capturing a CLI outside a documentation build.
Warning
Pick what you capture with the committed file in mind, because whatever the command prints is what gets checked into the repository. Verbose output is the trap: a single --verbosity DEBUG run echoes the configuration search, which means absolute paths, the hostâs pyproject.toml, git hashes and kernel details, all frozen into an image and pushed. A live block gets away with it, being regenerated per build and never committed.
Two rules of thumb: prefer a command whose output depends only on the CLI, and read the image once before committing it. This project pins the application directory in conf.py for the same reason: click.get_app_dir() otherwise answers per platform, so a --config default would render one way on macOS and another on Linux, and every capture of a help screen would flip with its authorâs laptop.
Standalone click:run blocks¶
You can also use the click:run directive without a preceding click:source block. This is useful when you want to demonstrate the usage of a CLI defined elsewhere, for example in your packageâs source code.
In the example below, we import the click_extra.cli.demo function, which is defined in the click_extra/cli.py source file. There is no need to redefine the CLI in a click:source block beforehand:
```{click:run}
from click_extra.cli import demo
invoke(demo, args=["--help"])
```
.. click:run::
from click_extra.cli import demo
invoke(demo, args=["--help"])
And the execution of that CLI renders just fine:
$ click-extra --help
Usage: click-extra [OPTIONS] COMMAND [ARGS]...
Click Extra CLI.
Options:
--time / --no-time Measure and print elapsed execution time.
[default: no-time]
--config CONFIG_PATH Location of the configuration file. Supports
local path with glob patterns or remote URL.
[default: ~/.config/click-extra/{*.toml,*.yaml,*.
yml,*.json,*.json5,*.jsonc,*.hjson,*.ini,*.xml,*.
plist,*.sqlite,*.sqlite3,*.conf,pyproject.toml}]
--no-config Ignore all configuration files and only use
command line parameters and environment
variables.
--validate-config FILE Validate the configuration file and exit.
--export-config FORMAT Export the configuration in the selected format
to <stdout>, then exit.
--accessible Accessibility mode: disable colors and render
tables in a borderless, screen-reader-friendly
format.
--color [auto|always|never] Colorize the output. A bare --color is the same
as --color=always. [default: auto]
--no-color Disable colorization (alias of --color=never).
--progress / --no-progress Show progress indicators during long operations.
Disabled for non-interactive output (pipes, dumb
terminals, CI) and by --accessible. [default:
progress]
--theme [auto|dark|dracula|light|manpage|monokai|nord|solarized_dark]
Color theme used for help screens. [default:
dark]
--params Show all CLI parameters, their provenance,
defaults and value, then exit.
--table-format [aligned|asciidoc|colon-grid|csv|csv-excel|csv-excel-tab|csv-unix|double-grid|double-outline|fancy-grid|fancy-outline|github|grid|heavy-grid|heavy-outline|hjson|html|jira|json|json5|jsonc|latex|latex-booktabs|latex-longtable|latex-raw|mediawiki|mixed-grid|mixed-outline|moinmoin|orgtbl|outline|pipe|plain|presto|pretty|psql|rounded-grid|rounded-outline|rst|simple|simple-grid|simple-outline|textile|toml|tsv|unsafehtml|vertical|xml|yaml|youtrack]
Rendering style of tables. [default: rounded-
outline]
--verbosity LEVEL Either CRITICAL, ERROR, WARNING, INFO, DEBUG.
[default: WARNING]
-v, --verbose Increase the default WARNING verbosity by one
level for each additional repetition of the
option. [default: 0]
-q, --quiet Decrease the default WARNING verbosity by one
level for each additional repetition of the
option. [default: 0]
--tree Show the tree of nested subcommands and exit.
--man Read the command's manual page and exit.
--help-format [carapace|json|json-full|man|markdown|markdown-full]
Render the command in the given format and exit.
--version Show the version and exit.
-h, --help Show this message and exit.
Demo:
8color Render all standard 8-color foreground/background...
colors Render every foreground color against every background...
gradient Render 24-bit RGB gradients vs.
palette Render a compact 256-color indexed swatch.
spinner Animate the spinner widget; --table lists the catalog...
styles Render every color with each text style (bold, dim,...
themes Render a sample help screen under every built-in theme,...
trail Trace a simulated batch of operations behind an...
Other commands:
convert-to-myst Convert reST docstrings to MyST markdown in Python...
help Show help for a command.
prebake Pre-bake build-time metadata into Python source files.
refresh-directives Refresh the self-updating blocks embedded in Markdown...
screenshot Capture a command's colored output and write it as an...
test-suite Run declarative CLI test cases against a command or...
wrap (run) Run, or introspect, any Click CLI through Click Extra.
Caution
Avoid --version in a live click:run block. VersionOption resolves the owning package by walking the call stack, then memoizes the result for the rest of the build. On this projectâs own docs, click_extra_manpages (set in conf.py) generates demoâs man pages before any page is read, and that walk resolves --version for the first time from a call path with no CliRunner.invoke frame in it, landing on a Sphinx frame instead of the CLIâs own module. The wrong value then sticks for every later --version example in the same build: verified, this block used to render Click Extra, version 9.1.0 â this buildâs Sphinx version, not click-extraâs (see the caution on the version page for the same quirk affecting {package_name}). --help and ordinary subcommands are unaffected, since they do no package detection.
Capture mode¶
click:run and click:tree execute the documented CLI through Clickâs test runner. On Click 8.4 and later, the output is captured at the file-descriptor level (Clickâs capture="fd" mode), so a CLI that writes through its stdout descriptor, such as one re-opening sys.stdout.fileno() to force UTF-8 output, renders normally instead of aborting the build with io.UnsupportedOperation.
Select the capture mode with the click_extra_run_capture value in conf.py:
conf.py¶click_extra_run_capture = "fd" # "fd" (default) or "sys"
Set it to "sys" to use Clickâs legacy in-memory capture, which exposes no file descriptor. On Click releases older than 8.4 the value is ignored, as the capture parameter does not exist.
Inline tests¶
The click:run directive can also be used to embed tests in your documentation.
You can write tests in your documentation, and they will be executed at build time. This allows you to catch regressions early, and ensure that your documentation is always up-to-date with the latest version of your CLI, in the spirit of doctest and Docs as Tests.
For example, here is a simple CLI:
```{click:source}
from click import echo, command
@command
def yo_cli():
echo("Yo!")
```
.. click:source::
from click import echo, command
@command
def yo_cli():
echo("Yo!")
Letâs put the code above in a click:source directive. And then put the following Python code into a click:run block:
```{click:run}
result = invoke(yo_cli, args=["--help"])
assert result.exit_code == 0, "CLI execution failed"
assert not result.stderr, "Found error messages in <stderr>"
assert "Usage: yo-cli [OPTIONS]" in result.stdout, "Usage line not found in help screen"
```
.. click:run::
result = invoke(yo_cli, args=["--help"])
assert result.exit_code == 0, "CLI execution failed"
assert not result.stderr, "Found error messages in <stderr>"
assert "Usage: yo-cli [OPTIONS]" in result.stdout, "Usage line not found in help screen"
See how we collect here the result of the invoke command, and separately inspect the exit_code, stderr and stdout of with assert statements.
If for any reason our CLI changes and its help screen is no longer what we expect, the test will fail and the documentation build will break with a message similar to:
Versions
========
* Platform: darwin; (macOS-15.5-arm64-64bit)
* Python version: 3.11.11 (CPython)
* Sphinx version: 8.2.3
* Docutils version: 0.21.2
* Jinja2 version: 3.1.6
* Pygments version: 2.19.2
Loaded Extensions
=================
(...)
* myst_parser (4.0.1)
* click_extra.sphinx (5.1.0)
Traceback
=========
File "(...)/click-extra/docs/sphinx.md:197", line 5, in <module>
AssertionError: Usage line not found in help screen
The full traceback has been saved in:
/var/folders/gr/1frk79j52flczzs2rrpfnkl80000gn/T/sphinx-err-5l6axu9g.log
Having your build fails when something unexpected happens is a great signal to catch regressions early.
On the other hand, if the build succeed, the click:run block will render as usual with the result of the invocation:
$ yo-cli --help
Usage: yo-cli [OPTIONS]
Options:
--help Show this message and exit.
Syntax highlight language¶
By default, code blocks produced by the directives are automatically highlighted with these languages:
click:source:pythonclick:run:ansi-shell-session
If for any reason you want to override these defaults, you can pass the language as an optional parameter to the directive.
Letâs say you have a CLI that is only printing SQL queries in its output:
from click_extra import echo, command, option
@command
@option("--name")
def sql_output(name):
sql_query = f"SELECT * FROM users WHERE name = '{name}';"
echo(sql_query)
Then you can force the SQL Pygments highlighter on its output by passing the short name of that lexer (sql) as the first argument to the directive:
```{click:run} sql
invoke(sql_output, args=["--name", "Joe"])
```
And renders to:
$ sql-output --name Joe
SELECT * FROM users WHERE name = 'Joe';
See how the output (the second line above) is now rendered with the sql Pygments lexer, which is more appropriate for SQL queries. But of course it also parse and renders the whole block as if it is SQL code, which mess up the rendering of the first line, as it is a shell command.
In fact, if you look at Sphinx logs, you will see that a warning has been raised because of that:
.../docs/sphinx.md:257: WARNING: Lexing literal_block "$ sql-output --name Joe\nSELECT * FROM users WHERE name = 'Joe';" as "sql" resulted in an error at token: '$'. Retrying in relaxed mode. [misc.highlighting_failure]
Hint
Alternatively, you can force syntax highlight with the :language: option, which takes precedence over the default language of the directive.
CLI reference tree¶
The click:tree directive walks a Click command group at build time and expands into a full CLI reference page: a summary table on top, then one --help capture per command, nested by depth. It is meant to replace per-project hand-rolled scripts that generate the same scaffolding (a summary table, anchors, one click:run per command) by hand.
The required argument is a Python expression evaluated in the per-document runner namespace; it must resolve to a click.Command. The optional body is Python preamble that runs in the same namespace before the expression is evaluated, so you can either rely on a prior click:source import or inline the import in the directiveâs body.
Here is a small recipe CLI to demonstrate:
from click_extra import echo, command, group, option
@group()
def kitchen():
"""Manage kitchen tools and recipes."""
@kitchen.command()
@option("--minutes", type=int, default=5)
def boil(minutes):
"""Boil water for tea."""
echo(f"Boiling for {minutes} minutes.")
@kitchen.group()
def pantry():
"""Inspect pantry contents."""
@pantry.command()
def jars():
"""List jars on the shelf."""
echo("Olives, honey, pickles.")
@pantry.command()
@option("--fruit", default="apple")
def count(fruit):
"""Count fruits in the basket."""
echo(f"Three {fruit}s.")
A single click:tree invocation expands into a summary table plus one --help capture for kitchen, kitchen boil, kitchen pantry, kitchen pantry count, and kitchen pantry jars:
```{click:tree} kitchen
:root-label: kitchen --help
```
Which renders as:
kitchen âhelp¶
$ kitchen --help
Usage: kitchen [OPTIONS] COMMAND [ARGS]...
Manage kitchen tools and recipes.
Options:
--time / --no-time Measure and print elapsed execution time.
[default: no-time]
--config CONFIG_PATH Location of the configuration file. Supports
local path with glob patterns or remote URL.
[default: ~/.config/kitchen/{*.toml,*.yaml,*.yml,
*.json,*.json5,*.jsonc,*.hjson,*.ini,*.xml,*.plis
t,*.sqlite,*.sqlite3,*.conf,pyproject.toml}]
--no-config Ignore all configuration files and only use
command line parameters and environment
variables.
--validate-config FILE Validate the configuration file and exit.
--export-config FORMAT Export the configuration in the selected format
to <stdout>, then exit.
--accessible Accessibility mode: disable colors and render
tables in a borderless, screen-reader-friendly
format.
--color [auto|always|never] Colorize the output. A bare --color is the same
as --color=always. [default: auto]
--no-color Disable colorization (alias of --color=never).
--progress / --no-progress Show progress indicators during long operations.
Disabled for non-interactive output (pipes, dumb
terminals, CI) and by --accessible. [default:
progress]
--theme [auto|dark|dracula|light|manpage|monokai|nord|solarized_dark]
Color theme used for help screens. [default:
dark]
--params Show all CLI parameters, their provenance,
defaults and value, then exit.
--table-format [aligned|asciidoc|colon-grid|csv|csv-excel|csv-excel-tab|csv-unix|double-grid|double-outline|fancy-grid|fancy-outline|github|grid|heavy-grid|heavy-outline|hjson|html|jira|json|json5|jsonc|latex|latex-booktabs|latex-longtable|latex-raw|mediawiki|mixed-grid|mixed-outline|moinmoin|orgtbl|outline|pipe|plain|presto|pretty|psql|rounded-grid|rounded-outline|rst|simple|simple-grid|simple-outline|textile|toml|tsv|unsafehtml|vertical|xml|yaml|youtrack]
Rendering style of tables. [default: rounded-
outline]
--verbosity LEVEL Either CRITICAL, ERROR, WARNING, INFO, DEBUG.
[default: WARNING]
-v, --verbose Increase the default WARNING verbosity by one
level for each additional repetition of the
option. [default: 0]
-q, --quiet Decrease the default WARNING verbosity by one
level for each additional repetition of the
option. [default: 0]
--tree Show the tree of nested subcommands and exit.
--man Read the command's manual page and exit.
--help-format [carapace|json|json-full|man|markdown|markdown-full]
Render the command in the given format and exit.
--version Show the version and exit.
-h, --help Show this message and exit.
Commands:
boil Boil water for tea.
help Show help for a command.
pantry Inspect pantry contents.
kitchen boil¶
$ kitchen boil --help
Usage: kitchen boil [OPTIONS]
Boil water for tea.
Options:
--minutes INTEGER [default: 5]
-h, --help Show this message and exit.
kitchen help¶
$ kitchen help --help
Usage: kitchen help [OPTIONS] [COMMAND_PATH]...
Show help for a command.
Options:
--search TEXT Search all subcommands for matching options or descriptions.
-h, --help Show this message and exit.
kitchen pantry¶
$ kitchen pantry --help
Usage: kitchen pantry [OPTIONS] COMMAND [ARGS]...
Inspect pantry contents.
Options:
-h, --help Show this message and exit.
Commands:
count Count fruits in the basket.
help Show help for a command.
jars List jars on the shelf.
kitchen pantry count¶
$ kitchen pantry count --help
Usage: kitchen pantry count [OPTIONS]
Count fruits in the basket.
Options:
--fruit TEXT [default: apple]
-h, --help Show this message and exit.
kitchen pantry help¶
$ kitchen pantry help --help
Usage: kitchen pantry help [OPTIONS] [COMMAND_PATH]...
Show help for a command.
Options:
--search TEXT Search all subcommands for matching options or descriptions.
-h, --help Show this message and exit.
kitchen pantry jars¶
$ kitchen pantry jars --help
Usage: kitchen pantry jars [OPTIONS]
List jars on the shelf.
Options:
-h, --help Show this message and exit.
Command |
Description |
|---|---|
Manage kitchen tools and recipes |
|
Boil water for tea |
|
Show help for a command |
|
Inspect pantry contents |
|
Count fruits in the basket |
|
Show help for a command |
|
List jars on the shelf |
Tree options¶
Option |
Description |
Default |
|---|---|---|
|
Maximum recursion depth into nested groups. |
|
|
Shift all generated headings down by N levels. Override when the auto-detected depth is wrong for the page layout. |
Surrounding section depth (root nested one level below the enclosing section). |
|
Slug prefix for every generated anchor. |
Slug of the CLI name. |
|
Display prefix for the command labels in the table and headings. |
The CLI name. |
|
Heading text for the root |
|
|
Skip the summary table. |
Table is rendered. |
|
Skip the root |
Root block is rendered. |
Inline import in the directive body¶
If the CLI lives in your package, you can skip the seed click:source block and import directly in the body:
```{click:tree} demo
from click_extra.cli import demo
```
The rendered output is identical to the kitchen example above: a summary table and one --help block per (sub)command. The only difference is where the CLI comes from: a package import instead of a preceding click:source block.
Note
click:tree is currently MyST-only because the expanded scaffolding uses MySTâs (label)= anchor syntax and pipe tables. An rST equivalent would emit .. _label: targets and list-table:: directives instead; it has not been implemented yet.
Configuration reference¶
The click:config directive documents a CLIâs config_schema at build time: a summary table linking each option to its section, then one heading per option with its docstring, type, default, and a TOML example pinned to the default value. Like click:tree, it replaces per-project hand-rolled generators that produce the same reference from the schema dataclass by hand.
The required argument is a Python expression evaluated in the per-document runner namespace. It accepts either a click.Command whose config_schema is wired (the schema is pulled off its ConfigOption), or a schema dataclass directly. The optional body is Python preamble, same as click:tree.
Click Extraâs own CLI declares a config_schema, so a single invocation documents its [tool.click-extra] section:
```{click:config} demo
from click_extra.cli import demo
```
Which renders as:
prebake.module¶
Path to the __init__.py to pre-bake, resolved relative to the project root. Overrides the [project.scripts] auto-discovery; leave unset to keep it.
Type: str | Default: (none)
test-suite.cases¶
Test cases written natively in the config format, an alternative to a file suite, taking precedence over it when both are set.
Type: list[dict] | Default: []
Each entry is a mapping of CLITestCase directive names, equivalent to one
item of a suite list. In TOML this reads as a [[tool.<cli>.test-suite.cases]]
array of tables. Declared as an extension point so the configuration engine
passes the raw mappings through unprocessed; the test-suite command turns
them into CLITestCase instances.
Example:
[tool.click-extra]
test-suite.cases = []
test-suite.file¶
Path to a test suite file, resolved relative to the project root.
Type: str | Default: "./tests/cli-test-suite.toml"
Its format is detected from the extension; the default is TOML, which (like JSON) parses with no optional dependency, unlike YAML and the others.
Example:
[tool.click-extra]
test-suite.file = "./tests/cli-test-suite.toml"
test-suite.timeout¶
Default timeout (seconds) for each case that does not set its own.
Type: int | Default: (none)
None leaves cases unbounded unless --timeout is passed.
Option |
Description |
Default |
|---|---|---|
Path to the |
(none) |
|
Test cases written natively in the config format, an alternative to a |
|
|
Path to a test suite file, resolved relative to the project root. |
|
|
Default timeout (seconds) for each case that does not set its own. |
(none) |
Config options¶
Option |
Description |
Default |
|---|---|---|
|
Shift all generated headings down by N levels. Override when the auto-detected depth is wrong for the page layout. |
Surrounding section depth (options nested one level below the enclosing section). |
|
TOML table header shown in the per-option examples. An explicitly empty value suppresses the header. |
|
|
Skip the summary table. |
Table is rendered. |
|
Skip the TOML example blocks. |
Examples are rendered. |
Option metadata comes from the schema_field_infos() introspection helper, which is also part of the public API for CLIs that render their own configuration reference (a show-config table, say): dotted kebab-case keys, type annotations, defaults from a pristine schema instance, and attribute docstrings. Docstrings are parsed as the host documentâs markup, and their first paragraph doubles as the optionâs summary in the table.
Caution
Attribute docstrings are recovered from the schemaâs source file. A schema defined inline in a click:source block was born in an exec call, has no source file, and therefore documents its options without descriptions. Import the schema from a real module instead, as in the example above.
Note
click:config is currently MyST-only: place it in a .md document with myst_parser enabled. Like click:tree, an rST equivalent has not been implemented yet.
python:* directives¶
Click Extra also adds five general-purpose Python execution directives, registered under a separate python domain (distinct from Sphinxâs built-in py domain for documenting API objects):
Directive |
Purpose |
|---|---|
|
Define and show a Python source block, executed silently. Use it to teach readers what a snippet looks like and to seed imports/variables for follow-up blocks. |
|
Execute a Python block and render its captured |
|
Execute a Python block and parse its captured |
|
Execute a Python block and parse its captured |
|
Execute a Python block and parse its captured |
These complement the Click directives: click:run is for showing simulated CLI sessions; python:run is for showing arbitrary Python output; the python:render* family is for inline content generation, replacing the regenerator-script + marker-region pattern many projects use to keep auto-tables in sync.
Hint
This project eats its own dog food: the ANSI lexer table in pygments.md is rendered live at build time by an inline python:render block that imports LEXER_MAP and prints a Markdown table. Read the exact source lines on GitHub for a real-world example of replacing a regenerator script with a one-block inline build-time computation.
Pick the right render¶
Directive |
Parser used for captured stdout |
When to use |
|---|---|---|
|
Whatever parser owns the host source file |
Generated markup matches the host file format. |
|
MyST, regardless of host |
Embed MyST-generated content in a |
|
reST, regardless of host |
Embed reST-generated content in a |
python:render reuses the host state machine, so cross-references and Sphinx-aware roles resolve naturally. The forced-parser variants (render-myst, render-rst) parse into a fresh sub-document and graft the resulting nodes back into the page.
python:render: docs as code¶
Tip
The strongest use case is replacing a docs/docs_update.py script that walks an in-process registry, renders Markdown, and rewrites a region of a .md file between <!-- start --> / <!-- end --> markers. With python:render, the same code lives inline in the page itself and runs at build time. The rendered HTML is always current because the source-of-truth registry is queried on every build.
Render the live list of Pythonâs built-in modules as a Markdown table, executed by Sphinx at build time:
```{python:render}
import sys
print("| Module | Type |")
print("|--------|------|")
for name in sorted(sys.builtin_module_names)[:5]:
print(f"| `{name}` | built-in |")
```
.. python:render::
import sys
print("| Module | Type |")
print("|--------|------|")
for name in sorted(sys.builtin_module_names)[:5]:
print(f"| `{name}` | built-in |")
Renders as a real HTML <table> (output truncated to 5 entries):
Module |
Type |
|---|---|
|
built-in |
|
built-in |
|
built-in |
|
built-in |
|
built-in |
Self-updating source with :mirror:¶
python:render accepts a :mirror: flag. On top of rendering live, a mirror block keeps a copy of its generated Markdown in the source .md, between two HTML-comment markers directly below the fence. The output stays reviewable in the raw file, in diffs, and on GitHub, which renders the mirrored Markdown even though it never executes the block. This revives the docs_update.py marker-region pattern with the generator inlined into the page: no separate regenerator script, and no drift.
Add :mirror: to a python:render fence:
```{python:render}
:mirror:
from click_extra.table import TableFormat, render_table
print(render_table(
[["Lisbon", "12:00"], ["Denver", "05:00"]],
headers=["City", "Local time"],
table_format=TableFormat.GITHUB,
))
```
Running click-extra refresh-directives on the file inserts the mirrored region below the fence, and refreshes it in place on every later run:
```{python:render}
:mirror:
...
```
<!-- mirror -->
| City | Local time |
| :----- | :--------- |
| Lisbon | 12:00 |
| Denver | 05:00 |
<!-- mirror-end -->
A few properties follow from the mirror being real Markdown:
The mirrored region is the single rendered copy, so the directive emits nothing of its own in mirror mode: otherwise the table would render twice. Add
:show-source:to also show the Python block above the region.Sphinx builds regenerate the region in memory before parsing the page, so the rendered HTML is always fresh, even when the committed region is stale. The build never writes to the source file: the committed copy is refreshed by
click-extra refresh-directives, typically from the same automation that keeps{matrix}blocks current.The
<!-- mirror -->âŠ<!-- mirror-end -->pair follows the same marker grammar as the<!-- matrix ⊠-->regions, and a mirror example nested inside a longer code fence (like the ones on this page) is never executed or refreshed.The region is reformatted by
mdformatlike any other Markdown, so a mirror block must printmdformat-canonical Markdown.render_tableinGITHUBmode already does; a hand-built table may be re-aligned by the formatter and then fight the generator.
:mirror: is scoped to python:render in a Markdown host, and shares the click_extra_enable_exec_directives opt-in with the rest of the executing directives.
Hiding the generator with <!-- mirror-src -->¶
A :mirror: fence renders live and mirrors its output into the source, but the generator fence itself stays visible: on GitHub or PyPI, which show the raw Markdown without running Sphinx, the reader sees the python:render code block above the table. When the output is the whole point (a table or diagram in readme.md) and the generator is just plumbing, the <!-- mirror-src --> comment form moves the generator into an HTML comment, so only its output renders.
The generator Python lives between an opening <!-- mirror-src line and a closing -->, each on its own line:
<!-- mirror-src
from click_extra.table import TableFormat, render_table
print(render_table(
[["Lisbon", "12:00"], ["Denver", "05:00"]],
headers=["City", "Local time"],
table_format=TableFormat.GITHUB,
))
-->
Running click-extra refresh-directives executes the generator and writes its output just below the comment, closed by a <!-- mirror-src-end --> marker:
<!-- mirror-src
...
-->
| City | Local time |
| :----- | :--------- |
| Lisbon | 12:00 |
| Denver | 05:00 |
<!-- mirror-src-end -->
Both markers are HTML comments, so GitHub, PyPI, and any plain Markdown renderer show only the generated table while the generator stays out of sight. Everything else matches the :mirror: fence: Sphinx regenerates the region in memory on each build so the rendered HTML is never stale, the committed copy is refreshed offline by click-extra refresh-directives, the region is reformatted by mdformat (so the generator must print mdformat-canonical Markdown), and an example nested inside a longer code fence (like the two above) is copied verbatim, never executed.
Choose between the two forms by what should be on the page: the :mirror: fence when the generator belongs there, like a docs example teaching python:render itself; the <!-- mirror-src --> comment when the page should read as its output alone, like a readme.md rendered on PyPI.
Cross-format rendering¶
python:render-myst and python:render-rst let a host file embed content authored in the other markup. This page is MyST, but the following block prints reST and parses it as such:
Note
A persimmon must be very ripe to eat raw.
In an rST host, python:render-myst provides the symmetric path: print MyST and have it parsed as MyST regardless of the surrounding .rst file.
Namespace persistence¶
Like click:source / click:run, the Python runner holds a per-document namespace, so consecutive blocks share imports and variables:
from textwrap import dedent
GREETING = "hello, sphinx"
HELLO, SPHINX
The python:source block ran silently to seed dedent and GREETING; the subsequent python:run referenced both.
The matrix directive¶
The matrix directive renders a packageâs release compatibility matrix for a given axis. Unlike the click:* and python:* families, it runs a fixed generator rather than user-supplied Python, so it carries no execution surface and is registered without the click_extra_enable_exec_directives opt-in. Two axes are built in:
{matrix} pythonrenders the interpreter matrix (release ranges Ă Python versions).{matrix} <distribution>(like{matrix} click) renders a dependency matrix (release ranges Ă that dependencyâs versions).
The generated table lives in the source, kept current by the offline updater described below, so it shows up in the raw Markdown (and in pull-request diffs) and the HTML build needs no git access (it works on a shallow clone). There are two ways to write it, both refreshed by the same refresh-directives command:
A directive fence,
```{matrix} pythonâŠ```, rendered by Sphinx. Simplest on a docs-only page, but GitHub shows the fenced block as a code block. An empty fence falls back to generating from the git tags at build time, so a freshly authored block renders before its first refresh.A comment marker region,
<!-- matrix python -->âŠ<!-- matrix-end -->, with the raw table between the markers. Being plain Markdown, it renders as a real table on GitHub and PyPI as well as in Sphinx. Options go in the start comment askey=valuepairs and bare flags:<!-- matrix click show-spec -->.install.mdâs tables use this form so they render everywhere.
The examples below use the directive fence; the marker form takes the same axis and options.
The python axis¶
This project uses it for the Python compatibility table in install.md. You write the block with just its axis and options:
```{matrix} python
:package: click-extra
```
and the updater fills in the table below the options, regenerated from every vMAJOR.MINOR.PATCH tag (reading the declared Python support from the Programming Language :: Python :: X.Y classifiers in pyproject.toml, falling back to requires-python, Poetryâs python = "...", then setup.pyâs python_requires). Consecutive releases that agree are grouped into one row, and a floor-only declaration is capped at the latest Python released while the range was current:
```{matrix} python
:package: click-extra
| `click-extra` | Released | `3.14` | `3.13` | `3.12` | `3.11` | `3.10` | `3.9` | `3.8` | `3.7` |
| :------------------ | :--------- | :----: | :----: | :----: | :----: | :----: | :---: | :---: | :---: |
| `6.2.x` â `8.x` | 2025-11-04 | â
| â
| â
| â
| â
| â | â | â |
| `6.0.x` â `6.1.x` | 2025-10-08 | â
| â
| â
| â
| â | â | â | â |
| `5.0.x` â `6.0.x` | 2025-05-13 | â | â
| â
| â
| â | â | â | â |
| `4.11.x` â `4.15.x` | 2024-10-08 | â | â
| â
| â
| â
| â | â | â |
| `4.9.x` â `4.10.x` | 2024-07-25 | â | â | â
| â
| â
| â
| â | â |
| `4.0.x` â `4.8.x` | 2023-05-08 | â | â | â
| â
| â
| â
| â
| â |
| `0.0.x` â `3.10.x` | 2021-10-18 | â | â | â | â
| â
| â
| â
| â
|
```
Three states, two sources¶
A release declares its Python support twice, and the two declarations answer different questions. The classifier list is what the project claims to have tested. requires-python is what an installer enforces: fall outside it and pip refuses to install, whatever the classifiers say. The matrix keeps them apart:
Cell |
Meaning |
|---|---|
â |
Declared, via a |
â |
Ruled out by |
â |
Neither. The release never claimed that version, and nothing in its metadata stops you. |
The third state is what a two-state table has to lie about. When 4.9.0 shipped in July 2024 it declared requires-python = ">= 3.9" with classifiers up to 3.12, and Python 3.13 did not exist yet. Marking that cell â would assert an incompatibility nobody ever declared, so it renders â instead, while 3.8 stays â because the >= 3.9 floor genuinely rules it out.
The result reads as a staircase: â fills the lower-left as the floor rises over the years, â
the middle band, and â the upper-right where the future had not happened yet.
A dependency axis¶
{matrix} <distribution> tracks a runtime dependency instead. For each release range it reads that distributionâs requirement specifier (PEP 621, Poetry, or setup.py) and marks â
/ â for each column version with packaging. An extras bracket and an environment marker are both transparent: tabulate[widechars]>=0.9 and tomli>=2; python_version<'3.11' each track the plain >= range. The distribution is matched on its PEP 503 normalized name, looked up in the runtime dependencies then in those behind an extra. Development dependency groups (PEP 735) are skipped, since no installer resolves them for a consumer. Columns are auto-derived: a minor series stays a single X.Y column unless an open (>=) floor pins a specific patch, in which case it splits into X.Y.0 plus that floor; the left edge is the version resolved in uv.lock. Add :show-spec: for a Spec column with each rangeâs raw specifier, in the releaseâs own spelling. Cells here stay two-valued: unlike Python, a dependency has no informational second declaration to disagree with its specifier, so there is nothing an undeclared cell could mean.
Poetryâs own range syntax is translated to PEP 440 before evaluation, since a projectâs older tags usually predate its move to PEP 621. Carets follow Poetryâs rule of bumping the leftmost non-zero component, so ^1.2.3 caps at 2.0.0 while ^0.2.3 caps at 0.3.0 and ^0.0.3 at 0.0.4: under a 0. prefix every release may break, and a caret there covers far less than the major series. Tilde and wildcard ranges (~1, ~1.2, 1.*, 1.2.*) translate the same way.
This project uses it for the Click compatibility table:
```{matrix} click
:package: click-extra
:show-spec:
| `click-extra` | Released | Spec | `8.4.2` | `8.4.1` | `8.4.0` | `8.3.3` | `8.3.1` | `8.3.0` | `8.2` | `8.1` | `8.0` |
| :------------------ | :--------- | :-------- | :-----: | :-----: | :-----: | :-----: | :-----: | :-----: | :---: | :---: | :---: |
| `8.x` | 2026-06-22 | `>=8.3.1` | â
| â
| â
| â
| â
| â | â | â | â |
| `7.17.x` â `7.20.x` | 2026-05-25 | `>=8.4.1` | â
| â
| â | â | â | â | â | â | â |
| `7.15.x` â `7.16.x` | 2026-05-03 | `>=8.3.1` | â
| â
| â
| â
| â
| â | â | â | â |
| `7.14.1` | 2026-04-26 | `>=8.1` | â
| â
| â
| â
| â
| â
| â
| â
| â |
| `7.14.0` | 2026-04-24 | `>=8.3.3` | â
| â
| â
| â
| â | â | â | â | â |
| `7.0.x` â `7.13.x` | 2025-11-17 | `>=8.3.1` | â
| â
| â
| â
| â
| â | â | â | â |
| `6.x` | 2025-09-25 | `>=8.3.0` | â
| â
| â
| â
| â
| â
| â | â | â |
| `5.x` | 2025-05-13 | `~=8.2.0` | â | â | â | â | â | â | â
| â | â |
| `4.9.x` â `4.15.x` | 2024-07-25 | `~=8.1.4` | â | â | â | â | â | â | â | â
| â |
| `1.7.x` â `4.8.x` | 2022-03-31 | `^8.1.1` | â
| â
| â
| â
| â
| â
| â
| â
| â |
| `0.0.x` â `1.6.x` | 2021-10-18 | `^8.0.2` | â
| â
| â
| â
| â
| â
| â
| â
| â
|
```
Options¶
Option |
Effect |
Default |
|---|---|---|
|
Header column label, rendered in backticks. |
repository folder name |
|
Git working tree to walk, absolute or relative to the documented projectâs root. |
projectâs git root |
|
Drop release rows below this package version. |
none (all tags) |
|
Regex selecting release tags. |
|
|
Left-to-right ordering of the version columns: |
|
|
Top-to-bottom ordering of the release rows: |
|
|
( |
none (all columns) |
|
(dependency axis) Add a |
off |
The :path: option makes the directive reusable across repositories: point it at a sibling checkout to render another packageâs matrix.
Keeping the tables current¶
The embedded tables are refreshed offline, formatter-style, by the refresh-directives command (which needs the sphinx extra):
$ click-extra refresh-directives docs/
It walks the given Markdown files or directories, regenerates each matrix blockâs table (both the {matrix} directive fences and the <!-- matrix ⊠--> marker regions) from that blockâs axis, options, and the projectâs git tags, and rewrites the block in place. Pass --check to write nothing and exit non-zero when a block is stale, so a CI job or pre-commit hook can fail on an out-of-date matrix. The same logic is importable as click_extra.sphinx.matrix.update_matrix_blocks(paths, check=...). A block whose generation fails (missing git binary, non-repository :path:, no matching data) is left untouched, so a transient failure never wipes a good table. Examples nested inside longer code fences (like the ones on this page) are documented illustrations and are never refreshed.
The same command also refreshes the python:render :mirror: regions found in the same files, in both the visible fence and the invisible <!-- mirror-src --> comment forms, by executing each blockâs Python (click_extra.sphinx.python.update_mirror_blocks(paths, check=...) is the importable form). One invocation therefore keeps every self-updating block of a documentation tree current, whatever its kind.
Note
Only the updater (and the empty-block fallback) needs the release tags, since it is the part that shells out to git. Run it wherever the full tag history is available. The HTML build renders the embedded table verbatim and needs no git access, so shallow clones and read-only build hosts render the matrix fine.
For content a directive cannot produce on its own, like a shared registry dumped into several files or an external generatorâs output, the same marker machinery is exposed as three primitives, importable from click_extra.sphinx:
marker_res(name)builds the(open, close)regexes of a<!-- name ⊠-->/<!-- name-end -->region, the grammar every self-updating marker shares.replace_region(text, name, content)swaps the body between those markers forcontent, keeping the markers so the region round-trips. It returns the text unchanged when either marker is absent, so it is safe to fan out over files that do not all carry the region.update_blocks(paths, rewrite, check=...)applies arewrite(text, path)callback to every Markdown file underpaths, writing back only the ones it changed (or, undercheck, returning the ones it would change). It is the read-rewrite-report loop behind bothupdate_matrix_blocksandupdate_mirror_blocks.
replace_region is the counterpart to those two refreshers for content that originates outside the document rather than from an inline directive.
Man pages¶
The Sphinx extension can render the roff man page tree of any Click CLI alongside the HTML build, so a projectâs docs site, release pipeline, and downstream packagers all share a single generator. Add one or more entries to click_extra_manpages in conf.py:
conf.py¶extensions = ["click_extra.sphinx"]
click_extra_manpages = [
{
"script": "my_pkg.cli:my_cli", # required
"prog_name": "my-cli", # optional, defaults to the resolved command's name
"output_dir": "man", # optional, defaults to "man"
"render_html": True, # optional, defaults to True
},
]
On every HTML build, the hook resolves each script with the same scanner as the click-extra wrap --man CLI and writes one .1 file per (sub)command into <outdir>/<output_dir>/, mirroring what click-extra wrap --man --output-dir DIR -- SCRIPT produces from the command line. An empty (or absent) list keeps the hook silent: no man pages, no warnings.
Only HTML-family builders (html, dirhtml, singlehtml) trigger the hook. Other builders (linkcheck, man, epub, coverage) skip it: roff in their output trees would be redundant or confusing.
The generator honors SOURCE_DATE_EPOCH for reproducible builds and inherits every option-group and Cloup-aware rendering rule documented in the man-page reference.
HTML siblings¶
Browsers download .1 files rather than render them, so each emitted page is also passed through a roff â HTML renderer when one is available. The result lands next to the source as <page>.<section>.html (like my-cli.1.html).
The hook tries mandoc -Thtml first, then groff -Thtml -mandoc, picking whichever it finds on PATH. mandoc is preferred for its semantic anchors: every section and option gets a stable id, which makes deep-linking work. If neither renderer is installed, the build still produces the .1 files and logs a single info-level notice, which render_html: False suppresses.
A typical CI container ships one or the other: Debian and Ubuntu have groff in build-essential, BSDs and recent macOS images ship mandoc. To pin the renderer on GitHub Actions, install it explicitly:
.github/workflows/docs.yaml¶- name: Install mandoc
run: sudo apt-get install --yes mandoc
Cross-linking from prose¶
To make the standard :manpage: role link to the HTML siblings the hook emits, set Sphinxâs manpages_url to the matching path:
conf.py¶manpages_url = "man/{page}.{section}.html"
With that in place, :manpage:`my-cli(1)` in any docstring or .md file resolves to man/my-cli.1.html in the rendered docs. The same template covers every subcommand page, since {page} matches the full hyphenated name the generator produces (my-cli, my-cli-build, my-cli-build-all).
Leaving manpages_url unset is fine. The role still renders as styled text; only the hyperlink target is missing.
click-extra-manpages directive¶
For a discoverable landing page, drop the click-extra-manpages directive anywhere in the docs. It walks click_extra_manpages and emits a bullet list with one entry per (sub)command in each declared tree, linked to the HTML sibling produced by the hook:
```{click-extra-manpages}
```
The directive takes no arguments. URLs are computed relative to the enclosing pageâs actual published location, not its source docname, so the same call resolves correctly on a top-level page, on a page nested under a subdirectory, and under any HTML-family builder: dirhtml publishes each page one directory deeper, as <docname>/index.html rather than <docname>.html, while singlehtml folds every document into one page at the build root, so its links need no directory traversal at all. When click_extra_manpages is empty, the directive renders nothing.
A live instance of the directive ships at the bottom of the man-page reference: the list there is what this projectâs own click_extra_manpages entry produces at build time.
GitHub alerts¶
Click Extraâs Sphinx extension automatically converts GitHub-flavored Markdown alerts into MyST admonitions.
This allows you to write documentation that renders correctly both on GitHub and in your Sphinx-generated documentation.
Deprecated since version 7.16.0: myst-parser 5.1.0 ships a native alert syntax extension covering the same five alert types. On that release and above, Click Extraâs converter steps aside: add "alert" to myst_enable_extensions and the rest of this section no longer applies, colon_fence included.
Setup¶
On myst-parser below 5.1.0, you need to enable the colon_fence extension in your Sphinx configuration, as the converter renders each alert as a colon fence:
conf.py¶extensions = [
...
"click_extra.sphinx",
]
myst_enable_extensions = ["colon_fence"]
Supported alert types¶
GitHub supports five alert types, all of which are replaced behind the scenes with their corresponding MyST admonitions:
Type |
GitHub syntax |
MyST syntax |
Rendered |
|---|---|---|---|
Note |
> [!NOTE]
> Useful information.
|
:::{note}
Useful information.
:::
|
Note Useful information. |
Tip |
> [!TIP]
> Helpful advice.
|
:::{tip}
Helpful advice.
:::
|
Tip Helpful advice. |
Important |
> [!IMPORTANT]
> Key information.
|
:::{important}
Key information.
:::
|
Important Key information. |
Warning |
> [!WARNING]
> Potential issues.
|
:::{warning}
Potential issues.
:::
|
Warning Potential issues. |
Caution |
> [!CAUTION]
> Negative consequences.
|
:::{caution}
Negative consequences.
:::
|
Caution Negative consequences. |
Usage¶
Write alerts using GitHubâs blockquote syntax:
> [!NOTE]
> This is a note that will render as an admonition in Sphinx.
> [!WARNING]
> Reader discretion is strongly advised.
These will render in Sphinx as:
Note
This is a note that will render as an admonition in Sphinx.
Warning
Reader discretion is strongly advised.
Rules¶
Playing with alerts on various GitHub websites, I reverse-engineered the following specifications:
Alert type must be in uppercase:
[!TIP], not[!tip].No spaces in the directive:
[! NOTE],[!NOTE ]or[ !NOTE]are invalid.Must be the first thing in the blockquote:
> Hello [!NOTE] This is a note.is interpreted as a normal blockquote, not an alert.Only the first line of the blockquote is parsed for the alert type: subsequent lines are considered part of the alert content.
The alert content can span multiple lines, as long as they are part of the same blockquote.
Empty blockquotes are ignored:
> [!TIP]without any content is not rendered.Nested blockquotes are supported: the alert content can contain other blockquotes, lists, code blocks, etc.
Nested alerts¶
GitHub alerts support nested content, including other blockquotes, lists, code blocks, and even nested alerts. This allows for complex documentation structures that render correctly both on GitHub and in Sphinx.
You can include various Markdown elements inside an alert:
> [!NOTE]
> This alert contains:
> - A bullet list
> - With multiple items
>
> And a code block:
> ```python
> print("Hello, world!")
> ```
Which renders as:
Note
This alert contains:
A bullet list
With multiple items
And a code block:
print("Hello, world!")
You can nest alerts within alerts for hierarchical information:
> [!WARNING]
> Be careful with this operation.
>
> > [!TIP]
> > If you encounter issues, try restarting the service.
Which renders as:
Warning
Be careful with this operation.
Tip
If you encounter issues, try restarting the service.
You can also mix GitHub alerts with MyST directives inside container directives:
````{note}
> [!TIP]
> First alert.
```{warning}
Nested MyST warning.
```
> [!CAUTION]
> Second alert after nested directive.
````
Note
Tip
First alert.
Warning
Nested MyST warning.
Caution
Second alert after nested directive.
For more complex documentation, you can combine multiple nested elements such as blockquotes, numbered lists, nested alerts, and code blocks:
> [!IMPORTANT]
> Before proceeding, ensure you have:
>
> 1. Backed up your data
> 2. Reviewed the changelog
>
> > This is important context that applies to all the steps above.
>
> > [!CAUTION]
> > This action cannot be undone.
>
> ```bash
> $ make backup
> ```
Which renders as:
Important
Before proceeding, ensure you have:
Backed up your data
Reviewed the changelog
This is important context that applies to all the steps above.
Caution
This action cannot be undone.
$ make backup
Todo-list deduplication¶
sphinx.ext.todo collects doctree nodes, not documented objects. A {todo} written once in a docstring therefore lands on the todolist page once per rendering of that docstring, and two conventions common to autodoc projects render one docstring several times:
A full-API page plus per-feature pages. A project that documents every module on one page, then documents the same modules again next to the prose explaining them, renders each docstring twice. Marking the second block
:no-index:does not help: that option suppresses the cross-reference target and the search-index entry, and leaves the docstring rendered in full.A package that re-exports its members.
automoduledocuments the imported names a package lists in__all__, so a symbol shows up once under the package and once under the module defining it. Both renderings can land on the same page.
The two multiply. Before this hook, Click Extraâs own todo-list showed 35 entries for 17 distinct {todo} directives, one of them repeated four times.
Nothing upstream deduplicates, so click_extra.sphinx trims the surplus nodes just before the list is rendered. No configuration is needed, and a project that enables neither the extension nor a todolist never notices the hook.
Among the renderings of one directive, the surviving entry is the first in (reached through a defining module, document name, position in the document) order. So a symbol reached through both its package and its module keeps the moduleâs attribution, and a reader following the original entry backlink lands on the same page from one build to the next.
Set the flag to get Sphinxâs raw output back, one entry per rendering:
conf.py¶click_extra_dedupe_todos = False
ANSI shell sessions¶
Sphinx extensions from Click Extra automaticcaly integrates the new ANSI-capable lexers for Pygments.
This allows you to render colored shell sessions in code blocks by referring to the ansi- prefixed lexers:
```{code-block} ansi-shell-session
$ # Print ANSI foreground colors.
$ for i in {0..255}; do \
> printf '\e[38;5;%dm%3d ' $i $i \
> (((i+3) % 18)) || printf '\e[0m\n' \
> done
[38;5;0m 0 [38;5;1m 1 [38;5;2m 2 [38;5;3m 3 [38;5;4m 4 [38;5;5m 5 [38;5;6m 6 [38;5;7m 7 [38;5;8m 8 [38;5;9m 9 [38;5;10m 10 [38;5;11m 11 [38;5;12m 12 [38;5;13m 13 [38;5;14m 14 [38;5;15m 15 [0m
[38;5;16m 16 [38;5;17m 17 [38;5;18m 18 [38;5;19m 19 [38;5;20m 20 [38;5;21m 21 [38;5;22m 22 [38;5;23m 23 [38;5;24m 24 [38;5;25m 25 [38;5;26m 26 [38;5;27m 27 [38;5;28m 28 [38;5;29m 29 [38;5;30m 30 [38;5;31m 31 [38;5;32m 32 [38;5;33m 33 [0m
[38;5;34m 34 [38;5;35m 35 [38;5;36m 36 [38;5;37m 37 [38;5;38m 38 [38;5;39m 39 [38;5;40m 40 [38;5;41m 41 [38;5;42m 42 [38;5;43m 43 [38;5;44m 44 [38;5;45m 45 [38;5;46m 46 [38;5;47m 47 [38;5;48m 48 [38;5;49m 49 [38;5;50m 50 [38;5;51m 51 [0m
[38;5;52m 52 [38;5;53m 53 [38;5;54m 54 [38;5;55m 55 [38;5;56m 56 [38;5;57m 57 [38;5;58m 58 [38;5;59m 59 [38;5;60m 60 [38;5;61m 61 [38;5;62m 62 [38;5;63m 63 [38;5;64m 64 [38;5;65m 65 [38;5;66m 66 [38;5;67m 67 [38;5;68m 68 [38;5;69m 69 [0m
[38;5;70m 70 [38;5;71m 71 [38;5;72m 72 [38;5;73m 73 [38;5;74m 74 [38;5;75m 75 [38;5;76m 76 [38;5;77m 77 [38;5;78m 78 [38;5;79m 79 [38;5;80m 80 [38;5;81m 81 [38;5;82m 82 [38;5;83m 83 [38;5;84m 84 [38;5;85m 85 [38;5;86m 86 [38;5;87m 87 [0m
[38;5;88m 88 [38;5;89m 89 [38;5;90m 90 [38;5;91m 91 [38;5;92m 92 [38;5;93m 93 [38;5;94m 94 [38;5;95m 95 [38;5;96m 96 [38;5;97m 97 [38;5;98m 98 [38;5;99m 99 [38;5;100m100 [38;5;101m101 [38;5;102m102 [38;5;103m103 [38;5;104m104 [38;5;105m105 [0m
[38;5;106m106 [38;5;107m107 [38;5;108m108 [38;5;109m109 [38;5;110m110 [38;5;111m111 [38;5;112m112 [38;5;113m113 [38;5;114m114 [38;5;115m115 [38;5;116m116 [38;5;117m117 [38;5;118m118 [38;5;119m119 [38;5;120m120 [38;5;121m121 [38;5;122m122 [38;5;123m123 [0m
[38;5;124m124 [38;5;125m125 [38;5;126m126 [38;5;127m127 [38;5;128m128 [38;5;129m129 [38;5;130m130 [38;5;131m131 [38;5;132m132 [38;5;133m133 [38;5;134m134 [38;5;135m135 [38;5;136m136 [38;5;137m137 [38;5;138m138 [38;5;139m139 [38;5;140m140 [38;5;141m141 [0m
[38;5;142m142 [38;5;143m143 [38;5;144m144 [38;5;145m145 [38;5;146m146 [38;5;147m147 [38;5;148m148 [38;5;149m149 [38;5;150m150 [38;5;151m151 [38;5;152m152 [38;5;153m153 [38;5;154m154 [38;5;155m155 [38;5;156m156 [38;5;157m157 [38;5;158m158 [38;5;159m159 [0m
[38;5;160m160 [38;5;161m161 [38;5;162m162 [38;5;163m163 [38;5;164m164 [38;5;165m165 [38;5;166m166 [38;5;167m167 [38;5;168m168 [38;5;169m169 [38;5;170m170 [38;5;171m171 [38;5;172m172 [38;5;173m173 [38;5;174m174 [38;5;175m175 [38;5;176m176 [38;5;177m177 [0m
[38;5;178m178 [38;5;179m179 [38;5;180m180 [38;5;181m181 [38;5;182m182 [38;5;183m183 [38;5;184m184 [38;5;185m185 [38;5;186m186 [38;5;187m187 [38;5;188m188 [38;5;189m189 [38;5;190m190 [38;5;191m191 [38;5;192m192 [38;5;193m193 [38;5;194m194 [38;5;195m195 [0m
[38;5;196m196 [38;5;197m197 [38;5;198m198 [38;5;199m199 [38;5;200m200 [38;5;201m201 [38;5;202m202 [38;5;203m203 [38;5;204m204 [38;5;205m205 [38;5;206m206 [38;5;207m207 [38;5;208m208 [38;5;209m209 [38;5;210m210 [38;5;211m211 [38;5;212m212 [38;5;213m213 [0m
[38;5;214m214 [38;5;215m215 [38;5;216m216 [38;5;217m217 [38;5;218m218 [38;5;219m219 [38;5;220m220 [38;5;221m221 [38;5;222m222 [38;5;223m223 [38;5;224m224 [38;5;225m225 [38;5;226m226 [38;5;227m227 [38;5;228m228 [38;5;229m229 [38;5;230m230 [38;5;231m231 [0m
[38;5;232m232 [38;5;233m233 [38;5;234m234 [38;5;235m235 [38;5;236m236 [38;5;237m237 [38;5;238m238 [38;5;239m239 [38;5;240m240 [38;5;241m241 [38;5;242m242 [38;5;243m243 [38;5;244m244 [38;5;245m245 [38;5;246m246 [38;5;247m247 [38;5;248m248 [38;5;249m249 [0m
[38;5;250m250 [38;5;251m251 [38;5;252m252 [38;5;253m253 [38;5;254m254 [38;5;255m255
```
.. code-block:: ansi-shell-session
$ # Print ANSI foreground colors.
$ for i in {0..255}; do \
> printf '\e[38;5;%dm%3d ' $i $i \
> (((i+3) % 18)) || printf '\e[0m\n' \
> done
[38;5;0m 0 [38;5;1m 1 [38;5;2m 2 [38;5;3m 3 [38;5;4m 4 [38;5;5m 5 [38;5;6m 6 [38;5;7m 7 [38;5;8m 8 [38;5;9m 9 [38;5;10m 10 [38;5;11m 11 [38;5;12m 12 [38;5;13m 13 [38;5;14m 14 [38;5;15m 15 [0m
[38;5;16m 16 [38;5;17m 17 [38;5;18m 18 [38;5;19m 19 [38;5;20m 20 [38;5;21m 21 [38;5;22m 22 [38;5;23m 23 [38;5;24m 24 [38;5;25m 25 [38;5;26m 26 [38;5;27m 27 [38;5;28m 28 [38;5;29m 29 [38;5;30m 30 [38;5;31m 31 [38;5;32m 32 [38;5;33m 33 [0m
[38;5;34m 34 [38;5;35m 35 [38;5;36m 36 [38;5;37m 37 [38;5;38m 38 [38;5;39m 39 [38;5;40m 40 [38;5;41m 41 [38;5;42m 42 [38;5;43m 43 [38;5;44m 44 [38;5;45m 45 [38;5;46m 46 [38;5;47m 47 [38;5;48m 48 [38;5;49m 49 [38;5;50m 50 [38;5;51m 51 [0m
[38;5;52m 52 [38;5;53m 53 [38;5;54m 54 [38;5;55m 55 [38;5;56m 56 [38;5;57m 57 [38;5;58m 58 [38;5;59m 59 [38;5;60m 60 [38;5;61m 61 [38;5;62m 62 [38;5;63m 63 [38;5;64m 64 [38;5;65m 65 [38;5;66m 66 [38;5;67m 67 [38;5;68m 68 [38;5;69m 69 [0m
[38;5;70m 70 [38;5;71m 71 [38;5;72m 72 [38;5;73m 73 [38;5;74m 74 [38;5;75m 75 [38;5;76m 76 [38;5;77m 77 [38;5;78m 78 [38;5;79m 79 [38;5;80m 80 [38;5;81m 81 [38;5;82m 82 [38;5;83m 83 [38;5;84m 84 [38;5;85m 85 [38;5;86m 86 [38;5;87m 87 [0m
[38;5;88m 88 [38;5;89m 89 [38;5;90m 90 [38;5;91m 91 [38;5;92m 92 [38;5;93m 93 [38;5;94m 94 [38;5;95m 95 [38;5;96m 96 [38;5;97m 97 [38;5;98m 98 [38;5;99m 99 [38;5;100m100 [38;5;101m101 [38;5;102m102 [38;5;103m103 [38;5;104m104 [38;5;105m105 [0m
[38;5;106m106 [38;5;107m107 [38;5;108m108 [38;5;109m109 [38;5;110m110 [38;5;111m111 [38;5;112m112 [38;5;113m113 [38;5;114m114 [38;5;115m115 [38;5;116m116 [38;5;117m117 [38;5;118m118 [38;5;119m119 [38;5;120m120 [38;5;121m121 [38;5;122m122 [38;5;123m123 [0m
[38;5;124m124 [38;5;125m125 [38;5;126m126 [38;5;127m127 [38;5;128m128 [38;5;129m129 [38;5;130m130 [38;5;131m131 [38;5;132m132 [38;5;133m133 [38;5;134m134 [38;5;135m135 [38;5;136m136 [38;5;137m137 [38;5;138m138 [38;5;139m139 [38;5;140m140 [38;5;141m141 [0m
[38;5;142m142 [38;5;143m143 [38;5;144m144 [38;5;145m145 [38;5;146m146 [38;5;147m147 [38;5;148m148 [38;5;149m149 [38;5;150m150 [38;5;151m151 [38;5;152m152 [38;5;153m153 [38;5;154m154 [38;5;155m155 [38;5;156m156 [38;5;157m157 [38;5;158m158 [38;5;159m159 [0m
[38;5;160m160 [38;5;161m161 [38;5;162m162 [38;5;163m163 [38;5;164m164 [38;5;165m165 [38;5;166m166 [38;5;167m167 [38;5;168m168 [38;5;169m169 [38;5;170m170 [38;5;171m171 [38;5;172m172 [38;5;173m173 [38;5;174m174 [38;5;175m175 [38;5;176m176 [38;5;177m177 [0m
[38;5;178m178 [38;5;179m179 [38;5;180m180 [38;5;181m181 [38;5;182m182 [38;5;183m183 [38;5;184m184 [38;5;185m185 [38;5;186m186 [38;5;187m187 [38;5;188m188 [38;5;189m189 [38;5;190m190 [38;5;191m191 [38;5;192m192 [38;5;193m193 [38;5;194m194 [38;5;195m195 [0m
[38;5;196m196 [38;5;197m197 [38;5;198m198 [38;5;199m199 [38;5;200m200 [38;5;201m201 [38;5;202m202 [38;5;203m203 [38;5;204m204 [38;5;205m205 [38;5;206m206 [38;5;207m207 [38;5;208m208 [38;5;209m209 [38;5;210m210 [38;5;211m211 [38;5;212m212 [38;5;213m213 [0m
[38;5;214m214 [38;5;215m215 [38;5;216m216 [38;5;217m217 [38;5;218m218 [38;5;219m219 [38;5;220m220 [38;5;221m221 [38;5;222m222 [38;5;223m223 [38;5;224m224 [38;5;225m225 [38;5;226m226 [38;5;227m227 [38;5;228m228 [38;5;229m229 [38;5;230m230 [38;5;231m231 [0m
[38;5;232m232 [38;5;233m233 [38;5;234m234 [38;5;235m235 [38;5;236m236 [38;5;237m237 [38;5;238m238 [38;5;239m239 [38;5;240m240 [38;5;241m241 [38;5;242m242 [38;5;243m243 [38;5;244m244 [38;5;245m245 [38;5;246m246 [38;5;247m247 [38;5;248m248 [38;5;249m249 [0m
[38;5;250m250 [38;5;251m251 [38;5;252m252 [38;5;253m253 [38;5;254m254 [38;5;255m255
In Sphinx, the snippet above renders to:
$ # Print ANSI foreground colors.
$ for i in {0..255}; do \
> printf '\e[38;5;%dm%3d ' $i $i \
> (((i+3) % 18)) || printf '\e[0m\n' \
> done
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
250 251 252 253 254 255
Legacy MyST + reStructuredText syntax¶
Before MyST was fully integrated into Sphinx, many projects used a mixed syntax setup with MyST and reStructuredText. If you are maintaining such a project or need to ensure compatibility with older documentation, you can use these legacy Sphinx snippets.
This rely on MySTâs ability to embed reStructuredText within MyST documents, via the {eval-rst} directive.
So instead of using the {click:source} and {click:run} MyST directive, you can wrap your reStructuredText code blocks with {eval-rst}:
```{eval-rst}
.. click:source::
from click import echo, command
@command
def yo_cli():
echo("Yo!")
.. click:run::
invoke(yo_cli)
```
Which renders to:
from click import echo, command
@command
def yo_cli():
echo("Yo!")
$ yo-cli
Yo!
Warning
CLI states and references are lost as soon as an {eval-rst} block ends. So a .. click:source:: directive needs to have all its associated .. click:run:: calls within the same rST block.
If not, you are likely to encounter execution tracebacks such as:
File ".../click-extra/docs/sphinx.md:372", line 1, in <module>
NameError: name 'yo_cli' is not defined
click_extra.sphinx API¶
classDiagram
StatelessDomain <|-- ClickDomain
StatelessDomain <|-- PythonDomain
Helpers and utilities for Sphinx.
Note
The MkDocs counterpart lives in click_extra.mkdocs, which achieves the same
ANSI color rendering by patching pymdownx.highlightâs formatter classes.
- click_extra.sphinx.MYST_NATIVE_ALERTS_VERSION = <Version('5.1.0')>
First
myst-parserrelease that ships the native"alert"syntax extension.Below this version,
click_extra.sphinx.alertspatches GitHub alert syntax into MyST admonitions via asource-read/include-readhook. At or above this version, the converter is skipped atsetup()time and projects should add"alert"tomyst_enable_extensionsinstead. A project with nomyst-parserinstalled writes no MyST document, so the converter is skipped there too.
- click_extra.sphinx.EXEC_DIRECTIVES_OPT_IN = 'click_extra_enable_exec_directives'
Name of the
conf.pyconfig flag that gates every code-execution directive.Default is
False. A project that addsclick_extra.sphinxto itsextensionslist gets the ANSI Pygments formatter unconditionally, plus the GitHub-alerts converter whenmyst-parseris belowMYST_NATIVE_ALERTS_VERSION(seealertsfor the deprecation rationale), but does not gain access to either theclick:*or thepython:*directive families until the maintainer opts in explicitly. Both familiesexecuser-supplied Python at build time with full Sphinx-process privileges; gating them behind a single explicit flag keeps a transitive import or a doc-only pull request from silently expanding the buildâs attack surface.
- click_extra.sphinx.SCREENSHOT_DIR_CONFIG = 'click_extra_screenshot_dir'
Name of the
conf.pyvalue locating the directoryclick:runwrites captures to.A path relative to the documentation source directory, holding the SVG a
click:runblock names with its:screenshot:option. Defaults toassets, matching where a Sphinx project conventionally keeps the images its pages embed, and where a README pointing at the repository finds them.
- click_extra.sphinx.SCREENSHOT_PRESET_CONFIG = 'click_extra_screenshot_preset'
Name of the
conf.pyvalue naming the terminal every capture is drawn as.One of
PRESETS, applied to eachclick:runblock whose:screenshot:does not name a preset of its own. Empty by default, which keeps the rendererâs neutral window: a project wanting all of its captures to look like the same desktop states it once here instead of on every block.
- click_extra.sphinx.SCREENSHOT_WATERMARK_CONFIG = 'click_extra_screenshot_watermark'
Name of the
conf.pyvalue crediting every capture aclick:runwrites.Empty by default, where the
screenshotcommand credits click-extra: a capture written by a documentation build is rewritten and committed on every build, so a mark naming a release would rewrite every image the day that release changes, and the page carrying the image already says what drew it. A project wanting one anyway states the text here, or per block with:screenshot-watermark:.
- click_extra.sphinx.RUN_CAPTURE_CONFIG = 'click_extra_run_capture'
Name of the
conf.pyvalue selecting the stream-capture mode for the CLIs thatclick:runandclick:treeexecute.Maps to the
captureparameter of ClickâsCliRunner,"sys"or"fd"(added in Click 8.4). Defaults to"fd"so a command writing throughsys.stdout.fileno()is captured at the file-descriptor level and renders, instead of aborting the build withio.UnsupportedOperation. Ignored on Click releases older than 8.4, which lack the parameter.
- click_extra.sphinx.setup(app)[source]
Register extensions to Sphinx.
Always-on features (no execution surface):
The ANSI-capable HTML formatter for Pygments (replaces
sphinx.highlighting.PygmentsBridgewith one that renders ANSI colors in code blocks).GitHub-flavored alert syntax (
> [!NOTE], etc.) in included and regular source files, converted to MyST/reST admonitions. Registered only when the installedmyst-parseris belowMYST_NATIVE_ALERTS_VERSION(5.1.0). On newer versions, the converter is skipped and a one-shot info message points users atmyst-parserâs native"alert"extension; with nomyst-parserinstalled it is skipped without a message. Seeclick_extra.sphinx.alertsfor the deprecation plan.The
matrixdirective, which renders a packageâs compatibility grid ({matrix} pythonor{matrix} <distribution>) from its git tag history. It runs a canned generator rather than user-supplied Python, so it carries no execution surface and needs no opt-in. Seeclick_extra.sphinx.matrix.Deduplication of the
todolistpage, whichsphinx.ext.todofills with one entry per rendering of a:todo:directive rather than one per directive. Inert on a project that enables neither the extension nor atodolist, and switched off withclick_extra.sphinx.todos.DEDUPE_TODOS_CONFIG. Seeclick_extra.sphinx.todos.
Opt-in features (gated behind
click_extra_enable_exec_directives):click:source/click:runto define and execute Click CLIs at build time.python:source/python:runto execute arbitrary Python at build time and render its source or capturedstdout.python:render/python:render-myst/python:render-rstto execute arbitrary Python and parse the capturedstdoutas live document content.
All directives in the opt-in group execute user-supplied Python with the same privileges as the Sphinx process. They are therefore disabled by default. Set
click_extra_enable_exec_directives = Trueinconf.pyto register them.Caution
This function forces the Sphinx app to use
sphinx.highlighting.PygmentsBridgeinstead of the default HTML formatter to add support for ANSI colors in code blocks.- Return type:
ExtensionMetadata