Skip to content

Gallery¤

Welcome to our gallery of examples!

Diagrams, charts, graphs, plots¤

with Diagrams¤

Diagram as Code for prototyping cloud system architectures.

```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

# 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.
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()

# Wrapping the image in a div prevents it from being wrapped in a paragraph,
# which would add unnecessary space around it.
print(f'<div><img src="data:image/png;base64, {png}"/></div>')
```

with D2¤

A modern diagram scripting language that turns text to diagrams.

Before and after becoming friendsOffice chatter in 2007Office chatter in 2012yearslaterAliceBobbyAliceBobby Fiveawkward small talkicebreaker attemptunfortunate outcomeuhm, hioh, hellowhat did you have for lunch?that's personalWant to play with ChatGPT?Yes!Write a play...about 2 friends...who find love...in a sequence diagram
```python exec="true" html="true"
import subprocess

diagram = """
direction: right

Before and after becoming friends: {
  2007: Office chatter in 2007 {
    shape: sequence_diagram
    alice: Alice
    bob: Bobby

    awkward small talk: {
      alice -> bob: uhm, hi
      bob -> alice: oh, hello

      icebreaker attempt: {
        alice -> bob: what did you have for lunch?
      }

      unfortunate outcome: {
        bob -> alice: that's personal
      }
    }
  }

  2012: Office chatter in 2012 {
    shape: sequence_diagram
    alice: Alice
    bob: Bobby
    alice -> bob: Want to play with ChatGPT?
    bob -> alice: Yes!
    bob -> alice.play: Write a play...
    alice.play -> bob.play: about 2 friends...
    bob.play -> alice.play: who find love...
    alice.play -> bob.play: in a sequence diagram
  }

  2007 -> 2012: Five\nyears\nlater
}
"""

# We simply run `d2` in a subprocess, passing it our diagram as input and capturing its output to print it.
svg = subprocess.check_output(["d2", "-", "-"], input=diagram, stderr=subprocess.DEVNULL, text=True)
print(svg)
```

with Matplotlib¤

Matplotlib is a comprehensive library for creating static, animated, and interactive visualizations in Python.

2024-07-08T23:57:48.246644 image/svg+xml Matplotlib v3.9.1, https://matplotlib.org/
```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")["price_data"]
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())
```

with pipdeptree¤

A command line utility to display dependency tree of the installed Python packages.

We call pipdeptree with its --mermaid option to generate a Mermaid diagram.

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

```bash exec="1" result="mermaid"
# Change the direction of the graph from top-down to left-right,
# and remove local version identifiers from our own package.
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.47.0.1.2.0"]
    jinja2["Jinja2\n3.1.4"]
    markdown["Markdown\n3.6"]
    markupsafe["MarkupSafe\n2.1.5"]
    mergedeep["mergedeep\n1.3.4"]
    mkdocs-autorefs["mkdocs-autorefs\n1.0.1"]
    mkdocs-get-deps["mkdocs-get-deps\n0.2.0"]
    mkdocs["mkdocs\n1.6.0"]
    mkdocstrings-python["mkdocstrings-python\n1.10.5.1.8.3"]
    mkdocstrings["mkdocstrings\n0.25.1"]
    packaging["packaging\n24.1"]
    pathspec["pathspec\n0.12.1"]
    platformdirs["platformdirs\n4.2.2"]
    pymdown-extensions["pymdown-extensions\n10.8.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.1"]
    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 -- ">=0.2.0" --> mkdocs-get-deps
    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 -- ">=20.5" --> packaging
    mkdocs -- ">=3.3.6" --> 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
    mkdocs-get-deps -- ">=1.3.4" --> mergedeep
    mkdocs-get-deps -- ">=2.2.0" --> platformdirs
    mkdocs-get-deps -- ">=5.1" --> pyyaml
    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.25" --> mkdocstrings
    mkdocstrings-python -- ">=0.47" --> griffe
    pymdown-extensions -- ">=3.6" --> markdown
    pymdown-extensions -- "any" --> pyyaml
    python-dateutil -- ">=1.5" --> six
    pyyaml-env-tag -- "any" --> pyyaml

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

with Plotly¤

The interactive graphing library for Python ✨

```python exec="true" html="true"
import plotly.express as px

fig = px.bar(x=["a", "b", "c"], y=[1, 3, 2])
print(fig.to_html(full_html=False, include_plotlyjs="cdn"))
```

with pydeps¤

Python Module Dependency graphs.

pydeps uses Graphviz under the hood to generate graphs. In this example we add links to the code reference in related nodes. Try clicking on the markdown_exec nodes!

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__exec_pythonmarkdown_exec.formatters._exec_pythonmarkdown_exec.formatters.pythonmarkdown_exec.formatters.pythonmarkdown_exec_formatters__exec_python->markdown_exec_formatters_pythonmarkdown_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_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_formatters_treemarkupsafe->markdown_exec_processorsmarkupsafe->markdown_exec_renderingmaterialmaterialmaterial->markdown_exec_formatters_treemkdocsmkdocsmkdocs->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_exceptionsmkdocs.exceptionsmkdocs_exceptions->markdown_exec_mkdocs_pluginmkdocs_exceptions->mkdocs_config_basemkdocs_exceptions->mkdocs_config_config_optionsmkdocs_utilsmkdocs.utilsmkdocs_exceptions->mkdocs_utilsmkdocs_plugins->markdown_exec_mkdocs_pluginmkdocs_plugins->mkdocs_config_config_optionsmkdocs_structuremkdocs.structuremkdocs_structure->markdown_exec_mkdocs_pluginmkdocs_structure->mkdocs_config_defaultsmkdocs_structure->mkdocs_pluginsmkdocs_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

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

# 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.

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¤

with Rich¤

Rich is a Python library for rich text and beautiful formatting in 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
```python exec="true" html="true"
import os

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

# Here we hardcode the code snippet we want to render,
# but we could instead include it from somewhere else using the `pymdownx.snippets` extension
# (https://facelessuser.github.io/pymdown-extensions/extensions/snippets/)
# or by reading it dynamically from Python.
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
"""

# We prevent Rich from actually writing to the terminal.
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")

# Wrapping the SVG in a div prevents it from being wrapped in a paragraph,
# which would add unnecessary space around it.
print(f"<div>{svg}</div>")
```

with PyTermGUI¤

Python TUI framework with mouse support, modular widget system, customizable and rapid terminal markup language and more!

PyTermGUI      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
```python exec="true" html="true"
from io import StringIO

import pytermgui as ptg

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
"""

terminal = ptg.Terminal(stream=StringIO(), size=(80, 16))
ptg.set_global_terminal(terminal)
with terminal.record() as recorder:
    recorder.write(ptg.tim.parse(ptg.highlight_python(code)))
svg = recorder.export_svg(inline_styles=True)

# Wrapping the SVG in a div prevents it from being wrapped in a paragraph,
# which would add unnecessary space around it.
print(f"<div>{svg}</div>")
```

Tip

There's a PyTermGUI-dedicated MkDocs plugin that allows to generate SVGs on-the-fly: Termage. It is implemented using regular expressions in the on_markdown event of MkDocs, so is probably less robust than our actual SuperFence implementation here, but also allows for less verbose source to generate the SVG snippets.

Console output¤

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 
```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
```

with Rich¤

Rich is a Python library for rich text and beautiful formatting in the terminal.

$ 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

```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>"))
```

SVG drawings¤

with Chalk¤

A declarative drawing API in Python.

```python exec="true" html="true"
from tempfile import NamedTemporaryFile
from chalk import Diagram, triangle, unit_x
from colour import Color

papaya = Color("#ff9700")

def sierpinski(n: int, size: int) -> Diagram:
    if n <= 1:
        return triangle(size)
    else:
        smaller = sierpinski(n - 1, size / 2)
        return smaller.above(smaller.beside(smaller, unit_x).center_xy())

d = sierpinski(5, 4).fill_color(papaya)

# Chalk doesn't provide an easy method to get a string directly,
# so we use a temporary file.
with NamedTemporaryFile("w+") as tmpfile:
    d.render_svg(tmpfile.name, height=256)
    tmpfile.seek(0)
    svg = tmpfile.read()

print(svg)
```

with Drawsvg 2¤

Programmatically generate SVG (vector) images, animations, and interactive Jupyter widgets.

```python exec="true" html="true"
import drawsvg as draw

d = draw.Drawing(200, 200, origin='center')

# Animate the position and color of circle
c = draw.Circle(0, 0, 20, fill='red')
# See for supported attributes:
# https://developer.mozilla.org/en-US/docs/Web/SVG/Element/animate
c.append_anim(draw.Animate('cy', '6s', '-80;80;-80',
                           repeatCount='indefinite'))
c.append_anim(draw.Animate('cx', '6s', '0;80;0;-80;0',
                           repeatCount='indefinite'))
c.append_anim(draw.Animate('fill', '6s', 'red;green;blue;yellow',
                           calc_mode='discrete',
                           repeatCount='indefinite'))
d.append(c)

# Animate a black circle around an ellipse
ellipse = draw.Path()
ellipse.M(-90, 0)
ellipse.A(90, 40, 360, True, True, 90, 0)  # Ellipse path
ellipse.A(90, 40, 360, True, True, -90, 0)
ellipse.Z()
c2 = draw.Circle(0, 0, 10)
# See for supported attributes:
# https://developer.mozilla.org/en-US/docs/Web/SVG/Element/animate_motion
c2.append_anim(draw.AnimateMotion(ellipse, '3s',
                                  repeatCount='indefinite'))
# See for supported attributes:
# https://developer.mozilla.org/en-US/docs/Web/SVG/Element/animate_transform
c2.append_anim(draw.AnimateTransform('scale', '3s', '1,2;2,1;1,2;2,1;1,2',
                                     repeatCount='indefinite'))
d.append(c2)
print(d.as_svg())
```

with Hyperbolic¤

A Python 3 library for constructing and drawing hyperbolic geometry.

```python exec="true" html="true"
import math

from drawsvg import Drawing
from hyperbolic.poincare import *
from hyperbolic.poincare.util import triangle_side_for_angles
import hyperbolic.tiles as htiles


p1 = 4
p2 = 3
q = 3
rotate = 0

theta1, theta2 = math.pi*2/p1, math.pi*2/p2
phi_sum = math.pi*2/q
r1 = triangle_side_for_angles(theta1/2, phi_sum/2, theta2/2)
r2 = triangle_side_for_angles(theta2/2, phi_sum/2, theta1/2)

t_gen1 = htiles.TileGen.make_regular(p1, hr=r1, skip=1)
t_gen2 = htiles.TileGen.make_regular(p2, hr=r2, skip=1)

t_layout = htiles.TileLayout()
t_layout.add_generator(t_gen1, (1,)*p1)
t_layout.add_generator(t_gen2, (0,)*p2, htiles.TileDecoratorNull())
start_tile = t_layout.default_start_tile(rotate_deg=rotate)

t1 = start_tile
t2 = t_layout.place_tile(t1.sides[-1])
t3 = t_layout.place_tile(t2.sides[-1])
point_base = t3.vertices[-1]
points = [Transform.rotation(deg=-i*360/p1).apply_to_point(point_base)
          for i in range(p1)]
vertices = start_tile.vertices
edges = []
for i, point in enumerate(points):
    v1 = vertices[i]
    v2 = vertices[(i+1)%p1]
    edge = Hypercycle.from_points(*v1, *v2, *point, segment=True, exclude_mid=True)
    edges.append(edge)
decorate_poly = Polygon(edges=edges, vertices=vertices)
decorator1 = htiles.TileDecoratorPolygons(decorate_poly)
t_layout.set_decorator(decorator1, 0)

start_tile = t_layout.default_start_tile(rotate_deg=rotate)
tiles = t_layout.tile_plane(start_tile, depth=6)

d = Drawing(2, 2, origin='center')
#d.draw(euclid.Circle(0, 0, 1), fill='silver')
for tile in tiles:
    d.draw(tile, hwidth=0.02, fill='red')
tiles[0].decorator = None
d.draw(
    Hypercycle.from_points(
        *tiles[0].vertices[0], *tiles[0].vertices[1], *point_base
    ),
    hwidth=0.02,
    fill='black',
)

d.set_render_size(w=400)
print(d.as_svg())
```

QRCodes¤

with qrcode¤

Python QR Code image generator.

```python exec="true" html="true"
import qrcode
from qrcode.image.svg import SvgPathImage

img = qrcode.make("https://github.com/sponsors/lincolnloop", box_size=20, border=2, image_factory=SvgPathImage)
print(f'<div>{img.to_string().decode("utf8")}</div>')
```

TUI screenshots¤

with Textual¤

Textual is a Rapid Application Development framework for Python, built by Textualize.io.

TextApp ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ Hello ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ World! ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

```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)))
```

File-trees¤

This example displays a file-tree of the current project, in which you can descend thanks to Material for MkDocs' code annotations. It uses a recursive Python function which accept a code block session name as parameter 🤯:

📁 .git/
📁 .github/ # (1)!
📁 .venv/
📁 .venvs/
📁 .vscode/ # (2)!
📁 build/ # (3)!
📁 config/ # (4)!
📁 docs/ # (5)!
📁 htmlcov/
📁 scripts/ # (6)!
📁 site/
📁 src/ # (7)!
📁 tests/ # (8)!
 .copier-answers.yml
 .envrc
 .gitignore
 .gitpod.dockerfile
 .gitpod.yml
 CHANGELOG.md
 CODE_OF_CONDUCT.md
 CONTRIBUTING.md
 LICENSE
 Makefile
 README.md
 devdeps.txt
 duties.py
 mkdocs.yml
 pyproject.toml
  1. 📁 ISSUE_TEMPLATE/ # (1)!
    📁 workflows/ # (2)!
     FUNDING.yml
    
    1.  bug_report.md
       config.yml
       feature_request.md
      
    2.  ci.yml
       release.yml
      
  2.  launch.json
     settings.json
     tasks.json
    
  3.  _editable_impl_markdown_exec.py
     markdown_exec.pth
    
  4. 📁 vscode/ # (1)!
     coverage.ini
     git-changelog.toml
     mypy.ini
     pytest.ini
     ruff.toml
    
    1.  launch.json
       settings.json
       tasks.json
      
  5. 📁 .overrides/ # (1)!
    📁 css/ # (2)!
    📁 insiders/ # (3)!
    📁 js/ # (4)!
    📁 snippets/ # (5)!
    📁 usage/ # (6)!
     changelog.md
     code_of_conduct.md
     contributing.md
     credits.md
     gallery.md
     index.md
     license.md
     schema.json
    
    1.  main.html
      
    2.  insiders.css
       material.css
       mkdocstrings.css
      
    3.  changelog.md
       goals.yml
       index.md
       installation.md
      
    4.  insiders.js
      
    5. 📁 gallery/ # (1)!
      📁 usage/ # (2)!
      
      1.  ansi.sh
         argparse.py
         argparse_format.py
         chalk.py
         d2.py
         diagrams.py
         drawsvg.py
         expandable_filetree.py
         hyperbolic.py
         matplotlib.py
         plotly.py
         pydeps.py
         pytermgui.py
         qrcode.py
         rich.py
         rich_terminal.py
         runpy.py
         textual.py
        
      2.  boolean_matrix.py
         hide.py
         multiple.pycon
         platform_html.py
         platform_md.py
         source.py
         source.pycon
        
    6.  index.md
       pyodide.md
       python.md
       shell.md
       tree.md
      
  6.  gen_credits.py
     gen_ref_nav.py
     insiders.py
     make
    
  7. 📁 markdown_exec/ # (1)!
    
    1. 📁 formatters/ # (1)!
       __init__.py
       ansi.css
       debug.py
       logger.py
       mkdocs_plugin.py
       processors.py
       py.typed
       pyodide.css
       pyodide.js
       rendering.py
      
      1.  __init__.py
         _exec_python.py
         base.py
         bash.py
         console.py
         markdown.py
         pycon.py
         pyodide.py
         python.py
         sh.py
         tree.py
        
  8.  __init__.py
     conftest.py
     test_base_formatter.py
     test_converter.py
     test_headings.py
     test_python.py
     test_shell.py
     test_toc.py
     test_tree.py
     test_validator.py
    
```python exec="1" session="filetree"
from fnmatch import fnmatch
from pathlib import Path

exclude = {"dist", "*cache*", ".devbox", ".hypothesis", ".pdm*", ".coverage*", "profile.*"}
no_recurse = {".venv*", "site", "htmlcov", ".git"}


def exptree(path: str, session: str) -> None:
    # List files and directories separately.
    files = []
    dirs = []
    for node in Path(path).iterdir():
        if any(fnmatch(node.name, pattern) for pattern in exclude):
            continue
        if node.is_dir():
            dirs.append(node)
        else:
            files.append(node)

    # Print directories first, then files (both sorted).
    recurse = []
    print("```tree")
    for directory in sorted(dirs):
        if any(fnmatch(directory.name, pattern) for pattern in no_recurse):
            print(f"{directory.name}/")
        else:
            recurse.append(directory.name)
            # Add code annotation at the end.
            print(f"{directory.name}/ # ({len(recurse)})!")
    for file in sorted(files):
        print(file.name)
    print("```\n")

    # Print contents of each annotated directory.
    for index, directory in enumerate(recurse, 1):
        new_path = f"{path}/{directory}"
        print(f"{index}. \n")
        # The recursive part!
        print(f'    ```python exec="1" session="{session}"')
        print(f'    exptree("{new_path}", "{session}")')
        print("    ```\n")
```

```python exec="1" session="filetree"
exptree(".", "filetree")
```

Python CLI documentation¤

with argparse (code block)¤

If you know a 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.
```python exec="true"
from duty.cli import get_parser

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

with argparse (Markdown)¤

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.
```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))
```

with runpy¤

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.
```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```")
```