Skip to main content

๐Ÿš€๐Ÿ€ PyRatatui โ€” Professional Python bindings for ratatui 0.30 ๐Ÿฆ€๐Ÿคฉ | ๐Ÿ’ฅ๐Ÿ”ฅ High-performance Rust TUI engine for building rich terminal applications, interactive CLIs, dashboards, and text-based UIs ๐Ÿ’ฏโœจ

Project description

๐Ÿ€ PyRatatui

Professional Python bindings for ratatui 0.30 โ€” powered by Rust & PyO3

PyPI version Python License: MIT Ratatui PyO3 Platforms asyncio

Build rich, high-performance terminal UIs in Python โ€” with the full power of Rust under the hood.

Quickstart ยท Installation ยท Widgets ยท Effects ยท Examples ยท API Reference ยท Docs


๐Ÿ–ผ๏ธ Gallery

Widget showcase Layout panels Styled text
List navigation Progress bars Dynamic table
Async reactive UI TachyonFX effects Effect DSL
Full app dashboard Popup widget Draggable popup
Scrollable popup TextArea editor ScrollView

What is PyRatatui?

PyRatatui exposes the entire ratatui Rust TUI library to Python via a thin, zero-overhead PyO3 extension module. You get:

  • Pixel-perfect terminal rendering from ratatui's battle-tested Rust layout engine
  • 30+ widgets out of the box: gauges, tables, trees, calendars, QR codes, images, markdown, and more
  • TachyonFX animations โ€” fade, sweep, glitch, dissolve, and composable effect pipelines
  • Async-native โ€” AsyncTerminal + asyncio integration for live, reactive UIs
  • Full type stubs โ€” every class and method ships with .pyi annotations for IDE autocomplete
  • Cross-platform โ€” Linux, macOS, and Windows (pre-built wheels on PyPI for all three)

Table of Contents


Installation

Recommended โ€” Pre-built Wheel

pip install pyratatui

Pre-built wheels are published to PyPI for:

Platform Architecture
Linux x86_64 (manylinux2014)
macOS x86_64 and arm64 (universal2)
Windows x86_64

If no wheel exists for your platform, pip will automatically compile from source (requires Rust โ€” see Building from Source).

Virtual Environment (Best Practice)

python -m venv .venv
source .venv/bin/activate        # Linux / macOS
# .venv\Scripts\activate         # Windows PowerShell

pip install pyratatui

Requirements

Requirement Minimum Notes
Python 3.10 3.11+ recommended
OS Linux, macOS, Windows crossterm backend
Rust 1.75 source builds only

Verify

import pyratatui
print(pyratatui.__version__)          # "0.2.2"
print(pyratatui.__ratatui_version__)  # "0.30"

Quickstart

Hello World

from pyratatui import Block, Color, Paragraph, Style, Terminal

with Terminal() as term:
    while True:
        def ui(frame):
            frame.render_widget(
                Paragraph.from_string("Hello, pyratatui! ๐Ÿ€  Press q to quit.")
                    .block(Block().bordered().title("Hello World"))
                    .style(Style().fg(Color.cyan())),
                frame.area,
            )
        term.draw(ui)
        ev = term.poll_event(timeout_ms=100)
        if ev and ev.code == "q":
            break

Output:

โ”Œ Hello World โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Hello, pyratatui! ๐Ÿ€  Press q to quit.                  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Scaffold a New Project

pyratatui init my_app
cd my_app
pip install -r requirements.txt
python main.py

Ultra-Minimal โ€” run_app Helper

from pyratatui import Paragraph, run_app

def ui(frame):
    frame.render_widget(
        Paragraph.from_string("Hello! Press q to quit."),
        frame.area,
    )

run_app(ui)

Core Concepts

Terminal & Frame

Terminal is the entry point. Use it as a context manager โ€” it saves the terminal state, enters alternate screen mode, enables raw input, and restores everything on exit (even after exceptions).

with Terminal() as term:
    term.draw(lambda frame: ...)    # render one frame
    ev = term.poll_event(timeout_ms=50)  # KeyEvent | None

Frame is passed to every render callback. It holds the drawable area and all render methods:

def ui(frame):
    area = frame.area  # Rect โ€” full terminal size
    frame.render_widget(widget, area)

Layout

Layout divides a Rect into child regions using constraints:

from pyratatui import Layout, Constraint, Direction

chunks = (
    Layout()
    .direction(Direction.Vertical)
    .constraints([
        Constraint.length(3),   # fixed 3 rows
        Constraint.fill(1),     # takes remaining space
        Constraint.length(1),   # fixed 1 row
    ])
    .split(frame.area)
)
header, body, footer = chunks

Constraint types:

Constraint Description
Constraint.length(n) Exactly n rows/columns
Constraint.percentage(pct) pct% of available space
Constraint.fill(n) Fill remaining space (proportionally weighted)
Constraint.min(n) At least n rows/columns
Constraint.max(n) At most n rows/columns
Constraint.ratio(num, den) Fractional proportion

Styling

All styling flows through Style, Color, and Modifier:

from pyratatui import Style, Color, Modifier

style = (
    Style()
    .fg(Color.cyan())
    .bg(Color.rgb(30, 30, 46))
    .bold()
    .italic()
)

# Named colors
Color.red()    Color.green()    Color.yellow()
Color.blue()   Color.magenta()  Color.cyan()
Color.white()  Color.gray()     Color.dark_gray()
# Light variants: Color.light_red(), Color.light_green(), ...
# 256-color: Color.indexed(42)
# True-color: Color.rgb(255, 100, 0)

Text Hierarchy

Text is composed bottom-up: Span โ†’ Line โ†’ Text:

from pyratatui import Text, Line, Span, Style, Color

text = Text([
    Line([
        Span("Status: ", Style().bold()),
        Span("OK", Style().fg(Color.green())),
        Span("  |  99.9%", Style().fg(Color.cyan())),
    ]),
    Line.from_string("Plain text line"),
    Line.from_string("Right-aligned").right_aligned(),
])

frame.render_widget(Paragraph(text).block(Block().bordered()), area)

Key Events

ev = term.poll_event(timeout_ms=100)
if ev:
    print(ev.code)   # "q", "Enter", "Up", "Down", "F1", etc.
    print(ev.ctrl)   # True if Ctrl held
    print(ev.alt)    # True if Alt held
    print(ev.shift)  # True if Shift held

# Common key codes
# Letters/digits: "a", "Z", "5"
# Special:        "Enter", "Esc", "Backspace", "Tab", "BackTab"
# Arrows:         "Up", "Down", "Left", "Right"
# Function:       "F1" โ€ฆ "F12"
# Ctrl+C:         ev.code == "c" and ev.ctrl

Tip โ€” Closure Capture: Always snapshot mutable state into default arguments to avoid late-binding issues in fast render loops:

count = state["count"]
def ui(frame, _count=count):  # โ† captured by value, not reference
    ...

Widget Reference

Standard Widgets

Widget Description
Paragraph Single or multi-line text, wrapping, scrolling
Block Bordered container with title, padding, and style
List + ListState Scrollable, selectable list
Table + TableState Multi-column table with header and footer
Gauge Filled progress bar
LineGauge Single-line progress indicator
BarChart Grouped vertical bar chart
Sparkline Inline sparkline trend chart
Scrollbar + ScrollbarState Attach scrollbars to any widget
Tabs Tabbed navigation bar
Clear Clears a rectangular area (use under popups)

Quick examples:

# Gauge
Gauge().percent(75).label("CPU 75%").style(Style().fg(Color.green()))

# Navigable list
list_state = ListState()
list_state.select(0)
items = [ListItem(s) for s in ["Alpha", "Beta", "Gamma"]]
frame.render_stateful_list(
    List(items).highlight_style(Style().fg(Color.yellow()).bold()),
    area, list_state,
)

# Table
header = Row.from_strings(["Name", "Status", "Uptime"])
rows = [Row.from_strings(["nginx", "โœ“ running", "14d"]), ...]
frame.render_stateful_table(
    Table(rows, [Constraint.fill(1)] * 3, header=header),
    area, table_state,
)

# Sparkline
Sparkline().data([10, 40, 20, 80, 55, 90]).max(100).style(Style().fg(Color.cyan()))

# Tabs
Tabs(["Overview", "Logs", "Config"]).select(1).highlight_style(Style().fg(Color.yellow()))

Third-Party Widgets

Widget Crate Description
Popup / PopupState tui-popup Centered or draggable popups
TextArea tui-textarea Full multi-line editor (Emacs keybindings, undo/redo)
ScrollView / ScrollViewState tui-scrollview Scrollable virtual viewport
QrCodeWidget tui-qrcode QR codes rendered in Unicode block characters
Monthly / CalendarDate ratatui widget-calendar Monthly calendar with event styling
BarGraph tui-bar-graph Gradient braille/block bar graphs
Tree / TreeState tui-tree-widget Collapsible tree view
TuiLoggerWidget tui-logger Live scrolling log viewer
ImageWidget / ImagePicker ratatui-image Terminal image rendering
Canvas ratatui Low-level line/point/rect drawing
Map ratatui World map widget
Button built-in Focus-aware interactive button

Third-party widget examples:

# Popup (stateful / draggable)
from pyratatui import Popup, PopupState, Style, Color

popup = Popup("Press any key to dismiss").title("Info").style(Style().bg(Color.blue()))
state = PopupState()
frame.render_stateful_popup(popup, frame.area, state)

# TextArea (multi-line editor)
from pyratatui import TextArea, Terminal

ta = TextArea.from_lines(["Hello", "World"])
ta.set_placeholder_text("Start typingโ€ฆ")
# inside loop:
frame.render_textarea(ta, area)
# forward key events:
ta.input_key(ev.code, ev.ctrl, ev.alt, ev.shift)

# QR Code
from pyratatui import QrCodeWidget, QrColors
qr = QrCodeWidget("https://ratatui.rs").colors(QrColors.Inverted)
frame.render_qrcode(qr, area)

# Calendar
from pyratatui import CalendarDate, CalendarEventStore, Monthly
store = CalendarEventStore.today_highlighted(Style().fg(Color.green()).bold())
cal = (Monthly(CalendarDate.today(), store)
       .show_month_header(Style().bold())
       .show_weekdays_header(Style().italic()))
frame.render_widget(cal, area)

# Gradient bar graph
from pyratatui import BarGraph, BarGraphStyle, BarColorMode
graph = (BarGraph([0.1, 0.4, 0.9, 0.6, 0.8])
         .bar_style(BarGraphStyle.Braille)
         .color_mode(BarColorMode.VerticalGradient)
         .gradient("turbo"))
frame.render_widget(graph, area)

# Markdown rendering
from pyratatui import markdown_to_text, Paragraph
text = markdown_to_text("# Hello\n\n**bold** _italic_ `code`")
frame.render_widget(Paragraph(text), area)

# Tree widget
from pyratatui import Tree, TreeItem, TreeState
items = [
    TreeItem("src", [TreeItem("main.rs"), TreeItem("lib.rs")]),
    TreeItem("Cargo.toml"),
]
tree = Tree(items).block(Block().bordered().title(" Files "))
state = TreeState()
frame.render_stateful_tree(tree, area, state)

TachyonFX Effects

PyRatatui ships the full tachyonfx effects engine. Effects are post-render transforms that mutate the frame buffer โ€” always apply them after rendering your widgets.

Effect Types

Effect Description
Effect.fade_from_fg(color, ms) Fade text from a color into its natural color
Effect.fade_to_fg(color, ms) Fade text out to a flat color
Effect.fade_from(bg, fg, ms) Fade both background and foreground from color
Effect.fade_to(bg, fg, ms) Fade both background and foreground to color
Effect.coalesce(ms) Characters materialize in from random positions
Effect.dissolve(ms) Characters scatter and dissolve
Effect.slide_in(direction, ms) Slide content in from an edge
Effect.slide_out(direction, ms) Slide content out to an edge
Effect.sweep_in(dir, span, grad, color, ms) Gradient sweep reveal
Effect.sweep_out(dir, span, grad, color, ms) Gradient sweep hide
Effect.sequence(effects) Run effects one after another
Effect.parallel(effects) Run effects simultaneously
Effect.sleep(ms) Delay before next effect in a sequence
Effect.repeat(effect, times=-1) Loop an effect (โˆ’1 = forever)
Effect.ping_pong(effect) Play an effect forward then backward
Effect.never_complete(effect) Keep an effect alive indefinitely

Interpolations

Interpolation.Linear, QuadIn/Out/InOut, CubicIn/Out/InOut, SineIn/Out/InOut, CircIn/Out/InOut, ExpoIn/Out/InOut, ElasticIn/Out, BounceIn/Out/BounceInOut, BackIn/Out/BackInOut

Basic Effect Usage

import time
from pyratatui import Effect, EffectManager, Interpolation, Color, Terminal, Paragraph

mgr = EffectManager()
mgr.add(Effect.fade_from_fg(Color.black(), 1000, Interpolation.SineOut))
last = time.monotonic()

with Terminal() as term:
    while not (ev := term.poll_event(timeout_ms=16)) or ev.code != "q":
        now = time.monotonic()
        elapsed_ms = int((now - last) * 1000)
        last = now

        def ui(frame, _mgr=mgr, _ms=elapsed_ms):
            # Step 1 โ€” render widgets
            frame.render_widget(Paragraph.from_string("Fading inโ€ฆ"), frame.area)
            # Step 2 โ€” apply effects to the same buffer
            frame.apply_effect_manager(_mgr, _ms, frame.area)

        term.draw(ui)

Effect DSL

Compile tachyonfx expressions at runtime โ€” perfect for config-driven or user-customisable animations:

from pyratatui import compile_effect, EffectManager

# DSL mirrors the Rust / tachyonfx expression syntax
effect = compile_effect("fx::coalesce(500)")
effect = compile_effect("fx::dissolve((800, BounceOut))")
effect = compile_effect("fx::fade_from_fg(Color::Black, (600, QuadOut))")
effect = compile_effect("fx::sweep_in(LeftToRight, 10, 5, Color::Black, (700, SineOut))")

mgr = EffectManager()
mgr.add(effect)

Cell Filters

Target effects at specific cells:

from pyratatui import CellFilter, Effect, Color

effect = Effect.fade_from_fg(Color.black(), 800)
effect.with_filter(CellFilter.text())                           # text cells only
effect.with_filter(CellFilter.inner(horizontal=1, vertical=1)) # inner area
effect.with_filter(CellFilter.fg_color(Color.cyan()))          # specific fg color
effect.with_filter(CellFilter.any_of([CellFilter.text(), CellFilter.all()]))

Async & Reactive UIs

Use AsyncTerminal to combine rendering with background asyncio tasks:

import asyncio
from pyratatui import AsyncTerminal, Gauge, Block, Style, Color

state = {"progress": 0}

async def background_worker():
    while state["progress"] < 100:
        await asyncio.sleep(0.1)
        state["progress"] += 2

async def main():
    worker = asyncio.create_task(background_worker())

    async with AsyncTerminal() as term:
        async for ev in term.events(fps=30):
            pct = state["progress"]

            def ui(frame, _pct=pct):
                frame.render_widget(
                    Gauge()
                    .percent(_pct)
                    .label(f"Loadingโ€ฆ {_pct}%")
                    .style(Style().fg(Color.green()))
                    .block(Block().bordered().title("Progress")),
                    frame.area,
                )

            term.draw(ui)

            if ev and ev.code == "q":
                break
            if pct >= 100:
                break

    worker.cancel()

asyncio.run(main())

AsyncTerminal.events() Parameters

async for ev in term.events(fps=30.0, stop_on_quit=True):
    # ev is KeyEvent | None
    # None emitted each tick (use for animations / periodic updates)
    # stop_on_quit=True exits the loop automatically on "q"

run_app / run_app_async Helpers

For simpler apps that don't need manual task management:

from pyratatui import run_app, run_app_async, Paragraph

# Synchronous
run_app(lambda frame: frame.render_widget(
    Paragraph.from_string("Hello!"), frame.area
))

# Asynchronous
import asyncio

async def main():
    tick = 0
    def ui(frame):
        nonlocal tick
        frame.render_widget(Paragraph.from_string(f"Tick: {tick}"), frame.area)
        tick += 1
    await run_app_async(ui, fps=30, on_key=lambda ev: ev.code == "q")

asyncio.run(main())

CLI Tool

PyRatatui ships a pyratatui CLI for project scaffolding and version inspection.

Usage: pyratatui [COMMAND]

Commands:
  init     Create a new PyRatatui project scaffold
  version  Show PyRatatui version

Options:
  --help   Show help message

pyratatui init

pyratatui init my_tui_app [--verbose]

Creates a ready-to-run project:

my_tui_app/
โ”œโ”€โ”€ main.py           # runnable hello world starter
โ”œโ”€โ”€ requirements.txt  # pyratatui dependency
โ””โ”€โ”€ README.md         # project docs
cd my_tui_app
pip install -r requirements.txt
python main.py

pyratatui version

pyratatui version
# PyRatatui 0.2.2

API Reference

Terminal

class Terminal:
    def __enter__(self) -> Terminal
    def __exit__(self, ...) -> bool
    def draw(self, draw_fn: Callable[[Frame], None]) -> None
    def poll_event(self, timeout_ms: int = 0) -> KeyEvent | None
    def area(self) -> Rect
    def clear(self) -> None
    def hide_cursor(self) -> None
    def show_cursor(self) -> None
    def restore(self) -> None

AsyncTerminal

class AsyncTerminal:
    async def __aenter__(self) -> AsyncTerminal
    async def __aexit__(self, ...) -> bool
    def draw(self, draw_fn: Callable[[Frame], None]) -> None
    async def poll_event(self, timeout_ms: int = 50) -> KeyEvent | None
    async def events(self, fps: float = 30.0, *, stop_on_quit: bool = True) -> AsyncIterator[KeyEvent | None]
    def area(self) -> Rect
    def clear(self) -> None
    def hide_cursor(self) -> None
    def show_cursor(self) -> None

Frame

class Frame:
    @property
    def area(self) -> Rect

    # Standard widgets (stateless)
    def render_widget(self, widget: object, area: Rect) -> None

    # Stateful widgets
    def render_stateful_list(self, widget: List, area: Rect, state: ListState) -> None
    def render_stateful_table(self, widget: Table, area: Rect, state: TableState) -> None
    def render_stateful_scrollbar(self, widget: Scrollbar, area: Rect, state: ScrollbarState) -> None

    # Popups
    def render_popup(self, popup: Popup, area: Rect) -> None
    def render_stateful_popup(self, popup: Popup, area: Rect, state: PopupState) -> None

    # Text editor
    def render_textarea(self, ta: TextArea, area: Rect) -> None

    # Scroll view
    def render_stateful_scrollview(self, sv: ScrollView, area: Rect, state: ScrollViewState) -> None

    # QR code
    def render_qrcode(self, qr: QrCodeWidget, area: Rect) -> None

    # Effects
    def apply_effect(self, effect: Effect, elapsed_ms: int, area: Rect) -> None
    def apply_effect_manager(self, manager: EffectManager, elapsed_ms: int, area: Rect) -> None

    # Prompts
    def render_text_prompt(self, prompt: TextPrompt, area: Rect, state: TextState) -> None
    def render_password_prompt(self, prompt: PasswordPrompt, area: Rect, state: TextState) -> None

Layout & Geometry

class Layout:
    def constraints(self, constraints: list[Constraint]) -> Layout
    def direction(self, direction: Direction) -> Layout
    def margin(self, margin: int) -> Layout
    def spacing(self, spacing: int) -> Layout
    def flex_mode(self, mode: str) -> Layout
    def split(self, area: Rect) -> list[Rect]

class Rect:
    x: int;  y: int;  width: int;  height: int
    right: int;  bottom: int;  left: int;  top: int
    def area(self) -> int
    def inner(self, horizontal: int = 1, vertical: int = 1) -> Rect
    def contains(self, other: Rect) -> bool
    def intersection(self, other: Rect) -> Rect | None
    def union(self, other: Rect) -> Rect

Style

class Style:
    def fg(self, color: Color) -> Style
    def bg(self, color: Color) -> Style
    def bold(self) -> Style
    def italic(self) -> Style
    def underlined(self) -> Style
    def dim(self) -> Style
    def reversed(self) -> Style
    def hidden(self) -> Style
    def crossed_out(self) -> Style
    def slow_blink(self) -> Style
    def rapid_blink(self) -> Style
    def patch(self, other: Style) -> Style
    def add_modifier(self, modifier: Modifier) -> Style
    def remove_modifier(self, modifier: Modifier) -> Style

Block

class Block:
    def title(self, title: str) -> Block
    def title_bottom(self, title: str) -> Block
    def bordered(self) -> Block                          # all four borders
    def borders(self, top, right, bottom, left) -> Block
    def border_type(self, bt: BorderType) -> Block       # Plain | Rounded | Double | Thick
    def style(self, style: Style) -> Block
    def border_style(self, style: Style) -> Block
    def title_style(self, style: Style) -> Block
    def padding(self, left, right, top, bottom) -> Block
    def title_alignment(self, alignment: str) -> Block

Prompts

# Blocking single-line text prompt (runs its own event loop)
value: str | None = prompt_text("Enter your name: ")
password: str | None = prompt_password("Password: ")

# Stateful inline prompts
state = TextState()
frame.render_text_prompt(TextPrompt("Search: "), area, state)
if state.is_complete():
    print(state.value())

Exceptions

Exception When raised
PyratatuiError Base exception for all library errors
BackendError Terminal backend failure
LayoutError Invalid layout constraint or split
RenderError Widget render failure
AsyncError Async / thread misuse
StyleError Invalid style combination

Examples

The examples/ directory contains 33 standalone, runnable scripts. Run any of them directly:

python examples/01_hello_world.py
python examples/07_async_reactive.py
python examples/08_effects_fade.py
# File Demonstrates
01 01_hello_world.py Terminal, Paragraph, Block, Style, Color
02 02_layout.py Layout, Constraint, Direction, nested splits
03 03_styled_text.py Span, Line, Text, Modifier
04 04_list_navigation.py List, ListState, keyboard navigation
05 05_progress_bar.py Gauge, LineGauge, time-based animation
06 06_table_dynamic.py Table, Row, Cell, TableState
07 07_async_reactive.py AsyncTerminal, live background metrics
08 08_effects_fade.py Effect.fade_from_fg, EffectManager
09 09_effects_dsl.py compile_effect(), DSL syntax
10 10_full_app.py Full production app: tabs, async, effects
11 11_popup_basic.py Popup โ€” basic centered popup
12 12_popup_stateful.py PopupState โ€” draggable popup
13 13_popup_scrollable.py KnownSizeWrapper โ€” scrollable popup content
14 14_textarea_basic.py TextArea โ€” basic multi-line editor
15 15_textarea_advanced.py TextArea โ€” modal vim-style editing
16 16_scrollview.py ScrollView, ScrollViewState
17 17_qrcode.py QrCodeWidget, QrColors
18 18_async_progress.py Async live progress with asyncio.Task
19 19_effects_glitch.py dissolve / coalesce glitch animation
20 20_effects_matrix.py sweep_in / sweep_out matrix-style
21 21_prompt_confirm.py Yes/No confirmation prompt
22 22_prompt_select.py Arrow-key selection menu
23 23_prompt_text.py TextPrompt, TextState
24 24_dashboard.py Full dashboard: Tabs, BarChart, Sparkline
25 25_calendar.py Monthly, CalendarDate, CalendarEventStore
26 26_bar_graph.py BarGraph, gradient styles
27 27_tree_widget.py Tree, TreeState, collapsible nodes
28 28_markdown_renderer.py markdown_to_text()
29 29_logger_demo.py TuiLoggerWidget, init_logger
30 30_image_view.py ImagePicker, ImageWidget, ImageState
31 31_canvas_drawing.py Canvas โ€” lines, points, rectangles
32 32_map_widget.py Map, MapResolution
33 33_button_widget.py Button โ€” focus state, key handling

Building from Source

Prerequisites

# 1. Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source "$HOME/.cargo/env"
rustup update stable

# 2. Install Maturin
pip install maturin

Development Build

git clone https://github.com/pyratatui/pyratatui.git
cd pyratatui

# Editable install โ€” fast compile, slower runtime
maturin develop

# Release build โ€” full Rust optimizations (recommended for benchmarking/use)
maturin develop --release

After changing Rust source files, re-run maturin develop to rebuild the extension. Python files in python/pyratatui/ are reflected immediately with no rebuild.

Build a Distributable Wheel

maturin build --release
# Wheel output: target/wheels/pyratatui-*.whl
pip install target/wheels/pyratatui-*.whl

Format & Lint

# Linux / macOS
./scripts/format.sh

# Windows
./scripts/format.ps1

# Python only (ruff + mypy)
ruff check .
ruff format .
mypy python/

Tests

# Python tests (pytest)
pytest tests/python/

# Rust unit tests
cargo test

Docker (source build)

FROM python:3.12-slim
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
ENV PATH="/root/.cargo/bin:${PATH}"
RUN pip install pyratatui

Platform Notes

Windows

Requires Windows Terminal or VS Code integrated terminal (Windows 10 build 1903+ for VT sequence support). The classic cmd.exe may not render all Unicode characters correctly.

macOS

Default Terminal.app works but has limited colour support. iTerm2 or Alacritty are recommended for true-colour and full Unicode rendering.

Linux

Any modern terminal emulator works. Verify true-colour support:

echo $COLORTERM   # should output "truecolor" or "24bit"

Troubleshooting

ModuleNotFoundError: No module named 'pyratatui._pyratatui' The native extension was not compiled. Run maturin develop --release or reinstall via pip install --force-reinstall pyratatui.

PanicException: pyratatui::terminal::Terminal is unsendable You called a Terminal method from a thread-pool thread. Use AsyncTerminal instead.

Garbage on screen after Ctrl-C Always use Terminal as a context manager. For emergency recovery: reset or stty sane in your shell.

ValueError: Invalid date CalendarDate.from_ymd(y, m, d) raises ValueError for invalid dates (e.g. Feb 30). Validate inputs first.


Contributing

Contributions are welcome! Here's how to get started:

  1. Fork the repository on GitHub
  2. Clone your fork and create a branch: git checkout -b feature/my-feature
  3. Install dev dependencies:
    pip install -e ".[dev]"
    maturin develop
    
  4. Make your changes โ€” Rust source lives in src/, Python in python/pyratatui/
  5. Run tests and linters:
    pytest tests/python/
    cargo test
    ruff check . && ruff format .
    mypy python/
    
  6. Open a Pull Request against main

Please follow the existing code style. For significant changes, open an issue first to discuss your approach.

Documentation

Docs are built with MkDocs Material:

pip install -e ".[docs]"
mkdocs serve          # local preview at http://localhost:8000
mkdocs build          # static site in site/

License

MIT ยฉ 2025 PyRatatui contributors โ€” see LICENSE for full text.


Built with ๐Ÿฆ€ ratatui ยท โšก tachyonfx ยท ๐Ÿ PyO3

GitHub ยท PyPI ยท Docs ยท Issues

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pyratatui-0.2.2.tar.gz (1.1 MB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

pyratatui-0.2.2-cp310-abi3-win_amd64.whl (2.9 MB view details)

Uploaded CPython 3.10+Windows x86-64

pyratatui-0.2.2-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.0 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.17+ x86-64

pyratatui-0.2.2-cp310-abi3-macosx_11_0_arm64.whl (2.7 MB view details)

Uploaded CPython 3.10+macOS 11.0+ ARM64

pyratatui-0.2.2-cp310-abi3-macosx_10_12_x86_64.whl (2.9 MB view details)

Uploaded CPython 3.10+macOS 10.12+ x86-64

File details

Details for the file pyratatui-0.2.2.tar.gz.

File metadata

  • Download URL: pyratatui-0.2.2.tar.gz
  • Upload date:
  • Size: 1.1 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pyratatui-0.2.2.tar.gz
Algorithm Hash digest
SHA256 e2ba69978afb8326f6a0c7c04da8c9375257953a2ca3cdef18724593daf284f8
MD5 d123725a4b06d6e3429bae18973e9ff9
BLAKE2b-256 b8eb91ba099410a356fd7440f15cc856ce82cf0986d799148277e7bf044814c1

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyratatui-0.2.2.tar.gz:

Publisher: ci.yml on pyratatui/pyratatui

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyratatui-0.2.2-cp310-abi3-win_amd64.whl.

File metadata

  • Download URL: pyratatui-0.2.2-cp310-abi3-win_amd64.whl
  • Upload date:
  • Size: 2.9 MB
  • Tags: CPython 3.10+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pyratatui-0.2.2-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 02dbe0e36ecaac7a6307450c044c74c52b94f1529b3337e10b0cf40dcfb7e670
MD5 6ccd25dd5f7bfc81a7aa3d0b1c6b806b
BLAKE2b-256 328357fd273ff989a314098e5fb0bf0c90a2afd1ac3e6c3911d9423ebfeed62b

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyratatui-0.2.2-cp310-abi3-win_amd64.whl:

Publisher: ci.yml on pyratatui/pyratatui

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyratatui-0.2.2-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pyratatui-0.2.2-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 cb8979f6d39402cfdc22e21f749bb3d089057eb3b2ce1537f7a50b02928e2b07
MD5 532d452f2a320a1d84bfb1b941703185
BLAKE2b-256 0a0b44aceab40fb14c30672cc058ec68a4d9cc4e9ce8b82f77b3f22546f57a8e

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyratatui-0.2.2-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: ci.yml on pyratatui/pyratatui

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyratatui-0.2.2-cp310-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for pyratatui-0.2.2-cp310-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 f1b5aa492fad6c4418152cd97ea7b22f6cf8e110797fc46bbeb96ed3a23b1eff
MD5 85c3debc3f31d441c924aebca7bf126b
BLAKE2b-256 b0018333709a9461b173b84acf67193a88391060e47c1929bbedc1148910ca6e

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyratatui-0.2.2-cp310-abi3-macosx_11_0_arm64.whl:

Publisher: ci.yml on pyratatui/pyratatui

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyratatui-0.2.2-cp310-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for pyratatui-0.2.2-cp310-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 743223126050d812d80c25fd5c890b16ddc23ada5502c2cf78c29fd2855212f7
MD5 f932c4cca18a0aa5c0b4ec3c37e8aeff
BLAKE2b-256 64c0890a0d487e92aed377eff449fb932c267342c572117cef1fa54bde0dbcba

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyratatui-0.2.2-cp310-abi3-macosx_10_12_x86_64.whl:

Publisher: ci.yml on pyratatui/pyratatui

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page