13 KiB
Python TUI frameworks for Fenris
Research snapshot: 2026-08-31. Planning only; no product implementation.
Decision
Use Textual for Fenris's later TUI prototype and, if the prototype checks below pass, for the persistent TUI.
Textual is the best fit for Fenris's keyboard-first Overview, Usage History, Drive Health, and Settings views because one framework supplies flexible grid/horizontal/vertical layouts, dashboard and form widgets, background workers, and a headless interaction driver (layout, widget gallery, workers, testing). Its principal costs are the largest direct dependency set in this shortlist and only a compact Sparkline as built-in charting; detailed historical plots would need a custom widget or the first-party textual-plotext integration (PyPI metadata, Sparkline, textual-plotext).
This recommendation is conditional on raising Fenris's Python floor from its currently documented Python 3.7+ to Python 3.9+: Textual 8.2.8 and Urwid 4.0.13 require Python 3.9+, while prompt_toolkit 3.0.53 requires Python 3.10+ (Fenris README, Textual metadata, Urwid metadata, prompt_toolkit metadata). If retaining Python 3.7 is mandatory, none of the current versions evaluated here qualifies.
Fallback: choose Urwid if prototype evidence shows that explicit low-level terminal/display control or a smaller direct dependency set matters more than Textual's higher-level layouts, forms, workers, and test driver (display modules, package metadata).
Scope and method
The shortlist covers maintained, full-screen-capable Python projects with first-party application/widget documentation: Textual, Urwid, and prompt_toolkit. The evaluation uses official documentation, repository release APIs, and PyPI package metadata. Versions, Python floors, dependencies, and release activity are a point-in-time snapshot and should be rechecked when dependencies are locked.
Comparison
| Criterion | Textual 8.2.8 | Urwid 4.0.13 | prompt_toolkit 3.0.53 |
|---|---|---|---|
| Terminal compatibility | PyPI classifies Linux, macOS, and Windows 10/11 support; this exceeds Fenris's Linux-only scope (metadata). | Offers pure-Python raw and OS curses displays. The manual compares UTF-8, color, mouse, and external-event-loop capabilities; curses is described as broadly terminal-compatible, while raw supports 88/256/24-bit color and external loops (display modules). | Provides full-screen applications and platform-specific POSIX/Windows test input; official input docs describe cross-platform one-key-at-a-time input and Windows behavior (full-screen apps, input, testing). |
| Responsive layout | Vertical, horizontal, and grid layouts support fractional, percentage, and automatic sizing, nesting, spans, overflow, docking, and runtime layout changes (layout). | Pile, Columns, GridFlow, Overlay, ListBox, padding, and flow/box/fixed sizing provide procedural composition (widgets). |
VSplit, HSplit, FloatContainer, ConditionalContainer, and scrollable panes compose full-screen regions, but the reviewed guide does not document breakpoint-style responsiveness (full-screen apps). |
| Dashboard, charts, history | Built-ins include DataTable, ProgressBar, Digits, and Sparkline; Sparkline summarizes reactive numerical data into bars according to available widget width (gallery, Sparkline). textual-plotext adds a PlotextPlot wrapper for richer plotting (repository). |
Includes BarGraph, GraphVScale, and ProgressBar; richer history views still require composition or custom rendering (graph widgets). |
The documented reusable widget set supports text areas, buttons, frames, dialogs, and menus, but does not list a chart widget; Fenris charts would therefore be custom controls/rendering (widget reference). |
| Forms and keyboard use | Built-ins include Input, MaskedInput, Select, checkbox/radio/selection controls, switches, buttons, and text areas (gallery). Input supports validation on change, blur, or submit and emits results with its messages (Input). |
Supplies Edit, Button, CheckBox, RadioButton, focus handling, and keyboard/mouse event support; application code owns more form orchestration (widgets). |
Strong key-binding, editing, completion, history, suggestion, and validation facilities; full-screen reusable components include TextArea and Button (input, full-screen apps). |
| Async/background updates | Async workers run coroutines concurrently; thread workers cover blocking APIs. Workers support cancellation and exclusivity, while thread-originated UI updates use call_from_thread() or thread-safe messages (workers). |
Supports Select, asyncio, Twisted, GLib, Tornado, and ZMQ event loops plus alarms and watched files; executor use varies by loop (main loop). | Uses asyncio natively and supports Application.run_async() inside an existing event loop (asyncio). |
| Testability | App.run_test() runs headlessly and returns a Pilot that can press keys, click, wait for queued messages, and run at specified terminal sizes (testing). |
The public manuals reviewed do not expose a comparable application pilot; tests would be built around widgets, rendering, callbacks, and event-loop seams (widgets, main loop). | Official testing guidance supplies platform-specific pipe input, DummyOutput, and app sessions for asserting results or data changes, while warning against brittle stdout-byte assertions (unit testing). |
| Direct dependency cost | Five required distributions: markdown-it-py[linkify], mdit-py-plugins, platformdirs, pygments, and rich; syntax-tree packages are optional-extra dependencies (metadata). |
Two required distributions: wcwidth and typing-extensions; curses, GLib, Tornado, Trio, Twisted, ZMQ, serial, and LCD integrations are optional extras (metadata). |
One required distribution: wcwidth (metadata). |
| Python floor | >=3.9,<4.0 (metadata). |
>=3.9.0 (metadata). |
>=3.10 (metadata). |
| Project health signal | Five non-draft releases from 8.2.4 through 8.2.8 were published between 2026-04-19 and 2026-06-30 (release API). | Five releases from 4.0.9 through 4.0.13 were published between 2026-08-14 and 2026-08-25 (release API). | Release 3.0.53 was published 2026-07-26, following 3.0.52 on 2025-08-27 (release API). |
All three show recent release activity, so maintenance does not eliminate a candidate. Textual wins on integrated application-level capabilities rather than on release recency alone.
Fit to the four views
- Overview: use Textual layout containers with
Digits, labels, progress indicators, and compact sparklines for monitoring status, usage-adjusted theoretical lifespan, projection confidence, and recent activity (layout, gallery). - Usage History: prototype a
DataTableplusSparklinefirst. Addtextual-plotextonly if user testing establishes a need for axes, multiple series, or richer plots, keeping plotting out of the core dependency decision until then (DataTable, Sparkline,textual-plotext). - Drive Health: use a table and status/progress widgets for NVMe attributes; wording must continue to distinguish endurance projection from a hardware-failure prediction (gallery, parent-map destination and terminology).
- Settings: Textual's selection, boolean, and validated text-entry controls cover a keyboard-only form without inventing controls (gallery, Input validation).
The TUI should consume snapshots from the independent collector rather than run NVMe collection in its UI loop; the ticket's parent map makes that independent-collector architecture part of the destination (parent map). Textual workers remain useful for non-blocking database/IPC reads and cancellable refreshes (workers).
Prototype acceptance checks
The later prototype should verify:
- Complete keyboard-only operation and predictable focus order across all four views.
- Reflow or deliberate scrolling at 80×24 and a representative wide terminal; Textual's test runner accepts explicit terminal sizes (testing).
- Smooth refresh while database/IPC reads are delayed, plus clean cancellation and shutdown (workers).
- Headless tests for view switching, settings validation, stale/error states, and collector disappearance (testing, Input).
- Readable behavior in the actual Linux terminal matrix, including SSH and tmux/screen if those are required; framework platform labels alone do not establish every terminal's behavior.
- Usage History performance with realistic observation volume and gaps.
- A table/text equivalent for every chart so observation gaps and projection confidence are not encoded by color or graphics alone.
- Installation with Fenris's chosen packaging mechanism and the agreed Python floor.
Newly surfaced questions
Do not open follow-up tickets during this research resolution; carry these into the next Wayfinder session:
- May the redesign raise Fenris's minimum Python version from 3.7 to 3.9?
- What is the minimum supported terminal contract: modern color terminals only, or also Linux virtual console, monochrome/16-color, SSH, tmux, and screen?
- Is a table plus sparkline sufficient for Usage History, or are axes, multiple series, zoom, and interval selection required?
- Should richer plotting be an optional install extra?
- What is the small-terminal policy: reflow, hide secondary content, scroll, or reject below a documented size?
- Which accessibility targets are required, including screen readers, reduced color, high contrast, and reduced animation?
- Does the TUI read observation storage directly or use an IPC snapshot API from the collector?
- Which settings are user-owned versus system/collector-owned, and which changes require privilege escalation?
- Are semantic interaction assertions sufficient, or are deterministic visual snapshots also required?