Skip to content

Commit

Permalink
docs: Add examples to gallery
Browse files Browse the repository at this point in the history
Issue-9: #9
  • Loading branch information
pawamoy committed Jun 13, 2024
1 parent 500ed1b commit 3c5ca75
Show file tree
Hide file tree
Showing 11 changed files with 357 additions and 93 deletions.
4 changes: 3 additions & 1 deletion devdeps.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ types-pyyaml>=6.0
# docs
black>=24.4
markdown-callouts>=0.4
markdown-exec>=1.8
mkdocs>=1.6
mkdocs-coverage>=1.0
mkdocs-gen-files>=0.5
Expand All @@ -40,3 +39,6 @@ pytermgui>=6.3
pipdeptree>=2.6
pip>=24
pygments>=2.15
drawsvg
hyperbolic
qrcode
204 changes: 115 additions & 89 deletions docs/gallery.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,176 +7,202 @@ hide:

Welcome to our gallery of examples!

## Diagrams (cloud/system architecture)

[Diagrams](https://github.com/mingrammer/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.

````md exec="1" source="tabbed-right" title="Diagrams"
## Diagrams, charts, graphs, plots

### with [Diagrams](https://github.com/mingrammer/diagrams)

> Diagram as Code for prototyping cloud system architectures.
````md exec="1" source="tabbed-right"
```python exec="true" html="true"
--8<-- "gallery/diagrams.py"
```
````

## Python dependency tree
### with [D2](https://d2lang.com/)

> A modern diagram scripting language that turns text to diagrams.
````md exec="1" source="tabbed-right"
```python exec="true" html="true"
--8<-- "gallery/d2.py"
```
````

### with [Matplotlib](https://matplotlib.org/)

> Matplotlib is a comprehensive library for creating static, animated, and interactive visualizations in Python.
````md exec="1" source="tabbed-right"
```python exec="1" html="1"
--8<-- "gallery/matplotlib.py"
```
````

### with [pipdeptree](https://github.com/tox-dev/pipdeptree)

[pipdeptree](https://github.com/tox-dev/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.
> A command line utility to display dependency tree of the installed Python packages.
````md exec="1" source="tabbed-right" title="pipdeptree mermaid diagram"
We call `pipdeptree` with its `--mermaid` option to generate a [Mermaid](https://mermaid.js.org/) diagram.

````md exec="1" source="tabbed-right"
```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:

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

## Python modules inter-dependencies
### with [pydeps](https://github.com/thebjorn/pydeps)

This example uses [pydeps](https://github.com/thebjorn/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](https://graphviz.org/). In this example we also add links
to the code reference in related nodes. Try clicking on the `markdown_exec` nodes!
> Python Module Dependency graphs.
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.
pydeps uses [Graphviz](https://graphviz.org/) 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!

````md exec="1" source="tabbed-right" title="pydeps module dependencies graph"
````md exec="1" source="tabbed-right"
```python exec="true" html="true"
--8<-- "gallery/pydeps.py"
```
````

## Code snippets

[Rich](https://github.com/Textualize/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](https://facelessuser.github.io/pymdown-extensions/extensions/snippets/)
or by reading it dynamically from Python.
We also prevent Rich from actually writing to the terminal.
### with [Rich](https://github.com/Textualize/rich)

> Rich is a Python library for rich text and beautiful formatting in the terminal.
````md exec="1" source="tabbed-right" title="Rich SVG code snippet"
````md exec="1" source="tabbed-right"
```python exec="true" html="true"
--8<-- "gallery/rich.py"
```
````

<!--
Similarly, [PyTermGUI](https://github.com/bczsalba/pytermgui) also allows
to export syntax-highlighted code as SVG.
### with [PyTermGUI](https://github.com/bczsalba/pytermgui)

<!--```python exec="true" html="true" source="tabbed-right" title="PyTermGUI SVG code snippet"
> Python TUI framework with mouse support, modular widget system, customizable and rapid terminal markup language and more!
````md exec="1" source="tabbed-right"
```python exec="true" html="true"
--8<-- "gallery/pytermgui.py"
<!--```
```
````

TIP: There's a PyTermGUI-dedicated MkDocs plugin that allows
to generate SVGs on-the-fly: [Termage](https://github.com/bczsalba/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.
-->
TIP: There's a PyTermGUI-dedicated MkDocs plugin that allows to generate SVGs on-the-fly: [Termage](https://github.com/bczsalba/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.

## Terminal output with colors
## 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](http://localhost:8000/markdown-exec/usage/#wrap-result-in-a-code-block).
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](http://localhost:8000/markdown-exec/usage/#wrap-result-in-a-code-block).

````md exec="1" source="tabbed-right" title="ANSI terminal output"
````md exec="1" source="tabbed-right"
```bash exec="true" result="ansi"
--8<-- "gallery/ansi.sh"
```
````

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](https://github.com/mkdocstrings/griffe) project.
Another example with [Griffe](https://mkdocstrings.github.io/griffe/):

````md exec="1" source="tabbed-right"
```console exec="1" source="console" result="ansi" returncode="1"
$ griffe check markdown_exec -ssrc -b1.8.3 -a1.5.0 --color --verbose
```
````

````md exec="1" source="tabbed-right" title="Rich terminal output"
### with [Rich](https://github.com/Textualize/rich)

> Rich is a Python library for rich text and beautiful formatting in the terminal.
````md exec="1" source="tabbed-right"
```python exec="true" html="true"
--8<-- "gallery/rich_terminal.py"
```
````

## TUI screenshots
## SVG drawings

[Textual](https://github.com/Textualize/textual) allows to build Terminal User Interfaces (TUIs).
In this example we generate the SVG image of a terminal interface.
### with [Drawsvg 2](https://github.com/cduck/drawsvg)

````md exec="1" source="tabbed-right" title="Textual screenshot"
```python exec="1" html="true"
--8<-- "gallery/textual.py"
> Programmatically generate SVG (vector) images, animations, and interactive Jupyter widgets.
````md exec="1" source="tabbed-right"
```python exec="true" html="true"
--8<-- "gallery/drawsvg.py"
```
````

## Charts and Plots
### with [Hyperbolic](https://github.com/cduck/hyperbolic)

With [Matplotlib](https://matplotlib.org/):
> A Python 3 library for constructing and drawing hyperbolic geometry.
````md exec="1" source="tabbed-right" title="matplotlib graph"
```python exec="1" html="1"
--8<-- "gallery/matplotlib.py"
````md exec="1" source="tabbed-right"
```python exec="true" html="true"
--8<-- "gallery/hyperbolic.py"
```
````

## Python module output
## QRCodes

This example uses Python's [`runpy`][runpy] module to run another
Python module. This other module's output is captured by temporarily
patching `sys.stdout` with a text buffer.
### with [qrcode](https://pypi.org/project/qrcode/)

````md exec="1" source="tabbed-right" title="runpy and script/module output"
```python exec="true"
--8<-- "gallery/runpy.py"
> Python QR Code image generator.
````md exec="1" source="tabbed-right"
```python exec="true" html="true"
--8<-- "gallery/qrcode.py"
```
````

## TUI screenshots

### with [Textual](https://github.com/Textualize/textual)

> Textual is a *Rapid Application Development* framework for Python, built by [Textualize.io](https://www.textualize.io/).
````md exec="1" source="tabbed-right"
```python exec="1" html="true"
--8<-- "gallery/textual.py"
```
````

## Python CLI documentation

### Argparse help message (code block)
### with [`argparse`](https://docs.python.org/3/library/argparse.html#module-argparse) (code block)

Instead of blindly running a module with `runpy` to get its help message,
if you know the project is using [`argparse`][argparse] to build its command line
interface, and if it exposes its parser, then you can get the help message
directly from the parser.
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.

````md exec="1" source="tabbed-right" title="argparse parser help message"
````md exec="1" source="tabbed-right"
```python exec="true"
--8<-- "gallery/argparse_format.py"
```
````

### Argparse parser documentation
### with [`argparse`](https://docs.python.org/3/library/argparse.html#module-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.
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.

````md exec="1" source="tabbed-right" title="CLI help using argparse parser"
````md exec="1" source="tabbed-right"
```python exec="true" updatetoc="no"
--8<-- "gallery/argparse.py"
```
````

## Other techniques

### with [`runpy`](https://docs.python.org/3/library/runpy.html#module-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.

````md exec="1" source="tabbed-right"
```python exec="true"
--8<-- "gallery/runpy.py"
```
````
14 changes: 14 additions & 0 deletions docs/snippets/gallery/chalk.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from colour import Color
from chalk import Diagram, triangle

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

d = sierpinski(5, 4).fill_color(papaya)
d.render()
48 changes: 48 additions & 0 deletions docs/snippets/gallery/d2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import os # markdown-exec: hide
if "CI" in os.environ: # markdown-exec: hide
print("D2 is not installed in CI, skipping this gallery example.") # markdown-exec: hide
raise SystemExit(0) # markdown-exec: hide
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)
13 changes: 12 additions & 1 deletion docs/snippets/gallery/diagrams.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,22 @@
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()

print(f'<img src="data:image/png;base64, {png}"/>')
# 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>')
Loading

0 comments on commit 3c5ca75

Please # to comment.