Skip to main content

Deadpan state-bureau TUI library for Typer CLIs — framed forms, decision stamps, themed help, ASCII header, live jobs ticker.

Project description

Leia em Português

CI PyPI Python versions License: AGPL-3.0-or-later

glory-to-protocol — Bureau of Computational Technology

СВОДКА · TL;DR

A Python TUI library for Typer CLIs that wraps your commands in a deadpan, state-bureau aesthetic — framed forms, four decision stamps (approve / reject / order / review), themed --help, an ASCII logo header, and a live ticker for background jobs. Built because useful tools and funny tools rarely overlap.

protocol-showcase — all TUI components rendered

ОГЛАВЛЕНИЕ · Index

ПОЛЕ № 1 · Purpose

A Python TUI library for Typer CLIs — forms with framed borders, four kinds of decision stamps (approve / reject / order / review), a themed --help renderer, an ASCII logo header, and a live ticker for background jobs. The library ships a ProtocolTyper subclass and make_app() helper so the bureau frame shows up on every command and sub-app without extra wiring; see Typer CLI integration below.

I built this because I kept reaching for libraries that were either useful or fun, and rarely both. I wanted my own CLIs to feel like an artifact — printing REJECTED with a timestamp instead of Error: invalid input. The fact that it doubles as a perfectly serviceable TUI toolkit is a side effect.

The aesthetic — Cyrillic accents, bureau titles, deadpan stamps — landed where it did because I'd been deep into Papers, Please at the time. The lib doesn't reuse a line of code or any asset from that game (see the disclaimer), but the vibe is unmistakably from the same shelf.

A note on the Russian: I speak some, and a few terms here are bent on purpose for the bureau-comic effect, but I'm nowhere near a native speaker. If anything reads as accidentally wrong — or, worse, accidentally offensive — please open an issue and I'll fix it.

ПОЛЕ № 2 · Installation

pip

pip install glory-to-protocol

uv

uv add glory-to-protocol

poetry

poetry add glory-to-protocol

ПОЛЕ № 3 · Usage

Configuration

The library exposes a single ProtocolSettings singleton that holds every piece of bureau-level branding. Defaults render the NIRVYTEKH look out of the box; override them when wiring the lib into your own CLI.

Field Default Effect
app_name "Protocol" Generic application name (used as fallback).
logo_text "Protocol" Text rendered as the large ASCII logo (header).
small_logo_text "Protocol" Text rendered inside the small bordered logo (stamps).
bureau_title "БЮРО NIRVYTEKH · Bureau of Computational Technology" Subtitle line under the large logo in the header.
director_name "Норман" Director name shown in the header meta row.
director_signature "Подписано: Норман, Директор NIRVYTEKH" Signature line at the form footer.
ascii.allowed_alphabet uppercase A–Z, 0–9 Characters allowed in logo_text / small_logo_text.

logo_text is validated against ascii.allowed_alphabet — passing characters outside the set raises InvalidASCIICharactersError.

Programmatic override (recommended)

Use configure(**overrides) at startup, before any component renders. This is the canonical coupling point when embedding the lib into your own Typer CLI.

import typer
from glory_to_protocol import configure, make_app

configure(
    app_name="MyBureau",
    logo_text="MyBureau",
    small_logo_text="MyBureau",
    director_name="Ada Lovelace",
    director_signature="Signed: Ada Lovelace, Director",
)

app: typer.Typer = make_app()

Calls layer: each configure() updates only the fields you pass; unspecified fields keep their value. For tests, reset_settings() clears the singleton.

Typer CLI integration

The lib ships a ProtocolTyper subclass and a make_app() helper that wire the bureau's themed --help renderer into every command and sub-app. See examples/showcase.py for the full reference.

import typer
from glory_to_protocol import configure, make_app
from glory_to_protocol.tui.forms import Form

configure(app_name="MyBureau", logo_text="MyBureau", small_logo_text="MyBureau")

app = make_app()


@app.command()
def status() -> None:
    with Form(title="status") as form:
        form.line("All systems nominal.")

Reference points in examples/showcase.py:

Components

Form

Context manager that draws the bureau form frame (top border, header, divider, body, signature, bottom border). Every other component renders into a Form.

from glory_to_protocol.tui.forms import Form

with Form(title="version") as form:
    form.line("Consulting bureau records...")

Constructor parameters:

Param Type Default Purpose
title str Tab label on the top border (e.g., "version").
console Console | None None Inject a Rich Console; auto-created if omitted.
show_header bool True Render the large logo + bureau title block at the top.
signature_text str | None None Override the footer signature; defaults to settings.

Methods: line(text, style=None, *, wrap=True), divider(), stamp(...), run_pending(jobs).

Logo

logo component

Two ASCII logo renderers driven by logo_text and small_logo_text:

from glory_to_protocol.tui.logo import logo_large, logo_small

print(logo_large())            # uses settings.logo_text
print(logo_small("ARCHIVE"))   # explicit override

Both accept an optional text: str | None; passing None reads the current settings. Results are memoized — configure() invalidates the cache.

Palette

theme palette

The theme module exposes named Rich Style objects for consistent typography across components:

from glory_to_protocol.tui import theme

form.line("Default report body.", style=theme.BODY)
form.line("Side note.", style=theme.MUTED)
form.line("Official accent.", style=theme.CYRILLIC_ACCENT)
form.line("Footer signature.", style=theme.SIGNATURE)

Other roles in the palette: theme.HEADER, theme.BORDER, theme.STAMP_APPROVE, theme.STAMP_REJECT, theme.STAMP_ORDER, theme.STAMP_REVIEW.

Wrap

line wrap behavior

Form.line(text) cell-wraps to the form's inner width, handling Latin, Cyrillic, and mixed-alphabet content correctly. Pass wrap=False to disable wrapping (the line is then truncated to fit):

form.line(long_text)                # default: wrap to inner width
form.line(long_text, wrap=False)    # truncate to one line

Stamps

stamp variants

Four stamp variants encode the bureau's terminal decisions on a request. Each takes a required label and an optional detail:

from glory_to_protocol.tui.stamps import (
    stamp_approve, stamp_reject, stamp_order, stamp_review,
)

form.stamp(stamp_approve("Q2 budget", "audit clean"))
form.stamp(stamp_reject("request #4711", "out of bureau scope"))
form.stamp(stamp_order("team 3 mobilization", "immediate execution"))
form.stamp(stamp_review("monthly report", "awaiting Gensek review"))
Variant Label (RU/EN) Use for
stamp_approve ОДОБРЕНО / APPROVED Request granted, action complete.
stamp_reject ОТКАЗАНО / REJECTED Request denied; include detail with the reason.
stamp_order ПРИКАЗ / DIRECT ORDER Imperative — the bureau is dictating an action.
stamp_review К СВЕДЕНИЮ / FOR REVIEW Awaiting external decision (e.g., from the Gensek).

Signature: stamp_<variant>(label: str, detail: str = "") -> Table.

Background jobs

background jobs live region

Form.run_pending(jobs) fans out a list of Jobs as async tasks and renders a live ticker until all reach a terminal state. Failures in one job are isolated — siblings keep running.

import asyncio
from glory_to_protocol.jobs.types import Job

async def fetch_quota() -> None:
    await asyncio.sleep(2)

jobs = [
    Job(label="fetching quota", coro_factory=fetch_quota),
    Job(label="archiving ledger", coro_factory=lambda: asyncio.sleep(3)),
]

with Form(title="sync") as form:
    form.line("Reconciling with the bureau...", style=theme.MUTED)
    outcomes = asyncio.run(form.run_pending(jobs))

for outcome in outcomes:
    print(outcome.label, outcome.status, outcome.duration_ms)

Job fields:

Field Type Default Purpose
label str Shown in the live ticker.
coro_factory Callable[[], Awaitable[None]] Factory that returns the coroutine to await.
critical bool False Tag-only today; reserved for future fail-fast use.

coro_factory is a factory, not a coroutine — passing the coroutine directly would bind it to the wrong event loop. Wrap with a lambda or a def that returns the awaitable. Each job receives a fresh awaitable on spawn.

Outcomes returned by run_pending:

Field Type Meaning
label str Echoes Job.label.
status "ok" | "fail" Terminal state.
error BaseException | None The exception, if status == "fail".
duration_ms int Wall-clock duration of the job.

The runner never raises on individual job failure; the caller decides how a failed background job affects the foreground stamp.

ПОЛЕ № 4 · Status & Roadmap

Pre-alpha (0.1.0). The public surface — Form, the four stamps, logo_large / logo_small, theme, configure(), Job / run_pending, and ProtocolTyper / make_app — is stable enough to drive a real CLI (78 tests, ~98% coverage), but minor versions may still rename or restructure things before 1.0.

Planned:

  • Job callbacks (per-job hooks on completion / failure)
  • Long-running jobs (progress reporting, cancellation surface)
  • Better tracebacks (themed, framed inside the bureau form)
  • Custom component authoring (public composition API)
  • Sentient Rubber Duck (Да, really)
  • Interactive TUI

Want a favor from the Верховный Лидер (Supreme Leader)? Open an issue.

ОТКАЗ · Disclaimer

This project's visual and thematic aesthetic is loosely inspired by Papers, Please (© Lucas Pope / 3909 LLC), specifically its evocation of a fictional Eastern-Bloc-style state bureau.

The inspiration is atmospheric only. Glory to Protocol does not use, reference, or distribute any code, asset, artwork, character, name, country, or trademark from Papers, Please. No content from Arstotzka — or any other fictional element of the game — appears in this repository. The bureau, its naming, its symbols, and its language are original to this project.

Papers, Please and Arstotzka are property of their respective owners. This project is not affiliated with, endorsed by, or sponsored by Lucas Pope or 3909 LLC.

If this project's atmosphere resonates with you, please consider supporting the original creator by buying Papers, Please on its official site or your preferred storefront. The work that inspired this aesthetic deserves to be paid for.

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

glory_to_protocol-0.1.2.tar.gz (81.6 kB view details)

Uploaded Source

Built Distribution

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

glory_to_protocol-0.1.2-py3-none-any.whl (39.2 kB view details)

Uploaded Python 3

File details

Details for the file glory_to_protocol-0.1.2.tar.gz.

File metadata

  • Download URL: glory_to_protocol-0.1.2.tar.gz
  • Upload date:
  • Size: 81.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for glory_to_protocol-0.1.2.tar.gz
Algorithm Hash digest
SHA256 f368425c9b80f9ff47e7e21a1e073da0b6a56f3630f949d17c5ee6f16de6a4b5
MD5 ce2641cee685e2446a370425f6cd5500
BLAKE2b-256 3f07d8a450940404321f2b2b44b7816aeee77df2cbeccaed504ab768be986ba9

See more details on using hashes here.

Provenance

The following attestation bundles were made for glory_to_protocol-0.1.2.tar.gz:

Publisher: release.yml on niltonfrederico/glory-to-protocol

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

File details

Details for the file glory_to_protocol-0.1.2-py3-none-any.whl.

File metadata

File hashes

Hashes for glory_to_protocol-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 af3527468174a72306e63194a0d1a2b180107475eab355f63ae05204be57bf31
MD5 2293f44a8bee89c75d162fc6f2f8da22
BLAKE2b-256 a49f9c41aa8810df61922f546f74bd8435c6cde104d2663b6ed85415e1f59286

See more details on using hashes here.

Provenance

The following attestation bundles were made for glory_to_protocol-0.1.2-py3-none-any.whl:

Publisher: release.yml on niltonfrederico/glory-to-protocol

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