Spinner¶
Animated CLI spinners for visual feedback during long-running operations. The Spinner class provides
configurable animations with Rich styling, context manager support, and decorator patterns.
Tip
Use spinners to indicate progress when exact completion time is unknown. For determinate progress, consider Rich’s built-in progress bars instead.
Quick overview¶
Multiple animation styles:
BRAILLE,DOTS,LINE,ARROW,BLOCKS,CIRCLE,MOON,CLOCKConfigurable via presets (
minimal,fancy,blocks,bounce,color_wave)Context manager and decorator support
Thread-safe animation loop
Success/failure indicators on completion
Usage patterns¶
Basic context manager¶
from kstlib.ui import Spinner
with Spinner("Loading data...") as spinner:
# Long operation here
data = fetch_data()
spinner.update("Processing...")
process(data)
# Spinner shows success checkmark on exit
As a decorator¶
from kstlib.ui import Spinner
@Spinner.wrap("Fetching results")
def fetch_results():
# Long operation
return api.get_results()
results = fetch_results() # Spinner runs during execution
With presets¶
from kstlib.ui import Spinner
# Use a preset style
with Spinner("Working...", preset="fancy"):
do_work()
# Or specify style directly
with Spinner("Computing...", style="MOON", interval=0.1):
compute()
Manual control¶
from kstlib.ui import Spinner
spinner = Spinner("Starting...")
spinner.start()
try:
for item in items:
spinner.update(f"Processing {item}...")
process(item)
spinner.succeed("All done!")
except Exception as e:
spinner.fail(f"Error: {e}")
finally:
spinner.stop()
Module reference¶
Animated spinner utilities for CLI feedback during long operations.
- class kstlib.ui.spinner.SpinnerStyle(value)[source]¶
Bases:
EnumPredefined spinner animation families.
Each style defines a sequence of frames that cycle during animation.
- BRAILLE = ('⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷')¶
- DOTS = ('⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏')¶
- LINE = ('|', '/', '-', '\\')¶
- ARROW = ('←', '↖', '↑', '↗', '→', '↘', '↓', '↙')¶
- BLOCKS = ('▁', '▂', '▃', '▄', '▅', '▆', '▇', '█', '▇', '▆', '▅', '▄', '▃', '▂')¶
- CIRCLE = ('◐', '◓', '◑', '◒')¶
- SQUARE = ('◰', '◳', '◲', '◱')¶
- MOON = ('🌑', '🌒', '🌓', '🌔', '🌕', '🌖', '🌗', '🌘')¶
- CLOCK = ('🕐', '🕑', '🕒', '🕓', '🕔', '🕕', '🕖', '🕗', '🕘', '🕙', '🕚', '🕛')¶
- class kstlib.ui.spinner.SpinnerPosition(value)[source]¶
Bases:
EnumPosition of the spinner relative to the message text.
- BEFORE = 'before'¶
- AFTER = 'after'¶
- class kstlib.ui.spinner.SpinnerAnimationType(value)[source]¶
Bases:
EnumType of animation to display.
- SPIN = 'spin'¶
- BOUNCE = 'bounce'¶
- COLOR_WAVE = 'color_wave'¶
- class kstlib.ui.spinner.Spinner(message='', *, style=SpinnerStyle.BRAILLE, position=SpinnerPosition.BEFORE, animation_type=SpinnerAnimationType.SPIN, interval=0.08, spinner_style='cyan', text_style=None, done_character='✓', done_style='green', fail_character='✗', fail_style='red', console=None, file=None)[source]¶
Bases:
objectAnimated spinner for CLI feedback during long operations.
Supports multiple animation styles including character spinners, bouncing bars, and color wave effects. Can be used as a context manager or controlled manually.
- Parameters:
message (str) – Text to display alongside the spinner.
style (SpinnerStyle | str) – Spinner animation style (SpinnerStyle enum or string name).
position (SpinnerPosition | str) – Where to place spinner relative to text (before/after).
animation_type (SpinnerAnimationType | str) – Type of animation (spin/bounce/color_wave).
interval (float) – Seconds between animation frames.
spinner_style (str | Style | None) – Rich style for the spinner character.
text_style (str | Style | None) – Rich style for the message text.
console (Console | None) – Optional Rich console instance.
file (IO[str] | None) – Output stream (defaults to sys.stderr).
Examples
Create a spinner with default settings:
>>> spinner = Spinner("Loading...") >>> spinner.message 'Loading...'
Create with custom style:
>>> spinner = Spinner("Working", style=SpinnerStyle.DOTS) >>> spinner = Spinner("Building", style="BLOCKS", interval=0.1)
Using as a context manager (terminal I/O):
>>> with Spinner("Processing...") as s: ... do_long_operation() ... s.update("Almost done...")
Manual control:
>>> spinner = Spinner("Working...") >>> spinner.start() >>> spinner.stop(success=True)
- __init__(self, message: 'str' = '', *, style: 'SpinnerStyle | str' = <SpinnerStyle.BRAILLE: ('⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷')>, position: 'SpinnerPosition | str' = <SpinnerPosition.BEFORE: 'before'>, animation_type: 'SpinnerAnimationType | str' = <SpinnerAnimationType.SPIN: 'spin'>, interval: 'float' = 0.08, spinner_style: 'str | Style | None' = 'cyan', text_style: 'str | Style | None' = None, done_character: 'str' = '✓', done_style: 'str | Style | None' = 'green', fail_character: 'str' = '✗', fail_style: 'str | Style | None' = 'red', console: 'Console | None' = None, file: 'IO[str] | None' = None) 'None' -> None[source]¶
Initialize spinner with configuration.
- classmethod from_preset(preset: 'str', message: 'str' = '', *, console: 'Console | None' = None, **overrides: 'Any') 'Spinner' -> Spinner[source]¶
Create a spinner from a named preset.
- Parameters:
- Returns:
Configured Spinner instance.
- Raises:
SpinnerError – If preset name is not found.
- Return type:
Examples
Create from a built-in preset:
>>> spinner = Spinner.from_preset("minimal", "Loading...") >>> spinner = Spinner.from_preset("fancy", "Processing data")
Override preset values:
>>> spinner = Spinner.from_preset("bounce", "Building", interval=0.05)
Invalid preset raises error:
>>> Spinner.from_preset("nonexistent") Traceback (most recent call last): ... kstlib.ui.exceptions.SpinnerError: Unknown preset 'nonexistent'. ...
- stop(self, *, success: 'bool' = True, final_message: 'str | None' = None) 'None' -> None[source]¶
Stop the spinner animation.
- update(self, message: 'str') 'None' -> None[source]¶
Update the spinner message while running.
- Parameters:
message (str) – New message to display.
- kstlib.ui.spinner.with_spinner(message: 'str' = 'Processing...', *, style: 'SpinnerStyle | str' = <SpinnerStyle.BRAILLE: ('⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷')>, log_style: 'str | None' = 'dim', capture_prints: 'bool' = True, log_zone_height: 'int | None' = None, **spinner_kwargs: 'Any') 'Callable[[Callable[P, R]], Callable[P, R]]' -> Callable[[Callable[P, R]], Callable[P, R]][source]¶
Wrap a function with a spinner, capturing its prints.
- Parameters:
message (str) – Spinner message to display.
style (SpinnerStyle | str) – Spinner animation style.
log_style (str | None) – Style for captured print output (None for no style).
capture_prints (bool) – If True, redirect stdout to spinner.log().
log_zone_height (int | None) – If set, use SpinnerWithLogZone with fixed height. The spinner stays at top, logs scroll in bounded zone below.
**spinner_kwargs (Any) – Additional arguments passed to Spinner.
- Returns:
Decorated function.
- Return type:
Callable[[Callable[P, R]], Callable[P, R]]
Examples
Basic decorator usage (terminal I/O):
>>> @with_spinner("Loading data...") ... def load_data(): ... return {"data": [1, 2, 3]} >>> result = load_data()
With log capture (prints appear above spinner):
>>> @with_spinner("Processing...", log_style="cyan") ... def process(): ... print("Step 1 complete") # Appears above spinner ... print("Step 2 complete") ... return True
Fixed log zone with bounded scrolling:
>>> @with_spinner("Building...", log_zone_height=5) ... def build(): ... for i in range(10): ... print(f"Step {i}") # Scrolls in 5-line zone ... return True
- class kstlib.ui.spinner.SpinnerWithLogZone(message='', *, log_zone_height=10, style=SpinnerStyle.BRAILLE, spinner_style='cyan', console=None, file=None, interval=0.08)[source]¶
Bases:
objectSpinner with a fixed position and a scrollable log zone.
The spinner stays fixed at the top while logs scroll in a zone below. When the zone is full, old logs are pushed out automatically.
- Parameters:
message (str) – Spinner message.
log_zone_height (int) – Number of lines for the log zone (default 10).
style (SpinnerStyle | str) – Spinner animation style.
spinner_style (str | None) – Rich style for spinner character.
console (Console | None) – Optional Rich console.
**kwargs – Additional Spinner arguments.
Examples
Create with custom log zone height:
>>> sz = SpinnerWithLogZone("Building...", log_zone_height=5) >>> sz._log_zone_height 5
Usage as context manager (terminal I/O):
>>> with SpinnerWithLogZone("Processing", log_zone_height=3) as sz: ... sz.log("Step 1 done") ... sz.log("Step 2 done") ... sz.update("Almost finished...")
- __init__(self, message: 'str' = '', *, log_zone_height: 'int' = 10, style: 'SpinnerStyle | str' = <SpinnerStyle.BRAILLE: ('⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷')>, spinner_style: 'str | None' = 'cyan', console: 'Console | None' = None, file: 'IO[str] | None' = None, interval: 'float' = 0.08) 'None' -> None[source]¶
Initialize the spinner with a fixed-height log zone and the usual Spinner options.
- stop(self, *, success: 'bool' = True, final_message: 'str | None' = None) 'None' -> None[source]¶
Stop the spinner and clean up the display.