click_extra.sphinx package

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-parser release that ships the native "alert" syntax extension.

Below this version, click_extra.sphinx.alerts patches GitHub alert syntax into MyST admonitions via a source-read / include-read hook. At or above this version, the converter is skipped at setup() time and projects should add "alert" to myst_enable_extensions instead. A project with no myst-parser installed 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.py config flag that gates every code-execution directive.

Default is False. A project that adds click_extra.sphinx to its extensions list gets the ANSI Pygments formatter unconditionally, plus the GitHub-alerts converter when myst-parser is below MYST_NATIVE_ALERTS_VERSION (see alerts for the deprecation rationale), but does not gain access to either the click:* or the python:* directive families until the maintainer opts in explicitly. Both families exec user-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.py value locating the directory click:run writes captures to.

A path relative to the documentation source directory, holding the SVG a click:run block names with its :screenshot: option. Defaults to assets, 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.py value naming the terminal every capture is drawn as.

One of PRESETS, applied to each click:run block 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.py value crediting every capture a click:run writes.

Empty by default, where the screenshot command 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.py value selecting the stream-capture mode for the CLIs that click:run and click:tree execute.

Maps to the capture parameter of Click’s CliRunner, "sys" or "fd" (added in Click 8.4). Defaults to "fd" so a command writing through sys.stdout.fileno() is captured at the file-descriptor level and renders, instead of aborting the build with io.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.PygmentsBridge with 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 installed myst-parser is below MYST_NATIVE_ALERTS_VERSION (5.1.0). On newer versions, the converter is skipped and a one-shot info message points users at myst-parser’s native "alert" extension; with no myst-parser installed it is skipped without a message. See click_extra.sphinx.alerts for the deprecation plan.

  • The matrix directive, which renders a package’s compatibility grid ({matrix} python or {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. See click_extra.sphinx.matrix.

  • Deduplication of the todolist page, which sphinx.ext.todo fills with one entry per rendering of a :todo: directive rather than one per directive. Inert on a project that enables neither the extension nor a todolist, and switched off with click_extra.sphinx.todos.DEDUPE_TODOS_CONFIG. See click_extra.sphinx.todos.

Opt-in features (gated behind click_extra_enable_exec_directives):

  • click:source / click:run to define and execute Click CLIs at build time.

  • python:source / python:run to execute arbitrary Python at build time and render its source or captured stdout.

  • python:render / python:render-myst / python:render-rst to execute arbitrary Python and parse the captured stdout as 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 = True in conf.py to register them.

Caution

This function forces the Sphinx app to use sphinx.highlighting.PygmentsBridge instead of the default HTML formatter to add support for ANSI colors in code blocks.

Return type:

ExtensionMetadata

Submodules

click_extra.sphinx.alerts module

Utilities to convert GitHub alerts into MyST admonitions for Sphinx.

Deprecated since version 7.16.0: myst-parser 5.1+ ships a native "alert" syntax extension that renders GitHub alerts as Sphinx admonitions, covering the same ground as this regex-based converter. click_extra.sphinx.setup() only wires the converter into Sphinx when the installed myst-parser is below 5.1.0 (see click_extra.sphinx.MYST_NATIVE_ALERTS_VERSION); on newer releases the hook is skipped at setup time and a log message points projects at myst-parser’s native extension, which they enable by adding "alert" to myst_enable_extensions.

Todo

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

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

See also

added in version 5.1.0.

click_extra.sphinx.alerts.GITHUB_ALERT_PATTERN = re.compile('^\\s*\\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\\]\\s*$')

Regex pattern to match GitHub alert type declaration (without leading >).

click_extra.sphinx.alerts.QUOTE_PREFIX_PATTERN = re.compile('^(\\s*)((?:>\\s*)+)(.*)$')

Regex pattern to extract indent, quote markers, and content.

click_extra.sphinx.alerts.CODE_FENCE_PATTERN = re.compile('^(\\s*)(`{3,}|~{3,})(.*)$')

Regex pattern to match code fence opening/closing lines.

click_extra.sphinx.alerts.INDENTED_CODE_BLOCK_PATTERN = re.compile('^( {4}|\\t)')

Regex pattern to match indented code block lines (4 spaces or 1 tab).

class click_extra.sphinx.alerts.Alert(alert_type, indent, depth, has_nested=False, opening_line_index=0)[source]

Bases: object

Represents a GitHub alert being processed.

alert_type: str
indent: str
depth: int
has_nested: bool = False
opening_line_index: int = 0
class click_extra.sphinx.alerts.FenceState(char, length, indent, is_code_block)[source]

Bases: object

Tracks code fence state.

char: str
length: int
indent: str
is_code_block: bool
class click_extra.sphinx.alerts.ParserState(result=<factory>, alert_stack=<factory>, fence_stack=<factory>, prev_line_blank=True, modified=False, just_opened_fence_directive=False)[source]

Bases: object

Mutable state for the alert parser.

result: list[str]
alert_stack: list[Alert]
fence_stack: list[FenceState]
prev_line_blank: bool = True
modified: bool = False
just_opened_fence_directive: bool = False
in_code_block()[source]

Check if currently inside a code block.

Return type:

bool

click_extra.sphinx.alerts.check_colon_fence(app: Sphinx) None[source]

Check that colon_fence support is enabled for MyST.

Raises:

ConfigError – If colon_fence is not in myst_enable_extensions.

Return type:

None

click_extra.sphinx.alerts.count_quote_depth(line)[source]

Parse a line to extract indent, quote depth, and content.

Return type:

tuple[str, int, str]

Returns:

tuple of (indent, depth, content) where depth is the number of > markers.

click_extra.sphinx.alerts.process_fence(state, indent, chars, after)[source]

Process a fence line, updating fence stack.

Return type:

None

click_extra.sphinx.alerts.close_alerts_to_depth(state, target_depth)[source]

Close all alerts deeper than target_depth.

When the alert has no body content (just the directive line, like a bare > [!TIP]), inject a MyST comment placeholder before the closing fence. Sphinx silently drops admonitions with an empty body, but myst-parser 5.1+’s native "alert" extension renders a title-only admonition for that input. The MyST comment is consumed at parse time, so the rendered HTML matches the upstream output: <div class="admonition tip"><p class="admonition-title">Tip</p></div>.

Return type:

None

click_extra.sphinx.alerts.mark_parent_nested(state)[source]

Mark the parent alert as having a nested alert and update its opening.

Return type:

None

click_extra.sphinx.alerts.open_alert(state, alert_type, indent, depth)[source]

Open a new alert at the given depth.

Return type:

None

click_extra.sphinx.alerts.process_quoted_line(state, line)[source]

Process a line that starts with quote markers.

Returns True if the line was handled as part of an alert.

Return type:

bool

click_extra.sphinx.alerts.replace_github_alerts(text)[source]

Transform GitHub alerts into MyST admonitions.

Identify GitHub alerts in the provided text and transform them into colon-fenced ::: MyST admonitions.

Returns None if no transformation was applied, else returns the transformed text.

Return type:

str | None

click_extra.sphinx.alerts.convert_github_alerts(app, *args)[source]

Convert GitHub alerts into MyST admonitions in content blocks.

Return type:

None

click_extra.sphinx.click module

Sphinx rendering of CLI based on Click Extra.

See also

These directives are based on Pallets’ Sphinx Themes, released under a BSD-3-Clause license.

Compared to the latter, it:

  • Add support for MyST syntax.

  • Adds rendering of ANSI codes in CLI results.

  • Has better error handling and reporting which helps you pinpoint the failing code in your documentation.

  • Removes the println function which was used to explicitly print a blank line. This is no longer needed as it is now handled natively.

click_extra.sphinx.click.RST_INDENT = '   '

The indentation used for rST code blocks lines.

click_extra.sphinx.click.PROMPT_SIGIL = '$'

Sigil a block draws before the command it ran.

Fixed rather than taken from PROMPT, which answers to the platform the build runs on: a page would otherwise prompt with a > for every reader whose documentation was built on Windows. A capture drawn as another terminal swaps it for that terminal’s own, see TerminalPreset.

Passed as the prompt argument of format_cli_prompt(), which is what builds the line for both the results code block and the SVG capture drawn from it.

click_extra.sphinx.click.DEFAULT_SCREENSHOT_DIR = 'assets'

Directory a click:run :screenshot: capture is written to by default.

Relative to the documentation source root. Overridden by the click_extra_screenshot_dir conf.py value.

click_extra.sphinx.click.SCREENSHOT_MARKER_START = '<!-- screenshot -->'

Opening marker of a click:run :mirror: region.

Written on its own line, directly below the fence, and paired with SCREENSHOT_MARKER_END. The region holds the Markdown link to the capture the block’s :screenshot: option names, so the image shows wherever the raw Markdown is read: on GitHub, on PyPI, in an editor’s preview.

Same <!-- name --> / <!-- name-end --> grammar as the python:render :mirror: regions, under a name saying what this one holds. Unlike those, the region’s content is derived from the option alone, never from executing anything: it goes stale only when the capture is renamed, which is why no build-time pass regenerates it in memory.

click_extra.sphinx.click.SCREENSHOT_MARKER_END = '<!-- screenshot-end -->'

Closing marker of a :mirror: region. See SCREENSHOT_MARKER_START.

click_extra.sphinx.click.MYST_CONTENT_OFFSET_INFLATED_MAX = <Version('5.1.0')>

Last myst-parser release that miscomputes a directive’s content_offset.

Up to and including this version, myst-parser over-counts content_offset by one whenever an option block is followed by a body ending in blank line(s): the parsed body is rebuilt through a string round-trip that drops one trailing blank line, so the option-block line count comes out one too high. This shifts the reported source line of every body element down by one.

This fix is not yet upstream: the open rework in #1175 does not include it, and also makes content_offset document-relative. So the release that eventually lands the fix requires ClickDirective.abs_content_offset to converge on the rST branch it already carries, not merely drop this compensation. See docs/upstream.md.

Todo

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

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

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

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

class click_extra.sphinx.click.TerminatedEchoingStdin(input, output)[source]

Bases: EchoingStdin

Like click.testing.EchoingStdin but adds a visible ^D in place of the EOT character ().

ClickRunner.invoke() adds  when terminate_input=True.

click_extra.sphinx.click.patch_subprocess()[source]

Patch subprocess to work better with ClickRunner.invoke().

subprocess.call output is redirected to click.echo so it shows up in the example output.

Caution

The replacement is installed on the subprocess module itself (not thread-local), so for the duration of the with block any other code in the same process that calls subprocess.call sees the patched version. With parallel_read_safe = True declared on ClickDomain, a parallel reader running concurrently on a different document gets the patched subprocess.call too. The redirection is benign (output goes to click.echo) but the race is real, and the parallel-safe claim is weaker than it looks for documents that themselves shell out via subprocess.call.

click_extra.sphinx.click.program_from_command_line(command_line)[source]

The program name Click is given for a command line a block displays.

A block documenting a multi-word program has to hand Click all of it: the command reads its own name back out of the context, for its usage line and for any output quoting the invocation that produced it, like the provenance header of a Carapace spec. Handing over the last word alone makes an image contradict the prompt drawn right above it.

Only an interpreter prefix is dropped, since it names no program: see _INTERPRETER_RE.

Return type:

str

class click_extra.sphinx.click.ClickRunner(capture=None)[source]

Bases: CliRunner

A sub-class of click.testing.CliRunner for Sphinx directive execution.

Produces unfiltered ANSI codes so that the Directive sub-classes below can render colors in the HTML output. Because Click Extra executes the documented command here, invoke() forces color across both color systems a CLI might use: color=True covers Click’s (should_strip_ansi), and forced_color() sets FORCE_COLOR for Rich’s (which rich-click uses and color=True never reaches). The MkDocs plugin shares the latter lever but cannot pass color=True, since it patches a renderer it never executes.

On Click 8.4+ the runner defaults to capture="fd" on Unix (overridable through the click_extra_run_capture conf.py value) so a documented command that writes through sys.stdout.fileno() is captured and rendered, instead of aborting the build with io.UnsupportedOperation. On Windows, where fd-backed streams are not supported, the default falls back to capture="sys".

isolation(*args, **kwargs)[source]

Echo a ^D marker at the end of the isolated stdin.

Todo

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

invoke(cli, args=None, prog_name=None, input=None, terminate_input=False, env=None, _output_lines=None, _show_prompt=True, **extra)[source]

Like CliRunner.invoke but displays what the user would enter in the terminal for env vars, command arguments, and prompts.

Parameters:
  • terminate_input – Whether to display ^D after a list of input.

  • _output_lines – A list used internally to collect lines to be displayed.

  • _show_prompt – Whether to draw the invocation above the output. Set from the directive’s :show-prompt: / :hide-prompt: options, see ClickDirective.show_prompt.

Return type:

Result

execute_source(directive)[source]

Execute the given code, adding it to the runner’s namespace.

Return type:

None

run_cli(directive)[source]

Execute the given source_code.

Returns a simulation of terminal execution, including a mix of input, output, prompts and tracebacks.

The execution context is augmented, so you can refer directly to these functions in the provided source_code:

  • invoke(): which is the same as ClickRunner.invoke()

  • isolated_filesystem(): A context manager that changes to a temporary directory while executing the block.

If any local variable in the provided source_code conflicts with these functions, a RuntimeError is raised to help you pinpoint the issue.

Return type:

list[str]

class click_extra.sphinx.click.ClickDirective(name, arguments, options, content, lineno, content_offset, block_text, state, state_machine)[source]

Bases: SphinxDirective

Base class of every click:* directive.

The two directive parsers count content_offset from different places, so anything naming a document line goes through abs_content_offset rather than reading content_offset directly.

has_content = True

May the directive have content?

required_arguments = 0

Number of required directive arguments.

optional_arguments = 1

The optional argument overrides the default Pygments language to use.

final_argument_whitespace = False

May the final argument contain whitespace?

option_spec: ClassVar[OptionSpec] = {'caption': <function unchanged_required>, 'class': <function class_option>, 'dedent': <function optional_int>, 'emphasize-lines': <function unchanged_required>, 'emphasize-result-lines': <function unchanged_required>, 'force': <function flag>, 'hide-prompt': <function flag>, 'hide-results': <function flag>, 'hide-source': <function flag>, 'language': <function unchanged_required>, 'lineno-start': <class 'int'>, 'linenos': <function flag>, 'name': <function unchanged>, 'show-prompt': <function flag>, 'show-results': <function flag>, 'show-source': <function flag>}

Options supported by this directive.

Support the same options as sphinx.directives.code.CodeBlock, and some specific to Click directives.

The standard emphasize-lines option applies to the source block only. Use emphasize-result-lines to highlight specific lines in the captured output block, with the same syntax (like :emphasize-result-lines: 1,3-5).

default_language: str

Default highlighting language to use to render the code block.

All Pygments’ languages short names are recognized.

show_source_by_default: bool = True

Whether to render the source code of the example in the code block.

show_results_by_default: bool = True

Whether to render the results of the example in the code block.

show_prompt_by_default: bool = True

Whether to draw the invocation above the results it produced.

runner_method: str

The name of the method to call on the ClickRunner instance.

runner_attr: ClassVar[str] = 'click_runner'

Name of the attribute holding the runner on the doctree.

Subclasses (like PythonDirective) override this so the Click and Python runners don’t collide on the same document.

runner_factory

Class to instantiate for the per-document runner.

Defaults to ClickRunner in ClickDirective (set after the class definition to break the forward reference).

alias of ClickRunner

property runner

Get or create the per-document runner.

Creates one runner per document, keyed by runner_attr.

property language: str[source]

Short name of the Pygments lexer used to highlight the code block.

Returns, in order of precedence, the language specified in the :language: directive options, the first argument of the directive (if any), or the default set in the directive class.

code_block_options(target='source')[source]

Render the options supported by Sphinx’ native code-block directive.

target selects which block these options will be attached to: "source" for the directive’s input source code, "results" for the captured output. emphasize-lines routes to the source block; emphasize-result-lines is rewritten as emphasize-lines on the results block, so authors can highlight different lines in each.

Return type:

list[str]

property show_source: bool[source]

Whether to show the source code of the example in the code block.

The last occurrence of either show-source or hide-source options wins. If neither is set, the default is taken from show_source_by_default.

property show_results: bool[source]

Whether to show the results of running the example in the code block.

The last occurrence of either show-results or hide-results options wins. If neither is set, the default is taken from show_results_by_default.

property show_prompt: bool[source]

Whether to draw the invocation above the output it produced.

The last occurrence of either show-prompt or hide-prompt options wins. If neither is set, the default is taken from show_prompt_by_default.

The prompt is one line rendered by format_cli_prompt(), prepended to the captured output. It is therefore part of the results, not a block of its own: hiding it drops it from the results code block and from the SVG a :screenshot: writes, which is drawn from the same lines. Reach for it when the invocation is noise the surrounding prose already carries, or when a capture is wanted as bare output.

property is_myst_syntax: bool[source]

Check if the current directive is written with MyST syntax.

property abs_content_offset: int[source]

0-based offset of the directive’s first content line in the document.

Both parsers expose a content_offset and they count it from different places: docutils from the top of the document, myst-parser from the directive’s own first line. Everything naming a document line reads this property instead, so the two conventions are reconciled in one place: the variable-conflict error of ClickRunner.run_cli(), and the source-line labels parse_into_section() attaches to a generated block.

Named after docutils’ abs_line_offset(), whose convention it follows.

render_code_block(lines, language, target='source')[source]

Render the code block with the source code or results.

target is forwarded to code_block_options() so the emphasize-lines / emphasize-result-lines split routes the right highlighting to each block.

Return type:

list[str]

property screenshot: str | None[source]

Name of the SVG capture this block renders to, without its extension.

Set by the :screenshot: option. None when the block renders its results as a code block, which is the default.

property screenshot_background: CaptureBackground[source]

Chrome the capture is drawn on, set by :screenshot-background:.

Defaults to the dark chrome a terminal and this package’s default theme both look like. A block rendering a light-background theme says so, or its capture washes out: see CaptureBackground.

property screenshot_columns: int | Literal['auto'][source]

Width the capture is laid out at, set by :screenshot-columns:.

Defaults to the fixed width a terminal capture is taken at. auto sizes the image to the longest line the block printed, which is what a run holding a line Click does not wrap needs to keep on one line: a prompt, a wide table, a machine-readable dump.

property screenshot_frame: dict[str, Any][source]

Window the capture is drawn in, set by the :screenshot-*: options.

Each entry is left out when its option is: the renderer then picks what the chrome asks for, which is what every block on these pages wants. :screenshot-backdrop:, :screenshot-border: and :screenshot-shadow: take a CSS color (or none to paint neither), :screenshot-border-width:, :screenshot-margin:, :screenshot-padding: and :screenshot-radius: take pixels, :screenshot-opacity: how solid the window’s body is, :screenshot-title: the caption drawn in the window’s own chrome, and :screenshot-watermark: a credit line for the image’s corner, which no capture carries here unless asked for. :screenshot-line-numbers: is a flag, numbering every line the block rendered, its prompt first. See render_svg().

A block naming no preset falls back to the one the click_extra_screenshot_preset conf.py value names, so a project drawing all of its captures as the same terminal states it once.

write_screenshot(results)[source]

Write the captured output as an SVG beside the documentation.

The file lands in the directory the click_extra_screenshot_dir conf.py value names, under the source root, so a README pointing at the repository embeds the very output this page renders live.

This is a side effect, not a rendering: the page keeps its results code block, which stays selectable, searchable and theme-aware where an image would not be. Use :mirror: to put the image on the page as well.

Writing during the build keeps the committed asset in step with the CLI without anyone remembering to refresh it, and it is deterministic: unique_id is pinned to the asset’s name, so an unchanged CLI rewrites byte-identical bytes and leaves the working tree clean.

Note

That refresh only happens when the document carrying the block is re-parsed: Sphinx’s environment cache skips unchanged sources. A change on the package side (a new config format widening a --config default, say) leaves every capture stale until a rebuild with a fresh environment (sphinx-build -E).

Return type:

None

run()[source]

Execute the directive and render its source and results.

Return type:

list[Node]

class click_extra.sphinx.click.SourceDirective(name, arguments, options, content, lineno, content_offset, block_text, state, state_machine)[source]

Bases: ClickDirective

Directive to declare a Click CLI source code.

This directive is used to declare a Click CLI example in the documentation. It renders the source code of the example in a Python code block.

default_language: str = 'python'

Default highlighting language to use to render the code block.

All Pygments’ languages short names are recognized.

show_source_by_default: bool = True

Whether to render the source code of the example in the code block.

show_results_by_default: bool = False

Whether to render the results of the example in the code block.

runner_method: str = 'execute_source'

The name of the method to call on the ClickRunner instance.

class click_extra.sphinx.click.RunDirective(name, arguments, options, content, lineno, content_offset, block_text, state, state_machine)[source]

Bases: ClickDirective

Directive to run a Click CLI example.

This directive is used to run a Click CLI example in the documentation. It renders the results of running the example in a shell session code block supporting ANSI colors.

default_language: str = 'ansi-shell-session'

Default highlighting language to use to render the code block.

All Pygments’ languages short names are recognized.

show_source_by_default: bool = False

Whether to render the source code of the example in the code block.

show_results_by_default: bool = True

Whether to render the results of the example in the code block.

runner_method: str = 'run_cli'

The name of the method to call on the ClickRunner instance.

option_spec: ClassVar[OptionSpec] = {'caption': <function unchanged_required>, 'class': <function class_option>, 'dedent': <function optional_int>, 'emphasize-lines': <function unchanged_required>, 'emphasize-result-lines': <function unchanged_required>, 'force': <function flag>, 'hide-prompt': <function flag>, 'hide-results': <function flag>, 'hide-source': <function flag>, 'language': <function unchanged_required>, 'lineno-start': <class 'int'>, 'linenos': <function flag>, 'mirror': <function flag>, 'name': <function unchanged>, 'screenshot': <function unchanged_required>, 'screenshot-backdrop': <function unchanged_required>, 'screenshot-background': <function _screenshot_background>, 'screenshot-border': <function unchanged_required>, 'screenshot-border-width': <function nonnegative_int>, 'screenshot-columns': <function _screenshot_columns>, 'screenshot-line-numbers': <function flag>, 'screenshot-margin': <function nonnegative_int>, 'screenshot-opacity': <function _screenshot_opacity>, 'screenshot-padding': <function nonnegative_int>, 'screenshot-preset': <function _screenshot_preset>, 'screenshot-radius': <function nonnegative_int>, 'screenshot-shadow': <function unchanged_required>, 'screenshot-title': <function unchanged_required>, 'screenshot-watermark': <function unchanged_required>, 'screenshot-watermark-color': <function unchanged_required>, 'show-prompt': <function flag>, 'show-results': <function flag>, 'show-source': <function flag>}

Adds the two options turning a run into a committed image.

The pair is deliberately independent. :screenshot: <name> only writes <name>.svg under the click_extra_screenshot_dir, leaving the page’s results code block alone: inside Sphinx that block beats an image, being selectable, searchable and theme-aware. :mirror: is what puts the image on the page, by keeping a Markdown link to it in the source .md between the same marker pair the python:render :mirror: flag uses, so the capture shows on GitHub and PyPI as well.

So :screenshot: alone maintains an asset some other surface embeds, and the two together also show it here. Both are refreshed offline by click-extra refresh-directives.

click_extra.sphinx.click.update_screenshot_blocks(paths, *, check=False, directory='assets')[source]

Refresh every click:run :mirror: region in the given sources.

See click_extra.blocks.update_blocks() for the walk, write, and check-mode contract. Unlike the python:render :mirror: refresher, this executes nothing: a region’s content is derived from the block’s :screenshot: name.

Parameters:
  • paths (Iterable[Path]) – Markdown files, or directories recursed for *.md.

  • check (bool) – report what would change without writing.

  • directory (str) – where the captures live, relative to each document.

Return type:

list[Path]

Returns:

the files whose regions were (or, under check, would be) updated.

class click_extra.sphinx.click.TreeDirective(name, arguments, options, content, lineno, content_offset, block_text, state, state_machine)[source]

Bases: ClickDirective

Render a complete CLI reference for a Click command and all its subcommands.

Walks the Click command tree at build time and emits, in MyST syntax:

  • A GFM summary table linking each command to its section anchor.

  • A heading + click:run --help block for the root command.

  • One heading + click:run --help block per subcommand, nested by depth.

Designed to replace per-project hand-rolled generators (like repomatic’s docs_update.py::cli_reference()) with a single declarative directive that walks the live command tree on every build.

The required argument is a Python expression evaluated in the per-document runner namespace; it must yield a click.Command. The optional directive body is Python preamble exec’d in the same namespace before evaluation, so authors may either import the CLI in a prior ``click:source :hide-source:` block or inline the import here.

Note

Currently MyST-only. Use the directive in a .md` document with myst_parser enabled.

has_content = True

May the directive have content?

required_arguments = 1

Number of required directive arguments.

optional_arguments = 0

The optional argument overrides the default Pygments language to use.

final_argument_whitespace = False

May the final argument contain whitespace?

option_spec: ClassVar[OptionSpec] = {'anchor-prefix': <function unchanged>, 'heading-offset': <function nonnegative_int>, 'label-prefix': <function unchanged>, 'max-depth': <function positive_int>, 'no-root': <function flag>, 'no-table': <function flag>, 'root-label': <function unchanged>}

Recognized directive options.

max-depth caps the recursion into nested click.Group commands (default: 10). heading-offset shifts all generated headings down by N levels. When unset, the directive reads state.memo.section_level and uses the surrounding section depth so the root nests one level below the enclosing section: inside the document’s h1 title this yields 1 (root at h2); inside an h3 section it yields 3 (root at h4). Override only when the auto-detected depth is wrong for the page layout. anchor-prefix and label-prefix override the slug and display prefix used for anchors and labels; both default to the CLI’s click.Command.name. root-label sets the heading text for the root help block (default: "Help screen"). no-table skips the summary table; no-root skips the root --help block.

run()[source]

Execute the directive and render its source and results.

Return type:

list[Node]

class click_extra.sphinx.click.ConfigDirective(name, arguments, options, content, lineno, content_offset, block_text, state, state_machine)[source]

Bases: ClickDirective

Render the configuration reference of a CLI’s config_schema.

Introspects a configuration schema dataclass at build time and expands, in MyST syntax:

  • A GFM summary table linking each option to its section anchor, with its one-line summary and default value.

  • One heading per option, with its docstring, type, default, and a TOML example pinned to the default value.

Option metadata comes from schema_field_infos(): dotted kebab-case keys, type annotations, defaults from a pristine schema instance, and attribute docstrings (which are parsed as the host document’s markup). Designed to replace per-project hand-rolled generators (like repomatic’s docs_update.py::config_deflist()) with a single declarative directive that documents the live schema on every build.

The required argument is a Python expression evaluated in the per-document runner namespace; it must yield either a click.Command whose config_schema is set (the schema is pulled off its ConfigOption), or a schema dataclass directly. The optional directive body is Python preamble exec’d in the same namespace before evaluation, so authors may either import the CLI in a prior click:source :hide-source: block or inline the import here.

Caution

Attribute docstrings are recovered from the schema’s source file, so a schema defined inside an exec’d click:source block documents its options without descriptions. Import the schema from a real module instead (see field_docstrings()).

Note

Currently MyST-only. Use the directive in a .md document with myst_parser enabled.

has_content = True

May the directive have content?

required_arguments = 1

Number of required directive arguments.

optional_arguments = 0

The optional argument overrides the default Pygments language to use.

final_argument_whitespace = False

May the final argument contain whitespace?

option_spec: ClassVar[OptionSpec] = {'heading-offset': <function nonnegative_int>, 'no-examples': <function flag>, 'no-table': <function flag>, 'section': <function unchanged>}

Recognized directive options.

heading-offset shifts all generated headings down by N levels; when unset, the surrounding section depth is used (same behavior as click:tree). section overrides the TOML table header shown in the per-option examples: it defaults to tool.{cli-name} when the argument is a CLI (matching how click-extra and its downstream CLIs read their section from pyproject.toml), and to no header at all for a bare schema; an explicitly empty :section: suppresses the header. no-table skips the summary table; no-examples skips the TOML example blocks.

run()[source]

Execute the directive and render its source and results.

Return type:

list[Node]

class click_extra.sphinx.click.ClickDomain(env)[source]

Bases: StatelessDomain

Setup new directives under the same click namespace:

  • click:source which renders a Click CLI source code

  • click:run which renders the results of running a Click CLI

  • click:tree which walks a Click command tree and renders the full --help reference for every subcommand, with a summary table on top

  • click:config which documents a CLI’s config_schema: a summary table plus one section per option, with types, defaults, and TOML examples

name: ClassVar[str] = 'click'

domain name: should be short, but unique

label: ClassVar[str] = 'Click'

domain label: longer, more descriptive (used in messages)

directives: ClassVar[dict] = {'config': <class 'click_extra.sphinx.click.ConfigDirective'>, 'run': <class 'click_extra.sphinx.click.RunDirective'>, 'source': <class 'click_extra.sphinx.click.SourceDirective'>, 'tree': <class 'click_extra.sphinx.click.TreeDirective'>}

directive name -> directive class

click_extra.sphinx.click.cleanup_runner(app, doctree)

Drop the ClickRunner from the doctree once the document is read.

Return type:

None

click_extra.sphinx.manpages module

Sphinx integration to render roff man pages alongside the HTML build.

A project that adds click_extra.sphinx to its extensions list and declares one or more entries in click_extra_manpages gets its Click command tree(s) emitted as .1 files into <outdir>/<output_dir>/ on every HTML build, with no project-local helper script. Pages mirror what click_extra.command_doc.write_manpages() produces from a CLI invocation, so the docs site, the release pipeline, and downstream packagers all share one generator.

When mandoc or groff is available on PATH, each .1 file is also rendered to a browser-viewable .html sibling. Browsers download raw .1 files rather than display them, so the HTML pass is what makes Sphinx’s :manpage: role useful when manpages_url points at this hook’s output.

The hook only fires for HTML-class builders (html, dirhtml, singlehtml). Non-HTML builders (linkcheck, man, epub, coverage, etc.) skip it: they typically have different output semantics, and writing roff into a linkcheck output/ directory serves no purpose.

Configuration shape:

click_extra_manpages = [
    {
        "script": "meta_package_manager.cli:mpm",  # required
        "prog_name": "mpm",  # optional, see below
        "output_dir": "man",  # optional, defaults to "man"
        "render_html": True,  # optional, see below
    },
]
  • script is resolved by click_extra.cli_wrapper.resolve_target_command() exactly as it would be from the click-extra man CLI: a console_scripts entry-point name, a module:function path, a .py file, or a plain module name.

  • prog_name is the basename used for both the man-page .TH header and the generated filenames. When omitted, it falls back to the resolved Click command’s own name attribute (mpm for the meta_package_manager.cli:mpm target), matching the default the click-extra man --output-dir CLI uses.

  • output_dir is a relative path under app.outdir. It is created on demand and reused across builds.

  • render_html toggles the HTML sibling pass. Defaults to True. When no renderer is on PATH, the build still produces the .1 files and logs a single info-level notice; set render_html to False to suppress that notice.

An empty (or absent) click_extra_manpages list disables the feature, which is the default for every project pulling in the extension.

Cross-referencing the generated pages from prose

To make :manpage:`myprog(1)` resolve to the HTML sibling this hook emits, set Sphinx’s manpages_url to the same output_dir:

manpages_url = "man/{page}.{section}.html"

Sphinx’s role provides {page}, {section} and {path} placeholders; the file layout produced here is {page}.{section} plus the optional .html extension, so the template above matches every file regardless of how deep the subcommand tree goes.

click_extra.sphinx.manpages.MANPAGES_CONFIG_KEY = 'click_extra_manpages'

Name of the conf.py config flag holding the man-page emit list.

click_extra.sphinx.manpages.DEFAULT_OUTPUT_DIR = 'man'

Subdirectory under app.outdir where .1 files land when the caller omits the output_dir entry. Picked to match the URL fragment projects typically publish their man pages under (like https://example.com/<project>/man/<cli>.1).

click_extra.sphinx.manpages.HTML_BUILDER_NAMES = frozenset({'dirhtml', 'html', 'singlehtml'})

Builder names that get the man-page emit hook.

Restricted to HTML-family builders because they are the ones whose output directory becomes the published docs site. Other builders (linkcheck, man, epub, coverage) have different output semantics, and writing roff into their build trees would either be redundant or confusing.

click_extra.sphinx.manpages.HTML_RENDERERS: tuple[tuple[str, tuple[str, ...]], ...] = (('mandoc', ('-Thtml',)), ('groff', ('-Thtml', '-mandoc')))

External roff → HTML renderers, tried in order.

mandoc is preferred: its HTML output ships semantic id anchors on every section and option (#NAME, #SYNOPSIS, #config…), which makes deep-linking from prose work. groff -Thtml -mandoc is the GNU fallback. If neither is on PATH, the HTML pass is skipped and only the .1 files are emitted.

click_extra.sphinx.manpages.MANPAGE_LIST_DIRECTIVE = 'click-extra-manpages'

Name of the directive that renders an auto-generated index of every man page declared in MANPAGES_CONFIG_KEY. The hyphenated form mirrors the click_extra_manpages config key it surfaces.

class click_extra.sphinx.manpages.ManpageListDirective(name, arguments, options, content, lineno, content_offset, block_text, state, state_machine)[source]

Bases: SphinxDirective

Render a bullet list with one link per emitted man page.

The directive walks every entry in MANPAGES_CONFIG_KEY and, for each, calls iter_command_contexts() to discover the (sub)command tree. Each list item links to the corresponding .1.html file written by the emit hook.

Link targets are stamped as build-root-relative URIs and rebased against the enclosing page by _rebase_manpage_links, so the list works whether it appears at the docs root or in a nested page. The directive takes no arguments and no content: it surfaces whatever the config declares at the time the doc is built.

has_content = False

May the directive have content?

required_arguments = 0

Number of required directive arguments.

optional_arguments = 0

Number of optional arguments after the required arguments.

run()[source]
Return type:

list[Node]

click_extra.sphinx.manpages.setup(app)[source]

Register the man-page hooks and the index directive on app.

Called from click_extra.sphinx.setup() so projects only need to list "click_extra.sphinx" in their extensions. The hooks are a no-op when click_extra_manpages is unset or empty, and the directive renders nothing in that case.

Return type:

None

click_extra.sphinx.matrix module

Release compatibility matrices derived from a project’s git history.

Walk every vX.Y.Z tag in a project’s git repository, extract each release’s declared support for some axis, group consecutive tags that agree, and render a GitHub-flavored markdown matrix suitable for a project’s install.md.

Two axes are supported:

  • Python ({matrix} python): per-tag support comes from the Programming Language :: Python :: X.Y classifier list in pyproject.toml (the explicit tested grid), falling back in priority order to PEP 621 requires-python, Poetry [tool.poetry.dependencies].python, and setup.py’s python_requires. A floor-only declaration is capped at the latest Python released while the range was current, so the set does not over-claim support for Pythons that did not yet exist. Cells are three-valued: the classifier list drives , requires-python drives , and a version neither attested nor ruled out renders as (see UNDECLARED_CELL).

  • A dependency ({matrix} <distribution>, like {matrix} click): the per-tag constraint is that distribution’s requirement specifier; columns are auto-derived from the specifier boundaries plus the uv.lock resolved version, and each ✅ / ❌ cell is computed with packaging.

The rendered tables back the always-on matrix Sphinx directive (see MatrixDirective), so a project’s install.md can embed a live matrix kept current by the click-extra refresh-directives command instead of a static table maintained by hand. The generation functions shell out to git; they carry no runtime CLI relevance and are kept out of the click_extra public API.

click_extra.sphinx.matrix.PYTHON_RELEASE_DATES: dict[str, str] = {'2.7': '2010-07-03', '3.0': '2008-12-03', '3.1': '2009-06-27', '3.10': '2021-10-04', '3.11': '2022-10-24', '3.12': '2023-10-02', '3.13': '2024-10-07', '3.14': '2025-10-07', '3.2': '2011-02-20', '3.3': '2012-09-29', '3.4': '2014-03-16', '3.5': '2015-09-13', '3.6': '2016-12-23', '3.7': '2018-06-27', '3.8': '2019-10-14', '3.9': '2020-10-05'}

ISO release date of each X.Y Python final release.

Used to cap cells when a release range’s Python support is derived from a requires-python-style floor rather than an explicit classifier list: a floor without upper bound would otherwise over-claim support for Pythons that did not yet exist while the range was current. Update this table each October when a new final release ships.

click_extra.sphinx.matrix.SUPPORTED_CELL: str = '✅'

Cell marking a version the release declares support for.

click_extra.sphinx.matrix.FORBIDDEN_CELL: str = '❌'

Cell marking a version the release’s own metadata rules out.

Reserved for a hard incompatibility: a version an installer refuses outright because it falls below the declared floor, on or above the declared ceiling, or inside an exclusion clause.

click_extra.sphinx.matrix.UNDECLARED_CELL: str = '–'

Cell marking a version the release neither supports nor forbids.

Covers the two flavours of absence of evidence: a Python that had not been released yet when the range shipped, and one that was released but never added to the classifier list. Neither is a statement of incompatibility, so spelling them FORBIDDEN_CELL would over-claim.

Note

Only the python axis produces this cell. A dependency’s requirement specifier is the whole truth about that dependency, with no second, informational source to disagree with it, so its cells stay binary.

click_extra.sphinx.matrix.DEFAULT_TAG_PATTERN: str = '^v\\d+\\.\\d+\\.\\d+$'

Default regex for release tags (vMAJOR.MINOR.PATCH).

click_extra.sphinx.matrix.DEFAULT_TAGS_SORT: str = 'version:refname'

Default git tag --sort argument.

click_extra.sphinx.matrix.NEWEST_FIRST: str = 'newest-first'

Ordering value: highest version / most recent release first.

click_extra.sphinx.matrix.OLDEST_FIRST: str = 'oldest-first'

Ordering value: lowest version / oldest release first.

click_extra.sphinx.matrix.ORDER_CHOICES: tuple[str, ...] = ('newest-first', 'oldest-first')

Allowed values of the column-order and row-order matrix options.

class click_extra.sphinx.matrix.PythonMatrixGroup(first_tag: str, last_tag: str, first_date: str, python_versions: tuple[str, ...], spec: str = '')[source]

Bases: NamedTuple

A contiguous run of release tags sharing the same Python support set.

Create new instance of PythonMatrixGroup(first_tag, last_tag, first_date, python_versions, spec)

first_tag: str

First tag in the group (in git tag --sort=version:refname order).

last_tag: str

Last tag in the group.

first_date: str

ISO YYYY-MM-DD date of the first tag’s commit.

python_versions: tuple[str, ...]

The X.Y Python versions this range supports, sorted ascending.

spec: str

The range’s declared requires-python, raw and unparsed.

Kept alongside the supported set because the two answer different questions: the set is what the release claims (its classifier list), while the spec is what an installer enforces. A version outside the spec is a hard incompatibility, a version merely missing from the set is an undeclared one. See UNDECLARED_CELL.

click_extra.sphinx.matrix.python_versions_released_by(cutoff_date, release_dates=None)[source]

Return Python X.Y versions released on or before cutoff_date.

Parameters:
Return type:

list[str]

Returns:

sorted list of X.Y strings.

click_extra.sphinx.matrix.parse_python_spec(spec)[source]

Parse a Python version spec into (floor, ceiling, excluded).

Supports:

  • PEP 440: >=3.10, >=3.10,<3.14, >=2.7, !=3.0.*, !=3.1.*.

  • Poetry caret: ^3.7 expands to >=3.7, <4.0.

  • Poetry tilde: ~3.7 expands to >=3.7, <3.8.

  • setup.py’s python_requires (uses the same PEP 440 syntax as requires-python).

Return type:

tuple[str, str, set[str]]

Returns:

(floor, ceiling, excluded) where each of floor and ceiling is a bare "X.Y" string (empty if unspecified) and excluded is the set of bare "X.Y" values one per !=X.Y.* clause. The ceiling is exclusive: <3.14 means 3.14 itself is not supported. Returns ("", "", set()) when no floor is found.

click_extra.sphinx.matrix.python_matrix_groups(project_root, *, tag_pattern='^v\\\\d+\\\\.\\\\d+\\\\.\\\\d+$', tags_sort='version:refname', version_floor='', release_dates=None)[source]

Walk every release tag in project_root and group consecutive releases that declare the same effective set of Python versions.

Parameters:
  • project_root (Path) – git working tree to walk.

  • tag_pattern (str) – regex matching release tags. Defaults to DEFAULT_TAG_PATTERN (vMAJOR.MINOR.PATCH).

  • tags_sort (str) – value passed to git tag --sort. Defaults to DEFAULT_TAGS_SORT (version:refname).

  • version_floor (str) – drop every release tag below this bare version ("4.9.0", "4.9"). Empty (the default) keeps all tags. Applied before grouping so the oldest surviving group starts at the floor.

  • release_dates (dict[str, str] | None) – Python-version release-date table used for the cap. Defaults to PYTHON_RELEASE_DATES.

Return type:

list[PythonMatrixGroup]

Returns:

List of PythonMatrixGroup, in chronological order. Tags with no Python declaration in any recognized form are skipped.

click_extra.sphinx.matrix.python_matrix_table(project_root, label, *, tag_pattern='^v\\\\d+\\\\.\\\\d+\\\\.\\\\d+$', tags_sort='version:refname', python_floor='', version_floor='', column_order='newest-first', row_order='newest-first', release_dates=None)[source]

Render the Python compatibility matrix as a GitHub-flavored markdown table.

By default newest releases sit on top and newest Python versions on the left, so the most recent compatibility information always sits in the upper-left corner of the table; row_order and column_order flip either axis.

Parameters:
  • project_root (Path) – git working tree to walk.

  • label (str) – the header column name (usually the package name, like "click-extra" or "repomatic"). Rendered in backticks.

  • tag_pattern (str) – passed to python_matrix_groups().

  • tags_sort (str) – passed to python_matrix_groups().

  • python_floor (str) – drop every Python X.Y column below this bare version ("3.9"). Empty (the default) keeps all columns. Trims columns only; combine with version_floor to also drop the old release rows that supported nothing above the floor.

  • version_floor (str) – passed to python_matrix_groups() to drop release rows below a bare package version.

  • column_order (str) – left-to-right ordering of the Python columns: NEWEST_FIRST (default) or OLDEST_FIRST.

  • row_order (str) – top-to-bottom ordering of the release rows: NEWEST_FIRST (default) or OLDEST_FIRST.

  • release_dates (dict[str, str] | None) – passed to python_matrix_groups().

Return type:

str

Returns:

rendered markdown table, or the empty string when no group was collected.

Raises:

ValueError – on an unrecognized column_order or row_order.

class click_extra.sphinx.matrix.DependencyMatrixGroup(first_tag: str, last_tag: str, first_date: str, spec: str)[source]

Bases: NamedTuple

A contiguous run of release tags declaring the same dependency spec.

Create new instance of DependencyMatrixGroup(first_tag, last_tag, first_date, spec)

first_tag: str

First tag in the group (in git tag --sort=version:refname order).

last_tag: str

Last tag in the group.

first_date: str

ISO YYYY-MM-DD date of the first tag’s commit.

spec: str

The raw requirement specifier declared for the dependency at this range.

click_extra.sphinx.matrix.POETRY_CARET_RE = re.compile('^\\^\\s*(\\d+)(?:\\.(\\d+))?(?:\\.(\\d+))?$')

Poetry’s caret range.

click_extra.sphinx.matrix.POETRY_TILDE_RE = re.compile('^~\\s*(\\d+)(?:\\.(\\d+))?(?:\\.(\\d+))?$')

Poetry’s tilde range.

Deliberately also matches a bare ~X. It cannot swallow PEP 440’s ~=, whose = is not a digit.

click_extra.sphinx.matrix.POETRY_WILDCARD_RE = re.compile('^(?:\\*|(\\d+)(?:\\.(\\d+))?\\.\\*)$')

Poetry’s wildcard range.

A bare * allows any version at all. X.* and X.Y.* pin their series, without PEP 440’s leading ==.

click_extra.sphinx.matrix.dependency_matrix_groups(project_root, dep_name, *, tag_pattern='^v\\\\d+\\\\.\\\\d+\\\\.\\\\d+$', tags_sort='version:refname', version_floor='')[source]

Group consecutive release tags declaring the same dep_name spec.

Parameters:

dep_name (str) – the distribution whose requirement specifier is tracked (like "click").

Return type:

list[DependencyMatrixGroup]

Returns:

DependencyMatrixGroup list in chronological order; tags with no declared requirement for dep_name are skipped.

click_extra.sphinx.matrix.dependency_matrix_table(project_root, label, dep_name, *, show_spec=False, tag_pattern='^v\\\\d+\\\\.\\\\d+\\\\.\\\\d+$', tags_sort='version:refname', version_floor='', column_order='newest-first', row_order='newest-first')[source]

Render the dep_name compatibility matrix as a markdown table.

Columns are auto-derived from the requirement specifiers across history (see _dependency_columns()) plus the uv.lock resolved version; each ✅ / ❌ cell is computed with packaging. Consecutive ranges whose cells coincide are re-merged into one row. By default newest releases sit on top and newest dependency versions on the left, matching the Python axis; row_order and column_order flip either axis.

Parameters:
  • label (str) – header column name (the documented package, in backticks).

  • dep_name (str) – the tracked distribution ("click").

  • show_spec (bool) – add a Spec column with each range’s raw specifier.

  • column_order (str) – left-to-right ordering of the version columns: NEWEST_FIRST (default) or OLDEST_FIRST.

  • row_order (str) – top-to-bottom ordering of the release rows: NEWEST_FIRST (default) or OLDEST_FIRST.

Return type:

str

Returns:

rendered markdown table, or "" when nothing was collected.

Raises:

ValueError – on an unrecognized column_order or row_order.

class click_extra.sphinx.matrix.MatrixDirective(name, arguments, options, content, lineno, content_offset, block_text, state, state_machine)[source]

Bases: SphinxDirective

Render a package’s compatibility matrix for a given axis.

{matrix} python renders the interpreter matrix (release ranges × Python versions). {matrix} <distribution> (like {matrix} click) renders a dependency matrix (release ranges × that dependency’s versions, from its requirement specifier across the git history). Both emit a GitHub-flavored table parsed by the host document’s parser, so it lands as a real <table>.

The table normally lives inside the block as its content, kept current by the offline updater (update_matrix_blocks(), exposed as the click-extra refresh-directives command). Rendering that embedded copy needs no git access at build time, so shallow clones and read-only build hosts still show the matrix. An empty block falls back to generating from the working tree’s git tags, so a freshly authored block renders before its first refresh.

Argument: the axis, python or a distribution name.

Options:

  • :package: — header column label. Defaults to the repository name.

  • :path: — git working tree to walk, absolute or relative to the documented project’s git root. Defaults to that git root.

  • :version-floor: — drop release rows below this package version.

  • :tag-pattern: — regex selecting release tags. Defaults to DEFAULT_TAG_PATTERN.

  • :column-order: — left-to-right ordering of the version columns: newest-first (default) or oldest-first.

  • :row-order: — top-to-bottom ordering of the release rows: newest-first (default) or oldest-first.

  • :python-floor: — (python axis) drop Python columns below X.Y.

  • :show-spec: — (dependency axis) add a raw-specifier Spec column.

The git fallback is resilient: a missing git binary, a non-repository path, or a tag-less repository logs a build warning and renders nothing rather than aborting the build.

has_content = True

May the directive have content?

required_arguments = 1

Number of required directive arguments.

optional_arguments = 0

Number of optional arguments after the required arguments.

option_spec: ClassVar[OptionSpec] = {'column-order': <function _order_option>, 'package': <function unchanged>, 'path': <function unchanged>, 'python-floor': <function unchanged>, 'row-order': <function _order_option>, 'show-spec': <function flag>, 'tag-pattern': <function unchanged>, 'version-floor': <function unchanged>}

Mapping of option names to validator functions.

run()[source]
Return type:

list[Node]

click_extra.sphinx.matrix.setup(app)[source]

Register the always-on matrix directive on app.

Called from click_extra.sphinx.setup() so projects only need to list "click_extra.sphinx" in their extensions. Unlike the click:* / python:* families, the directive is registered unconditionally: it runs a canned matrix generator, not user-supplied Python, so it needs no opt-in.

Return type:

None

click_extra.sphinx.matrix.update_matrix_blocks(paths, *, check=False)[source]

Refresh every {matrix} block in the given Markdown sources.

See click_extra.blocks.update_blocks() for the walk, write, and check-mode contract.

Return type:

list[Path]

Returns:

the files whose {matrix} blocks were (or, under check, would be) updated.

click_extra.sphinx.myst_docstrings module

Convert MyST-flavored docstrings to reST for sphinx.ext.autodoc.

Lightweight replacement for sphinx-autodoc2, which provided native MyST docstring parsing but is abandoned (last release 0.5.0, November 2023; incompatible with current Sphinx and docutils).

Hooks into autodoc-process-docstring to transparently convert MyST markdown syntax in Python docstrings to reStructuredText before Sphinx processes them. Preserves full compatibility with sphinx_autodoc_typehints, autodoc_default_options, and every other extension that builds on sphinx.ext.autodoc. See MyST docstrings for setup, usage, and limitations.

The conversion is idempotent: docstrings already in reST pass through unchanged. This allows incremental migration one module at a time.

Supported conversions:

Inline code (single backtick) is converted to reST double backticks. Field list markers (:param:, :return:) need no conversion; the content inside field list entries is converted normally (inline code, cross-references, links).

``{note} Register this extension in your Sphinx ``conf.py, before sphinx_autodoc_typehints if present:

extensions = [
    "sphinx.ext.autodoc",
    "click_extra.sphinx.myst_docstrings",
    "sphinx_autodoc_typehints",  # must come after
]

This requires click-extra[sphinx] in your docs dependency group.

click_extra.sphinx.myst_docstrings.myst_to_rst(lines)[source]

Convert MyST syntax to reST, modifying lines in place.

The conversion is idempotent: reST-only docstrings pass through unchanged because none of the patterns match reST syntax.

Return type:

None

click_extra.sphinx.myst_docstrings.setup(app)[source]

Sphinx extension entry point.

Raises:

ExtensionError – If sphinx_autodoc_typehints is already loaded.

click_extra.sphinx.todos module

Collapse the repeated entries sphinx.ext.todo accumulates on a todolist.

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 the same docstring several times:

  • A full-API page plus per-feature pages. A project documenting every module on one page, then documenting the same modules again next to the prose that explains them, renders each docstring twice. :no-index: on the second block does not help: it suppresses the cross-reference target and the search-index entry, leaving the docstring (and its todo node) rendered in full.

  • A package re-exporting its members. automodule documents the imported names a package lists in __all__, so a symbol appears once under the package and once under the module that defines it. Both renderings can even land on the same page.

The two multiply. On click-extra’s own documentation the untreated list showed 35 entries for 17 distinct :todo: directives, one of them repeated four times.

Nothing upstream deduplicates: {class}``sphinx.ext.todo.TodoListProcessor`` flattens the whole todo domain into the page in read order. This module removes the surplus nodes from that domain just before the processor reads it, so the rendered list carries one entry per directive.

Todo

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

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

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

click_extra.sphinx.todos.AUTODOC_DOCSTRING_MARKER = ':docstring of '

Separator autodoc puts between a source file and the object it documented.

A node produced from a docstring carries a synthetic source of the form {file}:docstring of {dotted.path}, where :file: is the file the reader reached the object through, not necessarily the one defining it. Splitting on this marker is what lets {func}``todo_identity`` recognize two renderings of a single docstring reached through two import paths.

click_extra.sphinx.todos.DEDUPE_TODOS_CONFIG = 'click_extra_dedupe_todos'

Name of the conf.py value gating the deduplication.

True by default: a todolist page listing the same item three times is a defect in every project I know of, and a project that has not enabled sphinx.ext.todo never notices the hook either way. Set it to False to get Sphinx’s raw output back, one entry per rendering.

click_extra.sphinx.todos.todo_identity(node)[source]

Identify the {todo} directive a node was rendered from.

Two nodes share an identity when they come from the same line of the same docstring or document, whatever page rendered them and whichever import path autodoc reached the object through. The file prefix is dropped from a docstring source precisely so a re-exported symbol matches the module that defines it.

Return type:

tuple[str, int | None]

click_extra.sphinx.todos.is_reexport(node)[source]

Tell whether a node was rendered through a package’s __init__.

Used to rank competing renderings: the entry surviving deduplication keeps its backlink and its “located in …” attribution, so a rendering reached through the defining module is worth more to a reader than the same docstring reached through a re-exporting package.

Return type:

bool

click_extra.sphinx.todos.deduplicate_todos(app, doctree, docname)[source]

Drop every todo node duplicating one already held by the domain.

Connected to doctree-resolved below the priority sphinx.ext.todo.TodoListProcessor runs at, and a no-op on any document holding no todolist, so the work happens once on the page that consumes the domain.

Among the renderings of one directive, the surviving node is the first in (rendered through a defining module, document name, position in the document) order. That ordering is total and reads the same on a clean build and an incremental one, so the backlink a reader follows does not move around between builds.

Note

The todo domain is mutated in place. That is safe because Sphinx pickles the environment at the end of the reading phase, before the writing phase this hook belongs to: the removals reach the page being written and never the cached environment, so an incremental rebuild still starts from the full set.

Return type:

None

click_extra.sphinx.todos.setup(app)[source]

Register the deduplication hook on app.

Called from click_extra.sphinx.setup() so projects only need to list "click_extra.sphinx" in their extensions. Priority 400 places the hook below the default 500 sphinx.ext.todo.TodoListProcessor is connected at, which is what makes the domain already trimmed by the time the list is rendered.

Return type:

None