Diagrams¶
rs_rich.diagram is the rs-rich-diagram crate from Python: graphs drawn
with box-drawing characters (or ASCII), laid out in layers, and DOT
(Graphviz) sources parsed and drawn natively. Rich has no counterpart. The
same layout draws Mermaid flowcharts.
| Name | What it is |
|---|---|
Graph |
a graph built in code; node and edge return it, so calls chain |
Diagram |
a Graph as a renderable |
draw_graph(graph, ascii=False) |
the drawing as a list of lines |
Dot |
a DOT source as a renderable |
parse_dot(source) |
a DOT source read into a DotGraph, or DotError |
DiagramNode, DiagramEdge and DotCluster are also exported as Node,
Edge and Cluster, and parse_dot and draw_graph as parse and
draw, their Rust names.
Graphs¶
Graph(direction="TD")
Graph.node(id, label=None, *, shape="rect") -> Graph
Graph.edge(source, target, *, label=None, stroke="solid", start=None, end="arrow",
min_length=1) -> Graph
Graph.link(source, target, *, label=None) -> Graph
Diagram(graph, *, ascii=None)
direction is "TD" (or "TB"), "BT", "LR" or "RL". Shapes are
rect, round, stadium, subroutine, cylinder, circle,
double_circle, asymmetric, rhombus, hexagon, parallelogram,
parallelogram_alt, trapezoid and trapezoid_alt; strokes solid,
thick, dotted and invisible; heads arrow, circle, cross or
None. An edge to an id with no node adds one labelled with its id, and
node on an existing id relabels it. link is an edge with no heads.
A Diagram copies the graph when it is made. It measures to the width of
its drawing; given less, it is cropped, never wrapped. ascii=True draws
with ASCII only; None follows the console.
from rs_rich.console import Console
from rs_rich import diagram
graph = (
diagram.Graph("LR")
.node("web", "Browser", shape="round")
.node("api", "API")
.node("db", "Postgres", shape="cylinder")
.edge("web", "api", label="HTTPS")
.edge("api", "db", label="reads")
)
console = Console(width=60, color_system=None)
console.print(diagram.Diagram(graph))
print(diagram.draw_graph(diagram.Graph().edge("a", "b"), ascii=True))
╭─────────╮ ┌─────┐ ╭──────────╮
│ Browser ├──HTTPS─►│ API ├──reads─►│ Postgres │
╰─────────╯ └─────┘ ╰──────────╯
['+---+', '| a |', '+-+-+', ' |', ' v', '+---+', '| b |', '+---+']
Graph.nodes and Graph.edges read the graph back (DiagramNode: id,
label, shape; DiagramEdge: source and target as node indexes,
label, stroke, start, end, length). A graph too large to lay out
(more than 2000 edges or 5000 nodes, checked before any layout work, or a
layout past its point or cell caps) renders as a one-line note;
draw_graph and Diagram.drawing() raise DiagramLayoutError instead.
DOT¶
Dot draws a DOT source with the same layout: digraph and graph,
rankdir, node shapes and labels, edge styles and labels, chains, and
clusters (drawn without their frames, with a note). What the parser does not
support (a node port, an HTML-like label, the record shape, ...) is shown
as the source under a note naming it and its line. parse_dot raises
DotError instead, with line and construct (None for a syntax error).
A source over 64 KB, with more than 500 nodes or 2000 edges (counted as
{ ... } groups expand), or nested more than 64 levels deep is refused the
same way, never drawn in part.
A DotGraph has graph, directed, strict, name, label, clusters
and notes.
console.print(diagram.Dot("digraph { rankdir=LR; build -> test -> ship }"))
try:
diagram.parse_dot("digraph {\n a -> b\n a:out -> c\n}")
except diagram.DotError as error:
print(error.line, error.construct)
╭───────╮ ╭──────╮ ╭──────╮
│ build ├─►│ test ├─►│ ship │
╰───────╯ ╰──────╯ ╰──────╯
3 a node port (`a:…`)
From the shell, rich dot FILE draws a DOT file and rich --markdown draws
```dot fences
(Diagrams guide).