Beautiful terminal spinners in Python
—
Core functionality for creating, starting, stopping, and managing spinner lifecycle. Provides multiple usage patterns including context managers, decorators, and manual control.
Creates a new spinner instance with comprehensive configuration options for appearance and behavior.
def __init__(
self,
text="",
color="cyan",
text_color=None,
spinner=None,
animation=None,
placement="left",
interval=-1,
enabled=True,
stream=sys.stdout
):
"""
Initialize a Halo spinner.
Parameters:
- text (str): Text to display alongside spinner (default: "")
- color (str): Spinner color - "cyan", "red", "green", etc. (default: "cyan")
- text_color (str): Text color, same options as color (default: None)
- spinner (str|dict): Spinner type name or custom spinner definition (default: "dots")
- animation (str): Text animation for long strings - "bounce", "marquee" (default: None)
- placement (str): Spinner position - "left" or "right" (default: "left")
- interval (int): Frame interval in milliseconds, -1 for spinner default (default: -1)
- enabled (bool): Whether spinner is active (default: True)
- stream (io.TextIOWrapper): Output stream (default: sys.stdout)
"""Start and stop spinners manually with full control over timing and lifecycle.
def start(self, text=None):
"""
Start the spinner on a separate thread.
Parameters:
- text (str, optional): Update spinner text when starting
Returns:
- Halo: Self for method chaining
"""
def stop(self):
"""
Stop the spinner and clear the line.
Returns:
- Halo: Self for method chaining
"""
def clear(self):
"""
Clear the current line and return cursor to start.
Returns:
- Halo: Self for method chaining
"""
def render(self):
"""
Manually render frames in a loop until stopped.
Returns:
- Halo: Self for method chaining
"""
def frame(self):
"""
Build and return the next frame to be rendered.
Returns:
- str: Complete frame string with spinner and text
"""
def text_frame(self):
"""
Build and return the text portion of the frame.
Returns:
- str: Text frame with color and animation applied
"""Usage Example:
from halo import Halo
import time
# Basic manual control
spinner = Halo(text='Loading data', spinner='dots')
spinner.start()
# Update text while running
time.sleep(1)
spinner.text = 'Processing data'
time.sleep(1)
spinner.text = 'Saving results'
time.sleep(1)
spinner.stop()Use spinners in with statements for automatic lifecycle management and exception safety.
def __enter__(self):
"""
Start spinner for context manager usage.
Returns:
- Halo: Self instance
"""
def __exit__(self, type, value, traceback):
"""
Stop spinner when exiting context.
Parameters:
- type: Exception type (if any)
- value: Exception value (if any)
- traceback: Exception traceback (if any)
"""Usage Example:
from halo import Halo
import time
# Context manager automatically handles start/stop
with Halo(text='Loading', spinner='dots'):
time.sleep(2)
# Spinner automatically stops when exiting block
# Works with exceptions too
try:
with Halo(text='Risky operation', spinner='dots'):
time.sleep(1)
raise ValueError("Something went wrong")
except ValueError:
print("Exception occurred, spinner was cleaned up")Apply spinners to functions as decorators for seamless integration with existing code.
def __call__(self, f):
"""
Allow Halo instance to be used as function decorator.
Parameters:
- f (callable): Function to wrap with spinner
Returns:
- callable: Wrapped function
"""Usage Example:
from halo import Halo
import time
# Decorator with configuration
@Halo(text='Processing data', spinner='dots', color='green')
def process_data():
time.sleep(3)
return "processed_data"
# Function runs with spinner automatically
result = process_data()
# Reusable spinner decorator
spinner_decorator = Halo(text='Working', spinner='star')
@spinner_decorator
def task_one():
time.sleep(2)
@spinner_decorator
def task_two():
time.sleep(1)
task_one()
task_two()Get information about the current spinner state and thread ID.
@property
def spinner_id(self):
"""
Get the thread ID of the currently running spinner.
Returns:
- str: Thread name/ID of the spinner thread, or None if not running
"""Usage Example:
from halo import Halo
import time
spinner = Halo(text='Processing', spinner='dots')
print(f"Spinner ID before start: {spinner.spinner_id}") # None
spinner.start()
print(f"Spinner ID while running: {spinner.spinner_id}") # Thread-1 (or similar)
time.sleep(1)
spinner.stop()
print(f"Spinner ID after stop: {spinner.spinner_id}") # Nonespinner = Halo(text='Starting', spinner='dots')
spinner.start()
for i in range(1, 101):
spinner.text = f'Progress: {i}%'
time.sleep(0.1)
spinner.stop()import sys
# Disable spinner for non-interactive environments
spinner = Halo(
text='Processing',
enabled=sys.stdout.isatty() # Only show in terminal
)
with spinner:
time.sleep(2)import io
# Capture spinner output
buffer = io.StringIO()
spinner = Halo(text='Working', stream=buffer)
with spinner:
time.sleep(1)
# spinner output captured in bufferInstall with Tessl CLI
npx tessl i tessl/pypi-halo