Skip to content

termopy

Terminal user interfaces as pure functions from state to an immutable view tree.

from termopy import UI, View, run


def app(ui: UI) -> View:
    count, set_count = ui.state(0)

    ui.on_event(lambda e: set_count(count + 1))

    return View.text(f"pressed {count} times").center(within=ui.size)


run(app)

Your app is a function. It runs again whenever something changes, and returns a picture of what should be on screen. There is no widget tree to keep in sync, because there is nothing kept between frames except the values you asked for by name.

Why you might want this

Rendering is a pure function. app(ui) -> View has no side effects on the screen. You can call it in a test, at any size, and assert on what came out. There is no event loop to start and no snapshot to approve.

from termopy.testing import render, to_text
from termopy.view import Size

assert "pressed 0 times" in to_text(render(app, size=Size(40, 10)))

A View is a value. It is an immutable grid of styled cells that composes with hcat, vcat and zcat. Two views side by side is a function call.

State is local and explicit. ui.state is a hook keyed by call order, like React's. A widget that needs to remember something asks for it; a widget that does not, does not.

No stylesheet. Styles are Style values in Python. Layout is arithmetic. Two real applications were ported to termopy without wanting a layout engine. See the comparison for what that cost and where it hurt.

Why you might not

termopy is small on purpose, and that is a real constraint:

  • POSIX only. It uses termios and tty; there is no Windows support.
  • Far fewer widgets than Textual. About twenty, against Textual's several dozen. No tree-with-checkboxes, no tabbed content, no data table with sorting.
  • No CSS, devtools, or web target. To restyle an app without touching Python, or serve it over HTTP, use Textual.
  • Young. Two applications have been ported to it. That is enough to have found real bugs, and not enough to have found all of them.

If you want the largest, most featureful terminal framework in Python, use Textual. termopy is for people who want a small core they can hold in their head, and whose UI code is testable without a harness.

Where to go next