Python Output Styling
Style all user-facing Python script output using the bundled style.py module.
Workflow
- •Read references/style.py to understand available functions
- •If
style.pydoesn't exist alongside the target script, copy from references/style.py into the same directory - •Import functions in the target script:
from style import header, success, error, info, section - •Replace raw
print()statements with styled output function calls - •Replace
input()prompts withstyled_input,confirm, orchooseas appropriate - •Wrap long-running iterables with
progressfor animated feedback - •Wrap long-running functions with
spinfor animated spinner feedback - •Verify script still works when Rich is not installed (graceful degradation to plain print)
Dependency
Rich is the preferred rendering backend. Install via:
bash
uv run --with rich script.py
When Rich is unavailable, all functions fall back to plain print() output automatically.
Color Palette
| Name | Hex | Rich Markup | Use |
|---|---|---|---|
| Brand | #AF87FF | [bold #AF87FF] | Headers, emphasis |
| Success | #87D787 | [#87D787] | Checkmarks, completion |
| Error | #FF5F5F | [#FF5F5F] | Errors, failures |
| Warning | #FFD75F | [#FFD75F] | Warnings, caution |
| Info | #87CEEB | [#87CEEB] | Info messages, tips |
| Muted | #808080 | [#808080] | Paths, secondary text |
Output Functions
| Function | Symbol | Color | Purpose |
|---|---|---|---|
header(title, subtitle="") | box | Brand | Bordered header panel |
section(text) | ━━ | Brand | Horizontal rule divider |
success(text) | ✓ | Success | Completed actions |
error(text) | ✗ | Error | Error messages |
warn(text) | ⚠ | Warning | Warnings |
info(text) | ● | Info | Informational |
step(text) | → | Muted | Action log (suppressed when quiet) |
note(text) | — | Muted | "Note:" prefixed |
banner(text) | box | Success | Completion banner |
error_block(*lines) | │ | Error | Multi-line error box |
list_item(label, value) | • | Brand+Muted | Key-value display |
dim(text) | — | Muted | De-emphasized text (suppressed when quiet) |
Utility Functions
| Function | Purpose |
|---|---|
table(headers, *rows) | Formatted table with headers. headers is a list of strings, each row is a list of strings |
pretty(obj) | Rich inspect for any Python object (falls back to pprint) |
Progress Functions
| Function | Returns | Purpose |
|---|---|---|
progress(iterable, label="") | Wrapped iterable | Rich progress bar wrapping any iterable |
spin(title, func, *args, **kwargs) | Function result | Animated spinner wrapping a function call |
Interactive Functions
| Function | Returns | Purpose |
|---|---|---|
confirm(prompt, default=True) | bool | y/n prompt |
choose(header, *options) | str | Numbered selection menu |
styled_input(prompt, password=False) | str | Styled text input (avoids shadowing built-in input) |
Pattern Mapping
| Old Pattern | New Pattern |
|---|---|
print("Title") + print("=====") | header("Title", "Subtitle") |
print("✓ done") | success("done") |
print("Error: ...") | error("...") |
print("Warning: ...") | warn("...") |
print(" - Label: value") | list_item("Label", "value") |
input("Continue? (y/n) ") | confirm("Continue?") |
print("Section...") between blocks | section("Section") |
print("Note: ...") | note("...") |
| Multi-line error block | error_block("line1", "line2") |
Numbered menu with input() | choose("Pick one:", "A", "B", "C") |
input("Name: ") | styled_input("Name:") |
getpass.getpass("Password: ") | styled_input("Password:", password=True) |
for item in items: ... (slow) | for item in progress(items, "Processing"): ... |
| Long function call with no feedback | result = spin("Installing...", install_pkg, "name") |
pprint(obj) | pretty(obj) |
Environment Variables
| Variable | Default | Effect |
|---|---|---|
NO_COLOR | unset | Disables all color output when set to any value |
STYLE_VERBOSE | 1 | 0=quiet (suppresses step/dim), 1=default, 2=verbose |
Rules
- •ALWAYS import from
style.py— never use Rich directly in scripts - •NEVER hard-code ANSI codes in scripts — use
style.pyfunctions exclusively - •Use
styled_inputinstead ofinputfor styled prompts to avoid shadowing the built-in - •
confirmreturns abool— use directly inif confirm("Continue?"): ... - •
spinreturns the wrapped function's return value — use:result = spin("Building...", build_fn) - •
choosereturns the selected option string — use:choice = choose("Pick:", "A", "B") - •
progresswraps an iterable — use:for item in progress(items, "Processing"): ... - •Do NOT wrap instant operations with
spin— only use for functions that take noticeable time - •Preserve all existing logic — only change output formatting, never behavior
- •Scripts must work identically when Rich is not installed (plain print fallback)