Python bindings¶
rs-rich on PyPI puts the Rust port behind Rich's Python API. A Rich program
moves over by changing its imports. The rendering, down to the last byte, is
the Rust port's.
from rs_rich.console import Console # was: from rich.console import Console
from rs_rich.table import Table # was: from rich.table import Table
table = Table(title="Star Wars Movies")
table.add_column("Released", justify="right", style="cyan", no_wrap=True)
table.add_column("Title", style="magenta")
table.add_row("Dec 20, 2019", "Star Wars: The Rise of Skywalker")
Console(width=50).print(table)
Star Wars Movies
┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Released ┃ Title ┃
┡━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ Dec 20, 2019 │ Star Wars: The Rise of │
│ │ Skywalker │
└──────────────┴─────────────────────────────────┘
The package is rs-rich and it imports as rs_rich. It never claims the
rich namespace, so both can be installed side by side. New here? Start with
Getting started: installing, a first program, and moving
a Rich program over.
The API¶
The package covers all of Rich 15.0.0's API, plus the port's own crates. The modules have Rich's names, so imports translate one for one, and every page below has runnable examples whose output is checked by the tests.
Rich's modules¶
| Rich | rs_rich | Reference |
|---|---|---|
rich.print, rich.get_console, rich.reconfigure, rich.print_json, rich.inspect |
the same names on rs_rich |
Console |
rich.console (Console, ConsoleOptions, Capture, Group, group, ...) |
rs_rich.console |
Console |
rich.segment, rich.measure, __rich__, __rich_console__, __rich_measure__ |
rs_rich.segment, rs_rich.measure, the same methods |
The render protocol |
rich.text (Text, Span, Lines), rich.emoji |
rs_rich.text, rs_rich.emoji |
Text |
rich.style, rich.theme |
rs_rich.style, rs_rich.theme |
Style |
rich.color |
rs_rich.color |
Color |
rich.box, rich.markup, rich.errors, rich.terminal_theme |
rs_rich.box, rs_rich.markup, rs_rich.errors, rs_rich.terminal_theme |
Boxes, markup and errors |
rich.table |
rs_rich.table |
Table |
rich.panel |
rs_rich.panel |
Panel |
rich.rule, rich.padding, rich.align, rich.constrain, rich.styled, rich.bar, rich.spinner |
the same under rs_rich |
Rules, padding, alignment and bars |
rich.columns, rich.containers, rich.layout |
the same under rs_rich |
Layout, columns and groups |
rich.tree |
rs_rich.tree |
Tree |
rich.markdown |
rs_rich.markdown |
Markdown |
rich.syntax |
rs_rich.syntax |
Syntax |
rich.pretty, rich.json, rich.highlighter (and rich.inspect) |
the same under rs_rich |
Pretty, JSON, inspect and highlighters |
rich.traceback |
rs_rich.traceback |
Traceback |
rich.live, rich.live_render, rich.status, rich.screen, rich.pager |
the same under rs_rich |
Live, status, screen and pager |
rich.progress, rich.progress_bar |
rs_rich.progress, rs_rich.progress_bar |
Progress |
rich.prompt |
rs_rich.prompt |
Prompts |
rich.logging |
rs_rich.logging |
Logging |
Your own classes render as they do with Rich, through __rich__,
__rich_console__ and __rich_measure__, anywhere a renderable goes.
The port's own crates¶
| Crate | rs_rich | Reference |
|---|---|---|
rs-rich-ext (39 modules: diagnostics, data, diffs, workflows, tables, terminals, frames, testing and QA, ...) |
rs_rich.ext, rs_rich.ext.<module> |
Extensions |
rs-rich-interact (pickers, input, confirmations, forms, a pager; headless runs; fuzzy matching) |
rs_rich.interact |
Interactive components |
rs-rich-art (images, FIGlet, GIFs, image diffs) |
rs_rich.art |
Art |
rs-rich-ext's charts (sparklines, bars, line charts, gauges, heatmaps, status matrices, KPI cards, timelines) |
rs_rich.chart |
Charts |
rs-rich-diagram (graphs, their layout, DOT) |
rs_rich.diagram |
Diagrams |
rs-rich-mermaid |
rs_rich.mermaid |
Mermaid |
rs-rich-micro (emoji-sized inline images written :micro:name:) |
rs_rich.micro |
Micro assets |
rs-rich-plugin-api and the extension registry |
rs_rich.plugins |
Plugins |
rs-rich-cli (the rich command) |
python -m rs_rich, the rich-rs script, rs_rich.cli.main |
The command line |
The little Rich has that the port cannot do (Jupyter output) raises
NotImplementedError rather than rendering something different from Rich.
Compatibility lists what is covered, the known
differences, and how the byte comparison with Rich 15.0.0 works.
Every class ships with type stubs (rs_rich/_native.pyi), so editors and type
checkers see the signatures documented here.
How it works¶
- All rendering is Rust. The Python modules only re-export classes from
the compiled
rs_rich._nativemodule. - The native module is glue. It converts Python arguments into the core
richcrate's types: character offsets into byte offsets, keyword styles into a style definition. It then writes core's output to the console'sfile. - Objects are specifications. A
TableorPanelstores what it was given and becomes a core object only when printed, so a table can still gain rows after it has been put in a panel. - Your objects render in place. When core reaches one of your objects
(in a table cell, say), it calls back into Python for its
__rich_console__or__rich_measure__, with the GIL held and no lock taken, and an exception raised there comes out ofprint.
Wheels and releases¶
- Wheels. One abi3 wheel per platform covers CPython 3.9 and later: Linux
(x86-64 and arm64, manylinux), macOS (arm64 and x86-64) and Windows
(x86-64). The
lumis(tree-sitter) code highlighter is a separate, larger build (--features lumis); Mermaid'smmdcbackend needs--features mmdcand Mermaid's own CLI. Thepythonworkflow builds each wheel on every change to core or the bindings, then installs it and renders with it on Python 3.9 and 3.13. - Releases. A
python-vX.Y.Ztag onmainrunspypi-release.yml. It checks the tag againstpyproject.toml's version, builds the wheels and the sdist, runs the whole test suite (compatibility with Rich 15.0.0 included) against the Linux x86-64 wheel, and only then publishes them with PyPI Trusted Publishing from thepypienvironment, with no token secret. See Branching and releases. - Versions. The package has its own version, starting at 0.0.1. It
bundles the Rust crates from its tag's commit, and
crates/rich-pyis never published to crates.io.
Building from source¶
python -m venv .venv && . .venv/bin/activate
pip install maturin "rich==15.0.0" pytest
cd crates/rich-py
maturin develop
pytest tests
The tests need Rich 15.0.0 only for the byte comparison (and Pillow, if
installed, for the art tests that take Pillow images). Install it in its own
virtualenv, never alongside rich-cli (see AGENTS.md). tests/test_cli.py
compares python -m rs_rich with the rich binary: it builds it with cargo,
or uses RS_RICH_CLI_BIN.