Tuiloom
Tuiloom builds typed, keyboard-navigable terminal menus with dynamic content, Unicode-safe rendering, captured task output, alerts, and free-form input. It is small enough to learn from one document while still handling the awkward parts of terminal state and background-work shutdown.
This README documents the complete public API of Tuiloom 0.1.1.1. Tuiloom requires Python 3.12 or newer and is tested on Linux and macOS with Python 3.12–3.14.
Contents
- Installation
- Quick start
- Core concepts
- Navigation and focus
- Screen state and visibility
- Commands and submenus
- Content sources
- Captured task output
- Safe shutdown
- Free-form and hidden input
- Alerts
- Messages
- Key bindings and global commands
- Terminal hyperlinks
- API reference
- Runtime constraints
- Development
Installation
Install the latest release from PyPI:
python -m pip install tuiloom
To install the version documented here explicitly:
python -m pip install tuiloom==0.1.1.1
Tuiloom ships inline typing information through py.typed and has no required
framework or event-loop dependency.
Quick start
from tuiloom import CommandContext, ScreenContext, TerminalApp, TerminalMenu
app = TerminalApp("Generator")
menu = TerminalMenu(
app,
ScreenContext(
menu_name="main",
title="Generation",
text="Choose an operation",
width=24,
),
content_source="Ready",
)
def generate(context: CommandContext) -> None:
context.menu.set_content_source("Generated")
menu.add_command("Generate", generate)
app.set_main_menu(menu)
app.run()
TerminalApp.run() is blocking. Call it from Python's main thread in a real
interactive terminal. It switches to the terminal's alternate screen and hides
the cursor while the application runs, then restores input, cursor, and screen
state before returning or propagating an exception.
Core concepts
A Tuiloom application has three main layers:
TerminalAppowns application-wide configuration, global commands, messages, captured-output state, and the root menu.TerminalMenuowns selectable commands, content, input, alerts, local overrides, and its event loop.ScreenContextis the mutable visible state of one menu: name, title, minimum width, descriptive text, and footer message.
Every menu belongs to exactly one application. A submenu and its parent must
belong to the same TerminalApp. Registering a menu with set_main_menu() makes
its automatic final row Quit; other menus use Back. Registering another main
menu restores Back on the previous one.
Callbacks receive a frozen CommandContext containing the active application,
menu, command handle, and triggering binding:
from tuiloom import CommandContext
def inspect_invocation(context: CommandContext) -> None:
context.menu.show_alert(
f"Command: {context.command!r}\nBinding: {context.binding!r}"
)
Navigation and focus
The menu has focus initially. The default controls are:
| Key | Action |
|---|---|
| Tab | Alternate between menu and content focus |
| Up / Down | Move the selected command, or scroll focused content vertically |
| Left / Right | Scroll focused content horizontally |
| Enter | Activate the selected command or confirm an alert |
| Escape | Leave input mode, go Back, or request Quit |
Command selection loops and skips disabled commands. The automatic Back or
Quit row is always selectable and always follows user commands. The selected
row contains >, so it remains visible without ANSI color support.
Tab changes focus only when the menu has content. Focused boxes use solid
borders; unfocused boxes use dotted borders. Moving upward while
auto_scroll="smart" suspends automatic following, and reaching the bottom
enables it again.
All controls can be remapped through KeyMap; see
Key bindings and global commands.
Screen state and visibility
ScreenContext fields are live and mutable. Tuiloom observes changes while the
menu runs:
menu.screen_context.title = "New title"
menu.screen_context.text = "Updated instructions"
menu.screen_context.message = "Saved"
menu.screen_context.width = 32
width is the minimum inner width, not a fixed terminal width. It must be a
positive integer or None; booleans are rejected. Tuiloom renders a
terminal-too-small message when the complete frame cannot fit.
By default, a content box and menu box have one blank row between them. Pass
content_spacing=False to remove it.
Setting menu.show = False clears the entire frame while its event loop,
content sources, and tasks keep running. While hidden, only global commands and
the Back/Escape binding are handled; other input is discarded and cannot be
replayed when the menu becomes visible again.
Commands and submenus
Menu commands
add_command() returns a stable MenuCommand handle. Use the owning menu to
mutate it:
def connect(context: CommandContext) -> None:
context.menu.show_alert("Connected")
def disconnect(context: CommandContext) -> None:
context.menu.clear_alert()
command = menu.add_command("Connect", connect)
menu.set_command_label(command, "Disconnect")
menu.set_command_behavior(command, disconnect)
menu.move_command(command, 0)
menu.disable_command(command)
menu.enable_command(command)
menu.set_exit_label("Close")
Positions are zero-based. position=None appends a command; an explicit valid
position inserts it there. Booleans, negative positions, and out-of-range
positions are rejected before mutation. menu.commands is an immutable tuple
view, and a handle from another menu is rejected.
The handle exposes read-only label, behavior, position, and enabled
properties. Their current values reflect mutations performed through the menu.
Submenus
settings = TerminalMenu(
app,
ScreenContext("settings", "Settings", text="Configure the application"),
)
open_settings = menu.add_menu(settings, "Settings", position=0)
Activating the returned command runs the submenu until its Back row is
activated. The parent then resumes. add_menu() rejects a submenu owned by a
different application.
Content sources
ContentSource accepts exactly four forms:
from collections.abc import Iterator
static_text = "one\ntwo"
static_lines = ["one", "two"]
def stream() -> Iterator[str]:
yield "one\n"
yield "two\n"
def refreshed() -> str | list[str]:
return ["current", "state"]
menu.set_content_source(static_text)
menu.set_content_source(static_lines)
menu.set_content_source(stream(), description="Loading records")
menu.set_content_source(refreshed, description="Refreshing state")
The forms behave differently:
| Source | Behavior |
|---|---|
str |
Static text split into display lines |
list[str] |
Static lines displayed as supplied |
Iterator[str] |
A background worker consumes chunks until exhaustion |
Callable[[], str | list[str]] |
A background worker repeatedly evaluates the latest state |
Static content is normalized immediately. Iterator chunks may contain partial
lines and multiple newlines; carriage returns replace the unfinished line, which
supports progress-style output. Iterators must yield only strings. Dynamic
callables must return str or list[str] and have at most one evaluation in
flight.
Unsafe terminal controls are removed from rendered content. Unicode graphemes, cell widths, safe SGR styling, tabs, and line boundaries are normalized for the terminal.
Inherited and local content
app = TerminalApp("Monitor", global_content_source="Shared status")
inherited = TerminalMenu(app, ScreenContext("main", "Main"))
local = TerminalMenu(
app,
ScreenContext("logs", "Logs"),
content_source="Local status",
)
A menu constructed with content_source=None takes the application's
global_content_source. A local source wins when supplied. If neither exists,
the content box is omitted and NO_CONTENT_SOURCE is shown in the footer when
the menu starts.
Replacing active content
set_content_source(source, description=...) changes stored content
immediately. If the menu is running and is not currently displaying a captured
task, Tuiloom installs it through the active event loop.
Replacing an iterator or an in-flight dynamic evaluation requests cooperative cancellation of the old source. The UI remains responsive, waits for the old worker to stop, and then installs only the latest requested replacement. If several replacements arrive during cleanup, the last request wins. Results and errors from an explicitly cancelled source are discarded; ordinary source errors still propagate after resources are cleaned up.
For cooperative iterator cancellation, Tuiloom calls an optional cancel()
method as soon as cancellation is requested and an optional close() method
when the consumer exits. A blocking iterator should implement cancel() so it
can wake or release its own __next__() call. A dynamic callable cannot be
interrupted in the middle of an evaluation; that call must return before its
worker can stop.
Auto-scroll
Set auto_scroll in the constructor or later:
menu = TerminalMenu(
app,
ScreenContext("logs", "Logs"),
content_source=stream(),
auto_scroll="smart",
)
menu.auto_scroll = "strict"
menu.auto_scroll = None
"smart"follows new iterator content until the user scrolls upward, then resumes when the viewport reaches the bottom."strict"always follows the newest iterator content.Nonedisables automatic following.
Changing the mode or replacing content resets smart-scroll state. Invalid modes
raise ValueError.
Captured task output
run_with_output() runs blocking Python work on a non-daemon worker and uses
its captured stdout/stderr as the menu's temporary content source:
def download() -> str:
print("Downloading…")
return "archive.zip"
def start_download(context: CommandContext) -> None:
context.menu.run_with_output(
download,
on_success=lambda path: context.menu.show_alert(f"Saved {path}"),
on_error=lambda error: context.menu.show_alert(str(error)),
description="Downloading archive",
)
menu.add_command("Download", start_download)
The call itself starts the work and returns immediately. It is valid only while
the menu is active, and only one captured task may run in the application at a
time. A second task raises RuntimeError.
During the task, auto-scroll is temporarily "strict". On normal completion,
Tuiloom restores the menu's previous content source and auto-scroll mode, then
runs on_success(result) or on_error(exception) on the UI thread.
Capture includes print() and Python writes to sys.stdout and sys.stderr.
It cannot capture subprocess output or direct POSIX file-descriptor writes.
Tuiloom does not inject a cancellation token into action: Stop and quit
discards its remaining output and outcome, but the action itself must return or
use application-owned cancellation state before its worker can terminate.
Safe shutdown
Quitting the root menu while a captured task, iterator, or dynamic evaluation is active displays:
1: Stop and quit
2: Wait and quit
0: Cancel
- Stop and quit requests cooperative cancellation, discards later results,
errors, output, and callbacks, and displays
Stopping…. The menu closes only after every worker has really terminated. - Wait and quit lets work finish normally, animates its description, runs a captured task's completion callback, and then quits even if that callback changes menus.
- Cancel restores the previous footer and keeps the menu running.
0 remains available during normal waiting. Once Stop and quit has been chosen,
cancellation is irreversible and further 0 input cannot restore the menu.
Iterator sources count as active work until they finish. Dynamic sources count
as active only while an evaluation is in progress. Static content does not block
exit. description from set_content_source() or run_with_output() labels
normal waiting; TASK_STOPPING controls the stopping footer.
Python cannot safely kill an arbitrary thread. Tuiloom therefore uses
cooperative cancellation and waits without a timeout before closing sockets,
restoring the terminal, or returning from the application. Native code that
never returns can leave Stopping… visible indefinitely. Use a separate process
when forceful termination is required.
The same guarantee applies when a callback, source, or renderer raises: Tuiloom joins active workers before restoring the terminal and propagating the error.
Free-form and hidden input
def submit_password(value: str) -> None:
if value:
menu.leave_input_mode()
menu.show_alert("Password received")
menu.enter_input_mode("Password: ", submit_password, hidden=True)
enter_input_mode() clears the previous buffer. Printable input is appended,
Backspace removes a complete Unicode grapheme, Enter calls the InputBehavior
with the full string, and Escape calls leave_input_mode() without submitting.
The callback decides whether input mode stays active after submission.
With hidden=True, one * is displayed per grapheme, including combining
characters and emoji sequences. The original Unicode text is passed to the
callback. Every global command is disabled while free-form input is active.
An alert temporarily suspends the prompt, buffer, hidden state, and input callback without destroying them. Clearing the alert reveals the same input state again.
Alerts
A blocking alert has no confirmation prompt and Enter does not close it:
menu.show_alert("Waiting for an external event")
menu.clear_alert()
A confirmable alert receives a CommandContext:
menu.show_alert(
"Saved",
on_confirm=lambda context: context.menu.set_content_source("Ready"),
prompt="Continue",
)
If prompt is omitted for a confirmable alert, Tuiloom uses
Press Enter to continue. The alert is cleared only after its callback returns
normally. If the callback raises, the alert remains and the exception
propagates. Alert confirmation has context.command is None and the Enter
binding in context.binding.
Alerts preserve the content box. Global commands remain active while an alert is displayed, and Escape still performs Back/Quit.
Messages
Messages occupy ScreenContext.message, the menu footer. Register custom
messages on the application, then show or suppress them by key:
from tuiloom import MessageKey
app.add_message("connected", "Connected successfully")
menu.show_message("connected") # True when displayed
menu.clear_message()
menu.disable_message("connected") # suppress only in this menu
menu.enable_message("connected")
app.disable_message("connected") # suppress in every menu
app.enable_message("connected")
menu.show_message(MessageKey.NO_CONTENT_SOURCE)
The built-in keys are:
MessageKey |
Value | Purpose |
|---|---|---|
NO_CONTENT_SOURCE |
"no_content_source" |
Explain a missing content source |
UNKNOWN_COMMAND |
"unknown_command" |
Report discarded textual command input |
TASK_EXIT_CHOICES |
"task_exit_choices" |
Show Stop, Wait, and Cancel choices |
TASK_WAITING |
"task_waiting" |
Label animated normal waiting |
TASK_STOPPING |
"task_stopping" |
Show irreversible cooperative stopping |
show_message() validates the key and returns False without changing the
footer when the message is suppressed. Otherwise it displays the message and
returns True. Local and application-wide suppression combine;
is_message_enabled() reports the effective state. Unknown keys raise
KeyError. Custom keys must be nonempty and unique, including against built-in
keys.
Automatic messages use the same registry and respect suppression.
Key bindings and global commands
Custom system bindings
from tuiloom import KeyBinding, KeyMap, TerminalApp
keymap = KeyMap()
keymap.set_binding("focus", KeyBinding("f", ctrl=True))
app = TerminalApp("App", keymap=keymap)
The seven system actions are focus, up, down, left, right, activate,
and back. keymap.bindings is a read-only live mapping, and the same bindings
are available as keymap.focus, keymap.up, and so on. action_for(binding)
returns the matching action or None.
set_binding() rejects unknown actions, non-KeyBinding values, and collisions
with another system action or application global command. Validation happens
before mutation, so a failure leaves the previous binding unchanged.
KeyBinding accepts a nonempty key string or normalized special-key name and the
boolean modifiers ctrl, alt, and shift. These aliases normalize to the
canonical names:
| Alias | Canonical key |
|---|---|
return |
enter |
esc |
escape |
arrow_up |
up |
arrow_down |
down |
arrow_left |
left |
arrow_right |
right |
Multi-character names are lowercased. Terminal protocols cannot always report every modifier distinctly: Ctrl+letter is commonly case-insensitive, and Shift may arrive only as character case.
Invisible global commands
def refresh(context: CommandContext) -> None:
context.menu.set_content_source("Refreshed")
refresh_command = app.add_global_command(
KeyBinding("r", ctrl=True),
"Refresh",
refresh,
)
app.set_global_command_binding(refresh_command, KeyBinding("f5"))
app.set_global_command_label(refresh_command, "Reload")
app.set_global_command_behavior(refresh_command, refresh)
Global commands are invoked immediately when their binding arrives and are not
rendered as menu rows. app.global_commands is an immutable tuple of handles;
their read-only metadata can power a custom help screen.
A menu can override or disable an application global command locally:
menu.set_global_command_behavior(refresh_command, refresh)
menu.disable_global_command(refresh_command)
menu.enable_global_command(refresh_command)
menu.clear_global_command_behavior(refresh_command)
Global-command handles belong to one application. Foreign handles are rejected. Global commands remain available while a menu is hidden or an alert is shown, but not during free-form input or a root task-exit choice.
Input priority is: task-exit choice, hidden-menu handling, free-form input, global commands, alerts, then focus/navigation. Unknown terminal sequences are consumed and do not block later input.
Terminal hyperlinks
from tuiloom import hyperlink
label = hyperlink("Project", "https://github.com/maroard/Tuiloom")
hyperlink() produces a complete OSC 8 hyperlink. It accepts only absolute
HTTP or HTTPS URLs with a network location. Empty URLs, whitespace, C0/C1
controls, Escape, and backslash are rejected with ValueError.
Visible text is sanitized: unsafe controls and nested OSC links are removed, while printable Unicode and safe SGR color/style sequences are preserved.
API reference
All supported imports come directly from tuiloom:
from tuiloom import (
AutoScrollMode,
CommandBehavior,
CommandContext,
ContentSource,
GlobalCommand,
InputBehavior,
KeyBinding,
KeyMap,
MenuCommand,
MessageKey,
ScreenContext,
TerminalApp,
TerminalMenu,
hyperlink,
)
Anything outside this export list is internal and may change without notice.
Type aliases
type ContentSource = (
str
| list[str]
| Iterator[str]
| Callable[[], str | list[str]]
)
type AutoScrollMode = Literal["smart", "strict"]
type CommandBehavior = Callable[[CommandContext], None]
type InputBehavior = Callable[[str], None]
AutoScrollMode | None is used where automatic scrolling may be disabled.
ScreenContext
ScreenContext(
menu_name: str,
title: str,
width: int | None = None,
text: str | None = None,
message: str | None = None,
)
A mutable dataclass holding visible menu state:
menu_name: internal name used in contextual messages;title: heading in the menu box;width: positive minimum inner width, orNonefor content-based sizing;text: optional description above commands;message: optional footer.
Construction and later assignment validate width; invalid values raise
ValueError.
KeyBinding
KeyBinding(
key: str,
ctrl: bool = False,
alt: bool = False,
shift: bool = False,
)
A frozen, hashable binding value. key must be a nonempty string or
construction raises ValueError; every modifier must be bool or construction
raises TypeError. Special aliases and multi-character normalization are
described above.
KeyMap
KeyMap()
bindings→Mapping[str, KeyBinding]: read-only live action mapping.focus,up,down,left,right,activate,back -> KeyBinding: current bindings exposed as dynamic read-only properties.set_binding(action: str, binding: KeyBinding) -> None: atomically replace a system binding. RaisesKeyErrorfor an unknown action,TypeErrorfor a non-binding, orValueErrorfor a collision.action_for(binding: KeyBinding) -> str | None: return the matching system action.
CommandContext
CommandContext(
app: TerminalApp,
menu: TerminalMenu,
command: MenuCommand | GlobalCommand | None,
binding: KeyBinding | None,
)
A frozen dataclass created by Tuiloom for callbacks. command is None for
alert confirmation. binding may be None for programmatic execution.
MenuCommand
MenuCommand(
menu: TerminalMenu,
label: str,
behavior: CommandBehavior,
)
Applications normally obtain this stable handle from add_command() or
add_menu() instead of constructing it directly. Its properties are read-only:
label -> str;behavior -> CommandBehavior;position -> int, zero-based among user commands;enabled -> bool.
Use the owning menu's set_command_*, move_command, disable_command, and
enable_command methods to mutate it.
GlobalCommand
GlobalCommand(
app: TerminalApp,
binding: KeyBinding,
label: str,
behavior: CommandBehavior,
)
Applications normally obtain this handle from add_global_command(). Its
read-only properties are binding, label, and behavior. Use the owning
application's set_global_command_* methods for application-wide mutation, or a
menu's global-command methods for local behavior and enablement.
MessageKey
MessageKey is a StrEnum with NO_CONTENT_SOURCE, UNKNOWN_COMMAND,
TASK_EXIT_CHOICES, TASK_WAITING, and TASK_STOPPING. The values and purpose
of each member are listed in Messages. Enum members can be passed
where a message key string is accepted.
TerminalApp
TerminalApp(
name: str,
global_content_source: ContentSource | None = None,
*,
keymap: KeyMap | None = None,
)
The constructor stores the display name, optional content inherited by menus created without a local source, and an optional custom system key map.
Read-only properties:
name -> str: application name displayed by every menu;global_content_source -> ContentSource | None: source inherited at menu construction;keymap -> KeyMap: configurable system key map;global_commands -> tuple[GlobalCommand, ...]: immutable ordered handle view;main_menu -> TerminalMenu | None: registered root menu.
Methods:
set_main_menu(menu: TerminalMenu) -> None
Register an application-owned root menu and update automatic exit labels.
Raises ValueError for a foreign menu.
add_global_command(
binding: KeyBinding,
label: str,
behavior: CommandBehavior,
) -> GlobalCommand
Register an invisible application command. Raises TypeError for a non-binding
and ValueError when the binding collides with a system or global command.
set_global_command_binding(
command: GlobalCommand,
binding: KeyBinding,
) -> None
set_global_command_label(command: GlobalCommand, label: str) -> None
set_global_command_behavior(
command: GlobalCommand,
behavior: CommandBehavior,
) -> None
Mutate an owned global handle. Binding replacement is validated atomically.
Foreign handles raise ValueError.
add_message(key: str, text: str) -> None
disable_message(key: str) -> None
enable_message(key: str) -> None
Register or globally suppress messages. add_message() raises ValueError for
an empty or duplicate key; enable/disable raise KeyError for unknown keys.
run() -> None
Run the registered main menu. Raises RuntimeError if no main menu exists or if
called outside Python's main thread. It blocks until the application exits,
joins workers, restores terminal state, and then propagates any pending error.
TerminalMenu
TerminalMenu(
app: TerminalApp,
screen_context: ScreenContext,
content_source: ContentSource | None = None,
content_spacing: bool = True,
show: bool = True,
auto_scroll: AutoScrollMode | None = None,
)
Create a menu owned by app. content_source=None inherits application
content. content_spacing and show must be booleans. auto_scroll accepts
"smart", "strict", or None.
Properties:
app -> TerminalApp: read-only owner;screen_context -> ScreenContext: read-only reference to mutable display state;commands -> tuple[MenuCommand, ...]: immutable ordered handle view;is_main -> bool: whether this is the registered root;show -> bool: readable and writable visibility state;auto_scroll -> AutoScrollMode | None: readable and writable iterator-follow policy.
Command methods
add_command(
label: str,
behavior: CommandBehavior,
*,
position: int | None = None,
) -> MenuCommand
add_menu(
submenu: TerminalMenu,
label: str,
*,
position: int | None = None,
) -> MenuCommand
Add a command or application-owned submenu and return its stable handle.
Invalid positions raise TypeError or ValueError; foreign submenus raise
ValueError.
set_command_label(command: MenuCommand, label: str) -> None
set_command_behavior(
command: MenuCommand,
behavior: CommandBehavior,
) -> None
move_command(command: MenuCommand, position: int) -> None
disable_command(command: MenuCommand) -> None
enable_command(command: MenuCommand) -> None
set_exit_label(label: str) -> None
Mutate owned menu commands or the automatic Back/Quit label. move_command()
requires an existing zero-based position. Foreign handles raise ValueError.
Global-command methods
set_global_command_behavior(
command: GlobalCommand,
behavior: CommandBehavior,
) -> None
clear_global_command_behavior(command: GlobalCommand) -> None
disable_global_command(command: GlobalCommand) -> None
enable_global_command(command: GlobalCommand) -> None
Override, restore, disable, or enable an application global command in this menu
only. Foreign handles raise ValueError.
Content and task methods
set_content_source(
content_source: ContentSource,
*,
description: str = "Content in progress",
) -> None
Store and, while active, safely install a content source. Replacement semantics are described in Replacing active content.
run_with_output[T](
action: Callable[[], T],
*,
on_success: Callable[[T], None],
on_error: Callable[[Exception], None],
description: str = "Task in progress",
) -> None
Start one captured application task. Raises RuntimeError outside an active
menu or when another task is running. Completion callbacks run on the UI thread.
Input and alert methods
enter_input_mode(
prompt: str,
behavior: InputBehavior,
*,
hidden: bool = False,
) -> None
leave_input_mode() -> None
Start a fresh input buffer or clear all input state.
show_alert(
text: str,
*,
on_confirm: CommandBehavior | None = None,
prompt: str | None = None,
) -> None
clear_alert() -> None
Show a blocking/confirmable alert or clear it and reveal suspended input state.
Message methods
show_message(key: str) -> bool
clear_message() -> None
disable_message(key: str) -> None
enable_message(key: str) -> None
is_message_enabled(key: str) -> bool
Show, clear, locally suppress, or inspect registered messages. Every keyed operation validates the key. Effective enablement combines local and global suppression.
Lifecycle methods
run() -> None
stop() -> None
run() runs the menu until Back/Quit and is intended for the application or a
submenu callback; calling it outside TerminalApp.run() raises RuntimeError.
stop() requests Back/Quit. On the root menu it presents safe shutdown choices
when background work is active; otherwise it stops immediately.
hyperlink
hyperlink(text: str, url: str) -> str
Return sanitized visible text wrapped in a complete OSC 8 open/close pair.
Unsafe or non-HTTP(S) URLs raise ValueError.
Runtime constraints
TerminalApp.run()is blocking, main-thread-only, and requires an interactive terminal.- Only one
run_with_output()task can run per application. - Python stdout/stderr capture does not include subprocess or direct file-descriptor output.
- Content and task workers are non-daemon and are always joined before terminal restoration or application return.
- Cancellation is cooperative. Tuiloom cannot forcibly terminate Python threads or native code that does not return.
- Terminal protocols may collapse modifier combinations, so not every theoretical
KeyBindingis distinguishable on every terminal. - Rendered content is Unicode-cell-aware and sanitizes unsafe terminal control sequences, but application callbacks remain responsible for their own domain errors and side effects.
Development
Clone the repository and install the locked development environment:
git clone https://github.com/maroard/Tuiloom.git
cd Tuiloom
make install
Available checks:
make check # Ruff lint/format check, strict MyPy, tests, and coverage
make fix # apply Ruff formatting and safe lint fixes
make build # build wheel/sdist and validate both with Twine
CI runs on Linux and macOS with Python 3.12, 3.13, and 3.14. It verifies typing,
tests, at least 90% branch-aware coverage, distributions, package metadata,
py.typed, licensing, and installation of the built wheel in a clean
environment.
Tuiloom is released under the MIT License. Report defects and request features through GitHub Issues.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tuiloom-0.1.1.1.tar.gz.
File metadata
- Download URL: tuiloom-0.1.1.1.tar.gz
- Upload date:
- Size: 67.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2d01b011b7fee124be107c06092aa23013835ea40641a7e83941013dfc50fa1a
|
|
| MD5 |
ff831a9322994bf5af53d99dcdc54145
|
|
| BLAKE2b-256 |
30c7495e1d4168fbb47c4824a236367959d8f5d1b5131816a4edeec2e230ad59
|
Provenance
The following attestation bundles were made for tuiloom-0.1.1.1.tar.gz:
Publisher:
release.yml on maroard/Tuiloom
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tuiloom-0.1.1.1.tar.gz -
Subject digest:
2d01b011b7fee124be107c06092aa23013835ea40641a7e83941013dfc50fa1a - Sigstore transparency entry: 2638995952
- Sigstore integration time:
-
Permalink:
maroard/Tuiloom@5aeadd7d9e21b99b185b5c36ba94cbc18aed562c -
Branch / Tag:
refs/tags/v0.1.1.1 - Owner: https://github.com/maroard
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@5aeadd7d9e21b99b185b5c36ba94cbc18aed562c -
Trigger Event:
release
-
Statement type:
File details
Details for the file tuiloom-0.1.1.1-py3-none-any.whl.
File metadata
- Download URL: tuiloom-0.1.1.1-py3-none-any.whl
- Upload date:
- Size: 52.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6cbf96a854afe9da0310c5b097d0fa637eca72658fe18e8ab7143d88b4dcb29f
|
|
| MD5 |
f2830050eabcc190156fdc3fd52ee36f
|
|
| BLAKE2b-256 |
173271216c5d8604df9f64e5a956d8f2bb4c2de94e143ddc5fbb9dfeca42d7e9
|
Provenance
The following attestation bundles were made for tuiloom-0.1.1.1-py3-none-any.whl:
Publisher:
release.yml on maroard/Tuiloom
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tuiloom-0.1.1.1-py3-none-any.whl -
Subject digest:
6cbf96a854afe9da0310c5b097d0fa637eca72658fe18e8ab7143d88b4dcb29f - Sigstore transparency entry: 2638996019
- Sigstore integration time:
-
Permalink:
maroard/Tuiloom@5aeadd7d9e21b99b185b5c36ba94cbc18aed562c -
Branch / Tag:
refs/tags/v0.1.1.1 - Owner: https://github.com/maroard
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@5aeadd7d9e21b99b185b5c36ba94cbc18aed562c -
Trigger Event:
release
-
Statement type: