Skip to content

Gallery¤

Welcome to our gallery of examples!

Diagrams (cloud/system architecture)¤

Diagrams offers a nice way of building diagrams. It also bundles a number of images used to illustrate objects and concepts so you can build good-looking diagrams. By default, Diagrams tries to write the result on disk, so we prevent that by patching its render method, and by ignoring the FileNotFoundError that ensues. Then we use its internal dot object and its pipe method to store the diagram in a variable, as base64 encoded PNG data. Finally we output an HTML image with the base64 data. Using SVG is not possible here since Diagrams embeds actual, smaller PNG files in the result, files which are not automatically added to the final site.

Diagrams
```python exec="true" html="true"
from base64 import b64encode
from contextlib import suppress

from diagrams import Diagram
from diagrams.k8s.clusterconfig import HPA
from diagrams.k8s.compute import Deployment, Pod, ReplicaSet
from diagrams.k8s.network import Ingress, Service

with suppress(FileNotFoundError):
    with Diagram("Exposed Pod with 3 Replicas", show=False) as diagram:
        diagram.render = lambda: None
        net = Ingress("domain.com") >> Service("svc")
        net >> [Pod("pod1"), Pod("pod2"), Pod("pod3")] << ReplicaSet("rs") << Deployment("dp") << HPA("hpa")
        png = b64encode(diagram.dot.pipe(format="png")).decode()

print(f'<img src="data:image/png;base64, {png}"/>')
```

Python dependency tree¤

pipdeptree is able to output a Mermaid diagram of your Python dependency tree. In this example we change the direction of the graph from top-down to left-right, and remove local version identifiers from our own package.

flowchart TD
    classDef missing stroke-dasharray: 5
    editables["editables\n0.5"]
    markdown-exec["markdown-exec\n1.7.0.1.0.2"]
    markdown["Markdown\n3.5.2"]
    pymdown-extensions["pymdown-extensions\n10.7.1"]
    pyyaml["PyYAML\n6.0.1"]
    markdown-exec -- ">=9" --> pymdown-extensions
    markdown-exec -- "any" --> editables
    pymdown-extensions -- ">=3.5" --> markdown
    pymdown-extensions -- "any" --> pyyaml

pipdeptree mermaid diagram
```bash exec="1" result="mermaid"
pipdeptree -p markdown-exec --mermaid 2>/dev/null |
    sed -E 's/\.dev.+"\]$/"]/;s/\+d.*"\]$/"]/'
```

Another example with more dependencies:

flowchart LR
    classDef missing stroke-dasharray: 5
    click_0["click\n8.1.7"]
    colorama["colorama\n0.4.6"]
    ghp-import["ghp-import\n2.1.0"]
    griffe["griffe\n0.42.0.1.2.0"]
    jinja2["Jinja2\n3.1.3"]
    markdown["Markdown\n3.5.2"]
    markupsafe["MarkupSafe\n2.1.5"]
    mergedeep["mergedeep\n1.3.4"]
    mkdocs-autorefs["mkdocs-autorefs\n1.0.1"]
    mkdocs["mkdocs\n1.5.3"]
    mkdocstrings-python["mkdocstrings-python\n1.9.0.1.6.0"]
    mkdocstrings["mkdocstrings\n0.24.1"]
    packaging["packaging\n24.0"]
    pathspec["pathspec\n0.12.1"]
    platformdirs["platformdirs\n4.2.0"]
    pymdown-extensions["pymdown-extensions\n10.7.1"]
    python-dateutil["python-dateutil\n2.9.0.post0"]
    pyyaml-env-tag["pyyaml_env_tag\n0.1"]
    pyyaml["PyYAML\n6.0.1"]
    six["six\n1.16.0"]
    watchdog["watchdog\n4.0.0"]
    ghp-import -- ">=2.8.1" --> python-dateutil
    griffe -- ">=0.4" --> colorama
    jinja2 -- ">=2.0" --> markupsafe
    mkdocs -- ">=0.1" --> pyyaml-env-tag
    mkdocs -- ">=0.11.1" --> pathspec
    mkdocs -- ">=1.0" --> ghp-import
    mkdocs -- ">=1.3.4" --> mergedeep
    mkdocs -- ">=2.0" --> watchdog
    mkdocs -- ">=2.0.1" --> markupsafe
    mkdocs -- ">=2.11.1" --> jinja2
    mkdocs -- ">=2.2.0" --> platformdirs
    mkdocs -- ">=20.5" --> packaging
    mkdocs -- ">=3.2.1" --> markdown
    mkdocs -- ">=5.1" --> pyyaml
    mkdocs -- ">=7.0" --> click_0
    mkdocs-autorefs -- ">=1.1" --> mkdocs
    mkdocs-autorefs -- ">=2.0.1" --> markupsafe
    mkdocs-autorefs -- ">=3.3" --> markdown
    mkdocstrings -- ">=0.3.1" --> mkdocs-autorefs
    mkdocstrings -- ">=1.1" --> markupsafe
    mkdocstrings -- ">=1.4" --> mkdocs
    mkdocstrings -- ">=2.11.1" --> jinja2
    mkdocstrings -- ">=2.2.0" --> platformdirs
    mkdocstrings -- ">=3.3" --> markdown
    mkdocstrings -- ">=6.3" --> pymdown-extensions
    mkdocstrings -- ">=7.0" --> click_0
    mkdocstrings-python -- ">=0.20" --> mkdocstrings
    mkdocstrings-python -- ">=0.40" --> griffe
    mkdocstrings-python -- ">=3.3,<3.6" --> markdown
    pymdown-extensions -- ">=3.5" --> markdown
    pymdown-extensions -- "any" --> pyyaml
    python-dateutil -- ">=1.5" --> six
    pyyaml-env-tag -- "any" --> pyyaml

pipdeptree mermaid diagram
```bash exec="1" result="mermaid"
pipdeptree -p mkdocstrings-python --mermaid 2>/dev/null |
    sed 's/flowchart TD/flowchart LR/'
```

Python modules inter-dependencies¤

This example uses pydeps to build a graph of interdependencies of your project's modules. Data is built and stored in a pydeps data structure, then translated to dot source, then rendered to SVG with Graphviz. In this example we also add links to the code reference in related nodes. Try clicking on the markdown_exec nodes!

Note

pydeps wasn't designed to be used in such a programatic way, so the code is a bit convoluted, but you could make a function of it, put it in an importable script/module, and reuse it cleanly in your executed code blocks.

Gjinja2jinja2markdown_exec.mkdocs_pluginmarkdown_exec.mkdocs_pluginjinja2->markdown_exec_mkdocs_pluginmkdocs_pluginsmkdocs.pluginsjinja2->mkdocs_pluginsmkdocs_structure_filesmkdocs.structure.filesjinja2->mkdocs_structure_filesmarkdownmarkdownmarkdown_execmarkdown_execmarkdown->markdown_execmarkdown_exec.formatters.basemarkdown_exec.formatters.basemarkdown->markdown_exec_formatters_basemarkdown_exec.formatters.pyodidemarkdown_exec.formatters.pyodidemarkdown->markdown_exec_formatters_pyodidemarkdown_exec.formatters.treemarkdown_exec.formatters.treemarkdown->markdown_exec_formatters_treemarkdown_exec.processorsmarkdown_exec.processorsmarkdown->markdown_exec_processorsmarkdown_exec.renderingmarkdown_exec.renderingmarkdown->markdown_exec_renderingmkdocs_config_config_optionsmkdocs.config.config_optionsmarkdown->mkdocs_config_config_optionsmarkdown_coremarkdown.coremarkdown_core->markdownmarkdown_core->markdown_exec_formatters_basemarkdown_treeprocessorsmarkdown.treeprocessorsmarkdown_treeprocessors->markdown_coremarkdown_treeprocessors->markdown_exec_processorsmarkdown_utilmarkdown.utilmarkdown_util->markdown_coremarkdown_util->markdown_treeprocessorsmarkdown_util->markdown_exec_processorsmarkdown_exec.formattersmarkdown_exec.formattersmarkdown_exec_formatters->markdown_execmarkdown_exec_formatters->markdown_exec_mkdocs_pluginmarkdown_exec_formatters_base->markdown_execmarkdown_exec.formatters.bashmarkdown_exec.formatters.bashmarkdown_exec_formatters_base->markdown_exec_formatters_bashmarkdown_exec.formatters.consolemarkdown_exec.formatters.consolemarkdown_exec_formatters_base->markdown_exec_formatters_consolemarkdown_exec.formatters.markdownmarkdown_exec.formatters.markdownmarkdown_exec_formatters_base->markdown_exec_formatters_markdownmarkdown_exec.formatters.pyconmarkdown_exec.formatters.pyconmarkdown_exec_formatters_base->markdown_exec_formatters_pyconmarkdown_exec.formatters.pythonmarkdown_exec.formatters.pythonmarkdown_exec_formatters_base->markdown_exec_formatters_pythonmarkdown_exec.formatters.shmarkdown_exec.formatters.shmarkdown_exec_formatters_base->markdown_exec_formatters_shmarkdown_exec_formatters_bash->markdown_execmarkdown_exec_formatters_console->markdown_execmarkdown_exec_formatters_markdown->markdown_execmarkdown_exec_formatters_pycon->markdown_execmarkdown_exec_formatters_pyodide->markdown_execmarkdown_exec_formatters_python->markdown_execmarkdown_exec_formatters_python->markdown_exec_formatters_pyconmarkdown_exec_formatters_sh->markdown_execmarkdown_exec_formatters_sh->markdown_exec_formatters_consolemarkdown_exec_formatters_tree->markdown_execmarkdown_exec.loggermarkdown_exec.loggermarkdown_exec_logger->markdown_exec_formatters_basemarkdown_exec_logger->markdown_exec_formatters_consolemarkdown_exec_logger->markdown_exec_formatters_pyconmarkdown_exec_logger->markdown_exec_mkdocs_pluginmarkdown_exec_processors->markdown_exec_renderingmarkdown_exec_rendering->markdown_exec_formatters_basemarkdown_exec_rendering->markdown_exec_formatters_bashmarkdown_exec_rendering->markdown_exec_formatters_pythonmarkdown_exec_rendering->markdown_exec_formatters_shmarkdown_exec_rendering->markdown_exec_formatters_treemarkdown_exec_rendering->markdown_exec_mkdocs_pluginmarkupsafemarkupsafemarkupsafe->markdown_exec_formatters_basemarkupsafe->markdown_exec_formatters_consolemarkupsafe->markdown_exec_formatters_pyconmarkupsafe->markdown_exec_processorsmarkupsafe->markdown_exec_renderingmkdocsmkdocsmkdocs->markdown_exec_mkdocs_pluginmkdocs_configmkdocs.configmkdocs_config->markdown_exec_mkdocs_pluginmkdocs_config->mkdocs_pluginsmkdocs_config->mkdocs_structure_filesmkdocs_config_basemkdocs.config.basemkdocs_config_base->markdown_exec_mkdocs_pluginmkdocs_config_base->mkdocs_configmkdocs_config_base->mkdocs_config_config_optionsmkdocs_config_defaultsmkdocs.config.defaultsmkdocs_config_base->mkdocs_pluginsmkdocs_config_config_options->markdown_exec_mkdocs_pluginmkdocs_config_config_options->mkdocs_config_defaultsmkdocs_config_defaults->markdown_exec_mkdocs_pluginmkdocs_config_defaults->mkdocs_config_basemkdocs_config_defaults->mkdocs_pluginsmkdocs_config_defaults->mkdocs_structure_filesmkdocs_plugins->markdown_exec_mkdocs_pluginmkdocs_plugins->mkdocs_config_config_optionsmkdocs_structuremkdocs.structuremkdocs_structure->markdown_exec_mkdocs_pluginmkdocs_structure->mkdocs_config_defaultsmkdocs_structure->mkdocs_pluginsmkdocs_utilsmkdocs.utilsmkdocs_structure->mkdocs_utilsmkdocs_structure_files->markdown_exec_mkdocs_pluginmkdocs_structure_files->mkdocs_pluginsmkdocs_utils->markdown_exec_mkdocs_pluginmkdocs_utils->mkdocs_config_basemkdocs_utils->mkdocs_config_config_optionsmkdocs_utils->mkdocs_config_defaultsmkdocs_utils->mkdocs_pluginsmkdocs_utils->mkdocs_structure_files

pydeps module dependencies graph
```python exec="true" html="true"
from pydeps import cli, colors, dot, py2depgraph
from pydeps.pydeps import depgraph_to_dotsrc
from pydeps.target import Target

cli.verbose = cli._not_verbose
options = cli.parse_args(["src/markdown_exec", "--noshow"])
colors.START_COLOR = options["start_color"]
target = Target(options["fname"])
with target.chdir_work():
    dep_graph = py2depgraph.py2dep(target, **options)
dot_src = depgraph_to_dotsrc(target, dep_graph, **options)
svg = dot.call_graphviz_dot(dot_src, "svg").decode()
svg = "".join(svg.splitlines()[6:])
svg = svg.replace('fill="white"', 'fill="transparent"')
reference = "../reference"
modules = (
    "markdown_exec",
    "markdown_exec.formatters",
    "markdown_exec.formatters.base",
    "markdown_exec.formatters.bash",
    "markdown_exec.formatters.console",
    "markdown_exec.formatters.markdown",
    "markdown_exec.formatters.pycon",
    "markdown_exec.formatters.pyodide",
    "markdown_exec.formatters.python",
    "markdown_exec.formatters.sh",
    "markdown_exec.formatters.tree",
    "markdown_exec.logger",
    "markdown_exec.mkdocs_plugin",
    "markdown_exec.processors",
    "markdown_exec.rendering",
)
for module in modules:
    svg_title = module.replace(".", "_")
    title_tag = f"<title>{svg_title}</title>"
    href = f"{reference}/{module.replace('.', '/')}/"
    svg = svg.replace(title_tag, f'<a href="{href}"><title>{module}</title>')
svg = svg.replace("</text></g>", "</text></a></g>")
print(svg)
```

Code snippets¤

Rich allows to export syntax-highlighted code as SVG. Here we hardcode the code snippet we want to render, but we could instead include it from somewhere else using the pymdownx.snippets extension or by reading it dynamically from Python. We also prevent Rich from actually writing to the terminal.

async context manager fromcontextlibimportasynccontextmanager importhttpx classBookClient(httpx.AsyncClient): asyncdefget_book(self,book_id:int)->str: response=awaitself.get(f"/books/{book_id}") returnresponse.text @asynccontextmanager asyncdefbook_client(*args,**kwargs): asyncwithBookClient(*args,**kwargs)asclient: yieldclient

Rich SVG code snippet
```python exec="true" html="true"
import os

from rich.console import Console
from rich.padding import Padding
from rich.syntax import Syntax

code = """
    from contextlib import asynccontextmanager
    import httpx


    class BookClient(httpx.AsyncClient):
        async def get_book(self, book_id: int) -> str:
            response = await self.get(f"/books/{book_id}")
            return response.text


    @asynccontextmanager
    async def book_client(*args, **kwargs):
        async with BookClient(*args, **kwargs) as client:
            yield client
"""

with open(os.devnull, "w") as devnull:
    console = Console(record=True, width=65, file=devnull, markup=False)
    renderable = Syntax(code, "python", theme="material")
    renderable = Padding(renderable, (0,), expand=False)
    console.print(renderable, markup=False)
svg = console.export_svg(title="async context manager")
print(svg)
```

Terminal output with colors¤

If you installed Markdown Exec with the ansi extra (pip install markdown-exec[ansi]), the ANSI colors in the output of shell commands will be translated to HTML/CSS, allowing to render them naturally in your documentation pages. For this to happen, use the result="ansi" option.

                40m   41m   42m   43m   44m   45m   46m   47m
     m   xYz    xYz   xYz   xYz   xYz   xYz   xYz   xYz   xYz 
    1m   xYz    xYz   xYz   xYz   xYz   xYz   xYz   xYz   xYz 
   30m   xYz    xYz   xYz   xYz   xYz   xYz   xYz   xYz   xYz 
 1;30m   xYz    xYz   xYz   xYz   xYz   xYz   xYz   xYz   xYz 
   31m   xYz    xYz   xYz   xYz   xYz   xYz   xYz   xYz   xYz 
 1;31m   xYz    xYz   xYz   xYz   xYz   xYz   xYz   xYz   xYz 
   32m   xYz    xYz   xYz   xYz   xYz   xYz   xYz   xYz   xYz 
 1;32m   xYz    xYz   xYz   xYz   xYz   xYz   xYz   xYz   xYz 
   33m   xYz    xYz   xYz   xYz   xYz   xYz   xYz   xYz   xYz 
 1;33m   xYz    xYz   xYz   xYz   xYz   xYz   xYz   xYz   xYz 
   34m   xYz    xYz   xYz   xYz   xYz   xYz   xYz   xYz   xYz 
 1;34m   xYz    xYz   xYz   xYz   xYz   xYz   xYz   xYz   xYz 
   35m   xYz    xYz   xYz   xYz   xYz   xYz   xYz   xYz   xYz 
 1;35m   xYz    xYz   xYz   xYz   xYz   xYz   xYz   xYz   xYz 
   36m   xYz    xYz   xYz   xYz   xYz   xYz   xYz   xYz   xYz 
 1;36m   xYz    xYz   xYz   xYz   xYz   xYz   xYz   xYz   xYz 
   37m   xYz    xYz   xYz   xYz   xYz   xYz   xYz   xYz   xYz 
 1;37m   xYz    xYz   xYz   xYz   xYz   xYz   xYz   xYz   xYz 
ANSI terminal output
```bash exec="true" result="ansi"
#!/bin/bash
# credits to https://github.com/42picky/42picky.github.io
text="xYz"  # Some test text
echo -e "\n                40m   41m   42m   43m   44m   45m   46m   47m"
for FGs in '    m' '   1m' '  30m' '1;30m' '  31m' '1;31m' '  32m' \
           '1;32m' '  33m' '1;33m' '  34m' '1;34m' '  35m' '1;35m' \
           '  36m' '1;36m' '  37m' '1;37m'; do
    FG=${FGs// /}
    echo -en " $FGs \033[$FG  ${text}  "
    for BG in 40m 41m 42m 43m 44m 45m 46m 47m; do
        echo -en "$EINS \033[$FG\033[${BG} ${text} \033[0m"
    done
    echo
done
echo
```

As an alternative, we can use Rich again to render the output of a command in a terminal, with colors. This example is taken directly from the documentation of the Griffe project.

$ griffe check griffe -ssrc -b0.24.0 -a0.23.0
src/griffe/loader.py:156: GriffeLoader.resolve_aliases(only_exported): Parameter kind was changed: positional or keyword -> keyword-only
src/griffe/loader.py:156: GriffeLoader.resolve_aliases(only_exported): Parameter default was changed: True -> None
src/griffe/loader.py:156: GriffeLoader.resolve_aliases(only_known_modules): Parameter kind was changed: positional or keyword -> keyword-only
src/griffe/loader.py:156: GriffeLoader.resolve_aliases(only_known_modules): Parameter default was changed: True -> None
src/griffe/loader.py:156: GriffeLoader.resolve_aliases(max_iterations): Parameter kind was changed: positional or keyword -> keyword-only
src/griffe/loader.py:308: GriffeLoader.resolve_module_aliases(only_exported): Parameter was removed
src/griffe/loader.py:308: GriffeLoader.resolve_module_aliases(only_known_modules): Parameter was removed
src/griffe/git.py:39: tmp_worktree(commit): Parameter was removed
src/griffe/git.py:39: tmp_worktree(repo): Positional parameter was moved: position: from 2 to 1 (-1)
src/griffe/git.py:75: load_git(commit): Parameter was removed
src/griffe/git.py:75: load_git(repo): Parameter kind was changed: positional or keyword -> keyword-only
src/griffe/git.py:75: load_git(submodules): Parameter kind was changed: positional or keyword -> keyword-only
src/griffe/git.py:75: load_git(try_relative_path): Parameter was removed
src/griffe/git.py:75: load_git(extensions): Parameter kind was changed: positional or keyword -> keyword-only
src/griffe/git.py:75: load_git(search_paths): Parameter kind was changed: positional or keyword -> keyword-only
src/griffe/git.py:75: load_git(docstring_parser): Parameter kind was changed: positional or keyword -> keyword-only
src/griffe/git.py:75: load_git(docstring_options): Parameter kind was changed: positional or keyword -> keyword-only
src/griffe/git.py:75: load_git(lines_collection): Parameter kind was changed: positional or keyword -> keyword-only
src/griffe/git.py:75: load_git(modules_collection): Parameter kind was changed: positional or keyword -> keyword-only
src/griffe/git.py:75: load_git(allow_inspection): Parameter kind was changed: positional or keyword -> keyword-only

Rich terminal output
```python exec="true" html="true"
import os

from rich.console import Console

report = """$ griffe check griffe -ssrc -b0.24.0 -a0.23.0
[bold]src/griffe/loader.py[/]:156: GriffeLoader.resolve_aliases([#7faeff]only_exported[/]): [#afaf72]Parameter kind was changed[/]: positional or keyword -> keyword-only
[bold]src/griffe/loader.py[/]:156: GriffeLoader.resolve_aliases([#7faeff]only_exported[/]): [#afaf72]Parameter default was changed[/]: True -> None
[bold]src/griffe/loader.py[/]:156: GriffeLoader.resolve_aliases([#7faeff]only_known_modules[/]): [#afaf72]Parameter kind was changed[/]: positional or keyword -> keyword-only
[bold]src/griffe/loader.py[/]:156: GriffeLoader.resolve_aliases([#7faeff]only_known_modules[/]): [#afaf72]Parameter default was changed[/]: True -> None
[bold]src/griffe/loader.py[/]:156: GriffeLoader.resolve_aliases([#7faeff]max_iterations[/]): [#afaf72]Parameter kind was changed[/]: positional or keyword -> keyword-only
[bold]src/griffe/loader.py[/]:308: GriffeLoader.resolve_module_aliases([#7faeff]only_exported[/]): [#afaf72]Parameter was removed[/]
[bold]src/griffe/loader.py[/]:308: GriffeLoader.resolve_module_aliases([#7faeff]only_known_modules[/]): [#afaf72]Parameter was removed[/]
[bold]src/griffe/git.py[/]:39: tmp_worktree([#7faeff]commit[/]): [#afaf72]Parameter was removed[/]
[bold]src/griffe/git.py[/]:39: tmp_worktree([#7faeff]repo[/]): [#afaf72]Positional parameter was moved[/]: position: from 2 to 1 (-1)
[bold]src/griffe/git.py[/]:75: load_git([#7faeff]commit[/]): [#afaf72]Parameter was removed[/]
[bold]src/griffe/git.py[/]:75: load_git([#7faeff]repo[/]): [#afaf72]Parameter kind was changed[/]: positional or keyword -> keyword-only
[bold]src/griffe/git.py[/]:75: load_git([#7faeff]submodules[/]): [#afaf72]Parameter kind was changed[/]: positional or keyword -> keyword-only
[bold]src/griffe/git.py[/]:75: load_git([#7faeff]try_relative_path[/]): [#afaf72]Parameter was removed[/]
[bold]src/griffe/git.py[/]:75: load_git([#7faeff]extensions[/]): [#afaf72]Parameter kind was changed[/]: positional or keyword -> keyword-only
[bold]src/griffe/git.py[/]:75: load_git([#7faeff]search_paths[/]): [#afaf72]Parameter kind was changed[/]: positional or keyword -> keyword-only
[bold]src/griffe/git.py[/]:75: load_git([#7faeff]docstring_parser[/]): [#afaf72]Parameter kind was changed[/]: positional or keyword -> keyword-only
[bold]src/griffe/git.py[/]:75: load_git([#7faeff]docstring_options[/]): [#afaf72]Parameter kind was changed[/]: positional or keyword -> keyword-only
[bold]src/griffe/git.py[/]:75: load_git([#7faeff]lines_collection[/]): [#afaf72]Parameter kind was changed[/]: positional or keyword -> keyword-only
[bold]src/griffe/git.py[/]:75: load_git([#7faeff]modules_collection[/]): [#afaf72]Parameter kind was changed[/]: positional or keyword -> keyword-only
[bold]src/griffe/git.py[/]:75: load_git([#7faeff]allow_inspection[/]): [#afaf72]Parameter kind was changed[/]: positional or keyword -> keyword-only
"""

with open(os.devnull, "w") as devnull:
    console = Console(record=True, width=150, file=devnull)
    console.print(report, markup=True, highlight=False)
print(console.export_html(inline_styles=True, code_format="<pre><code>{code}</code></pre>"))
```

TUI screenshots¤

Textual allows to build Terminal User Interfaces (TUIs). In this example we generate the SVG image of a terminal interface.

TextApp ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Hello ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ World! ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Textual screenshot
```python exec="1" html="true"
from textual.app import App, ComposeResult
from textual.widgets import Static
from textual._doc import take_svg_screenshot


class TextApp(App):
    CSS = """
    Screen {
        background: darkblue;
        color: white;
        layout: vertical;
    }
    Static {
        height: auto;
        padding: 2;
        border: heavy white;
        background: #ffffff 30%;
        content-align: center middle;
    }
    """

    def compose(self) -> ComposeResult:
        yield Static("Hello")
        yield Static("[b]World![/b]")


print(take_svg_screenshot(app=TextApp(), terminal_size=(80, 24)))
```

Charts and Plots¤

With Matplotlib:

2024-03-20T18:14:38.863223 image/svg+xml Matplotlib v3.8.3, https://matplotlib.org/
matplotlib graph
```python exec="1" html="1"
# https://matplotlib.org/stable/gallery/lines_bars_and_markers/scatter_demo2.html
from io import StringIO

import matplotlib.cbook as cbook
import matplotlib.pyplot as plt
import numpy as np

# Load a numpy record array from yahoo csv data with fields date, open, close,
# volume, adj_close from the mpl-data/example directory. The record array
# stores the date as an np.datetime64 with a day unit ('D') in the date column.
price_data = cbook.get_sample_data("goog.npz", np_load=True)["price_data"].view(np.recarray)
price_data = price_data[-250:]  # get the most recent 250 trading days

delta1 = np.diff(price_data.adj_close) / price_data.adj_close[:-1]

# Marker size in units of points^2
volume = (15 * price_data.volume[:-2] / price_data.volume[0]) ** 2
close = 0.003 * price_data.close[:-2] / 0.003 * price_data.open[:-2]

fig, ax = plt.subplots()
ax.scatter(delta1[:-1], delta1[1:], c=close, s=volume, alpha=0.5)

ax.set_xlabel(r"$\Delta_i$", fontsize=15)
ax.set_ylabel(r"$\Delta_{i+1}$", fontsize=15)
ax.set_title("Volume and percent change")

ax.grid(True)
fig.tight_layout()

buffer = StringIO()
plt.savefig(buffer, format="svg")
print(buffer.getvalue())
```

Python module output¤

This example uses Python's runpy module to run another Python module. This other module's output is captured by temporarily patching sys.stdout with a text buffer.

Usage: mkdocs [OPTIONS] COMMAND [ARGS]...

  MkDocs - Project documentation with Markdown.

Options:
  -V, --version         Show the version and exit.
  -q, --quiet           Silence warnings
  -v, --verbose         Enable verbose output
  --color / --no-color  Force enable or disable color and wrapping for the
                        output. Default is auto-detect.
  -h, --help            Show this message and exit.

Commands:
  build      Build the MkDocs documentation
  get-deps   Show required PyPI packages inferred from plugins in mkdocs.yml
  gh-deploy  Deploy your documentation to GitHub Pages
  new        Create a new MkDocs project
  serve      Run the builtin development server
runpy and script/module output
```python exec="true"
import sys
import warnings
from contextlib import suppress
from io import StringIO
from runpy import run_module

old_argv = list(sys.argv)
sys.argv = ["mkdocs"]
old_stdout = sys.stdout
sys.stdout = StringIO()
warnings.filterwarnings("ignore", category=RuntimeWarning)
with suppress(SystemExit):
    run_module("mkdocs", run_name="__main__")
output = sys.stdout.getvalue()
sys.stdout = old_stdout
sys.argv = old_argv

print(f"```\n{output}\n```")
```

Python CLI documentation¤

Argparse help message (code block)¤

Instead of blindly running a module with runpy to get its help message, if you know the project is using argparse to build its command line interface, and if it exposes its parser, then you can get the help message directly from the parser.

usage: duty [GLOBAL_OPTS...] [DUTY [DUTY_OPTS...] [DUTY_PARAMS...]...]

A simple task runner.

positional arguments:
  remainder

Global options:
  -d DUTIES_FILE, --duties-file DUTIES_FILE
                        Python file where the duties are defined.
  -l, --list            List the available duties.
  -h [DUTY ...], --help [DUTY ...]
                        Show this help message and exit. Pass duties names to
                        print their help.
  -V, --version         show program's version number and exit
  --debug-info          Print debug information.
  -c {stdout,stderr,both,none}, --capture {stdout,stderr,both,none}
                        Which output to capture. Colors are supported with
                        'both' only, unless the command has a 'force color'
                        option.
  -f {pretty,tap}, --fmt {pretty,tap}, --format {pretty,tap}
                        Output format. Pass your own Jinja2 template as a
                        string with '-f custom=TEMPLATE'. Available variables:
                        command, title (command or title passed with -t), code
                        (exit status), success (boolean), failure (boolean),
                        number (command number passed with -n), output
                        (command output), nofail (boolean), quiet (boolean),
                        silent (boolean). Available filters: indent
                        (textwrap.indent).
  -y, --pty             Enable the use of a pseudo-terminal. PTY doesn't allow
                        programs to use standard input.
  -Y, --no-pty          Disable the use of a pseudo-terminal. PTY doesn't
                        allow programs to use standard input.
  -p, --progress        Print progress while running a command.
  -P, --no-progress     Don't print progress while running a command.
  -q, --quiet           Don't print the command output, even if it failed.
  -Q, --no-quiet        Print the command output when it fails.
  -s, --silent          Don't print anything.
  -S, --no-silent       Print output as usual.
  -z, --zero, --nofail  Don't fail. Always return a success (0) exit code.
  -Z, --no-zero, --strict
                        Return the original exit code.
argparse parser help message
```python exec="true"
from duty.cli import get_parser

parser = get_parser()
print(f"```\n{parser.format_help()}\n```")
```

Argparse parser documentation¤

In this example, we inspect the argparse parser to build better-looking Markdown/HTML contents. We simply use the description and iterate on options, but more complex stuff is possible of course.

duty¤

A simple task runner.

Options:

  • -d,--duties-file: Python file where the duties are defined.(default: duties.py)
  • -l,--list: List the available duties.
  • -h,--help DUTY: Show this help message and exit. Pass duties names to print their help.
  • -V,--version: show program's version number and exit
  • --debug-info: Print debug information.
  • -c,--capture: Which output to capture. Colors are supported with 'both' only, unless the command has a 'force color' option.
  • -f,--fmt,--format: Output format. Pass your own Jinja2 template as a string with '-f custom=TEMPLATE'. Available variables: command, title (command or title passed with -t), code (exit status), success (boolean), failure (boolean), number (command number passed with -n), output (command output), nofail (boolean), quiet (boolean), silent (boolean). Available filters: indent (textwrap.indent).
  • -y,--pty: Enable the use of a pseudo-terminal. PTY doesn't allow programs to use standard input.
  • -Y,--no-pty: Disable the use of a pseudo-terminal. PTY doesn't allow programs to use standard input.
  • -p,--progress: Print progress while running a command.
  • -P,--no-progress: Don't print progress while running a command.
  • -q,--quiet: Don't print the command output, even if it failed.
  • -Q,--no-quiet: Print the command output when it fails.
  • -s,--silent: Don't print anything.
  • -S,--no-silent: Print output as usual.
  • -z,--zero,--nofail: Don't fail. Always return a success (0) exit code.
  • -Z,--no-zero,--strict: Return the original exit code.
CLI help using argparse parser
```python exec="true" updatetoc="no"
import argparse

from duty.cli import get_parser

parser = get_parser()
lines = []
lines.append(f"## duty")
if parser.description:
    lines.append(parser.description)
lines.append("\nOptions:\n")
for action in parser._actions:
    opts = [f"`{opt}`" for opt in action.option_strings]
    if not opts:
        continue
    line = "- " + ",".join(opts)
    if action.metavar:
        line += f" `{action.metavar}`"
    line += f": {action.help}"
    if action.default and action.default != argparse.SUPPRESS:
        line += f"(default: {action.default})"
    lines.append(line)
print("\n".join(lines))
```