Yet Another Terminal Spinner
Project description
yaspin: Yet Another Terminal Spinner for Python
Yaspin provides a full-featured terminal spinner to show the progress during long-hanging operations.
It is easy to integrate into existing codebase by using it as a context manager or as a function decorator:
import time
from yaspin import yaspin
# Context manager:
with yaspin():
time.sleep(3) # time consuming code
# Function decorator:
@yaspin(text="Loading...")
def some_operations():
time.sleep(3) # time consuming code
some_operations()
Yaspin also provides an intuitive and powerful API. For example, you can easily summon a shark:
import time
from yaspin import yaspin
with yaspin().white.bold.shark.on_blue as sp:
sp.text = "White bold shark in a blue sea"
time.sleep(5)
Features
- Runs at all major CPython versions (3.10, 3.11, 3.12, 3.13, 3.14), PyPy
- Supports all (70+) spinners from cli-spinners
- Supports all colors, highlights, attributes and their mixes from termcolor library
- Easy to combine with other command-line libraries, e.g. prompt-toolkit
- Flexible API, easy to integrate with existing code
- User-friendly API for handling POSIX signals
- Safe pipes and redirects:
$ python script_that_uses_yaspin.py > script.log
$ python script_that_uses_yaspin.py | grep ERROR
Table of Contents
Installation
From PyPI using pip package manager:
pip install --upgrade yaspin
Or install the latest sources from GitHub:
pip install https://github.com/pavdmyt/yaspin/archive/master.zip
Usage
Basic Example
import time
from random import randint
from yaspin import yaspin
with yaspin(text="Loading", color="yellow") as spinner:
time.sleep(2) # time consuming code
success = randint(0, 1)
if success:
spinner.ok("✅ ")
else:
spinner.fail("💥 ")
It is also possible to control spinner manually:
import time
from yaspin import yaspin
spinner = yaspin()
spinner.start()
time.sleep(3) # time consuming tasks
spinner.stop()
Run any spinner from cli-spinners
import time
from yaspin import yaspin
from yaspin.spinners import Spinners
with yaspin(Spinners.earth, text="Earth") as sp:
time.sleep(2) # time consuming code
# change spinner
sp.spinner = Spinners.moon
sp.text = "Moon"
time.sleep(2) # time consuming code
Any Colour You Like 🌈
import time
from yaspin import yaspin
with yaspin(text="Colors!") as sp:
# Support all basic termcolor text colors
colors = ("red", "green", "yellow", "blue", "magenta", "cyan", "white")
for color in colors:
sp.color, sp.text = color, color
time.sleep(1)
Advanced colors usage
import time
from yaspin import yaspin
from yaspin.spinners import Spinners
text = "Bold blink magenta spinner on cyan color"
with yaspin().bold.blink.magenta.bouncingBall.on_cyan as sp:
sp.text = text
time.sleep(3)
# The same result can be achieved by passing arguments directly
with yaspin(
Spinners.bouncingBall,
color="magenta",
on_color="on_cyan",
attrs=["bold", "blink"],
) as sp:
sp.text = text
time.sleep(3)
Run any spinner you want
import time
from yaspin import yaspin, Spinner
# Compose new spinners with custom frame sequence and interval value
sp = Spinner(["😸", "😹", "😺", "😻", "😼", "😽", "😾", "😿", "🙀"], 200)
with yaspin(sp, text="Cat!"):
time.sleep(3) # cat consuming code :)
Change spinner properties on the fly
import time
from yaspin import yaspin
from yaspin.spinners import Spinners
with yaspin(Spinners.noise, text="Noise spinner") as sp:
time.sleep(2)
sp.spinner = Spinners.arc # spinner type
sp.text = "Arc spinner" # text along with spinner
sp.color = "green" # spinner color
sp.side = "right" # put spinner to the right
sp.reversal = True # reverse spin direction
time.sleep(2)
Spinner with timer
import time
from yaspin import yaspin
with yaspin(text="elapsed time", timer=True) as sp:
time.sleep(3.1415)
sp.ok()
Custom streams
By default, yaspin outputs to sys.stdout. You can redirect spinner output to any stream using the stream parameter:
import sys
import time
from io import StringIO
from yaspin import yaspin
# Output to stderr instead of stdout
with yaspin(text="Processing...", stream=sys.stderr):
time.sleep(2)
# Capture spinner output in a string
output_buffer = StringIO()
with yaspin(text="Buffered output", stream=output_buffer):
time.sleep(1)
print("Captured:", output_buffer.getvalue())
For debugging stream lifecycle issues, enable warnings when operations are attempted on closed streams:
import time
from yaspin import yaspin
# Enable warnings for debugging (disabled by default)
with yaspin(text="Debug mode", warn_on_closed_stream=True):
time.sleep(2)
This is particularly useful in testing environments or when integrating with libraries that manage stream lifecycles.
Custom Ellipsis
If the text does not fit in the terminal it gets truncated, you can set a custom ellipsis to signal truncation.
import time
from yaspin import yaspin
with yaspin(text="some long text", ellipsis="...") as sp:
time.sleep(2)
Dynamic text
import time
from datetime import datetime
from yaspin import yaspin
class TimedText:
def __init__(self, text):
self.text = text
self._start = datetime.now()
def __str__(self):
now = datetime.now()
delta = now - self._start
return f"{self.text} ({round(delta.total_seconds(), 1)}s)"
with yaspin(text=TimedText("time passed:")):
time.sleep(3)
Writing messages
You should not write any message in the terminal using print while spinner is open.
To write messages in the terminal without any collision with yaspin spinner, a .write() method is provided:
import time
from yaspin import yaspin
with yaspin(text="Downloading images", color="cyan") as sp:
# task 1
time.sleep(1)
sp.write("> image 1 download complete")
# task 2
time.sleep(2)
sp.write("> image 2 download complete")
# finalize
sp.ok("✔")
Integration with other libraries
Utilizing hidden context manager it is possible to toggle the display of
the spinner in order to call custom methods that write to the terminal. This is
helpful for allowing easy usage in other frameworks like prompt-toolkit.
Using the powerful print_formatted_text function allows you even to apply
HTML formats and CSS styles to the output:
import sys
import time
from yaspin import yaspin
from prompt_toolkit import HTML, print_formatted_text
from prompt_toolkit.styles import Style
# override print with feature-rich ``print_formatted_text`` from prompt_toolkit
print = print_formatted_text
# build a basic prompt_toolkit style for styling the HTML wrapped text
style = Style.from_dict({
'msg': '#4caf50 bold',
'sub-msg': '#616161 italic'
})
with yaspin(text='Downloading images') as sp:
# task 1
time.sleep(1)
with sp.hidden():
print(HTML(
u'<b>></b> <msg>image 1</msg> <sub-msg>download complete</sub-msg>'
), style=style)
# task 2
time.sleep(2)
with sp.hidden():
print(HTML(
u'<b>></b> <msg>image 2</msg> <sub-msg>download complete</sub-msg>'
), style=style)
# finalize
sp.ok()
Handling POSIX signals
Handling keyboard interrupts (pressing Control-C):
import time
from yaspin import kbi_safe_yaspin
with kbi_safe_yaspin(text="Press Control+C to send SIGINT (Keyboard Interrupt) signal"):
time.sleep(5) # time consuming code
Handling other types of signals:
import os
import time
from signal import SIGTERM, SIGUSR1
from yaspin import yaspin
from yaspin.signal_handlers import default_handler, fancy_handler
sigmap = {SIGUSR1: default_handler, SIGTERM: fancy_handler}
with yaspin(sigmap=sigmap, text="Handling SIGUSR1 and SIGTERM signals") as sp:
sp.write("Send signals using `kill` command")
sp.write("E.g. $ kill -USR1 {0}".format(os.getpid()))
time.sleep(20) # time consuming code
Injecting spinner into a function
The @inject_spinner decorator provides access to the spinner instance from within the decorated function
by injecting it as the first argument. This gives you the flexibility to control the spinner's behavior directly.
import time
from yaspin import inject_spinner
from yaspin.core import Yaspin
@inject_spinner()
def simple_task(spinner: Yaspin, items: list) -> None:
for i, _ in enumerate(items, 1):
spinner.text = f"Processing item {i}/{len(items)}"
time.sleep(1)
spinner.ok("✓")
simple_task(["item1", "item2", "item3"])
More examples.
Development
Clone the repository:
git clone https://github.com/pavdmyt/yaspin.git
Install dev dependencies:
poetry install
Lint code:
make lint
Format code:
make fmt
Run tests:
make test
Contributing
- Fork it!
- Create your feature branch:
git checkout -b my-new-feature - Commit your changes:
git commit -m 'Add some feature' - Push to the branch:
git push origin my-new-feature - Submit a pull request
- Make sure tests are passing
License
- MIT - Pavlo Dmytrenko; https://twitter.com/pavdmyt
- Contains data from cli-spinners: MIT License, Copyright (c) Sindre Sorhus sindresorhus@gmail.com (sindresorhus.com)
Project details
Release history Release notifications | RSS feed
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 yaspin-3.4.0.tar.gz.
File metadata
- Download URL: yaspin-3.4.0.tar.gz
- Upload date:
- Size: 42.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a83a81ac7a9d161e116fb668a7e4d10d87fb18d02b4b08a17b7e472f465f3c90
|
|
| MD5 |
b04cd57eb692239dd94f34965b218eff
|
|
| BLAKE2b-256 |
8dc5826a862dcfcb9e85321f96d6f1b4b96b3b9bf37df6f63dce9cffd0b17053
|
Provenance
The following attestation bundles were made for yaspin-3.4.0.tar.gz:
Publisher:
publish-to-pypi.yml on pavdmyt/yaspin
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
yaspin-3.4.0.tar.gz -
Subject digest:
a83a81ac7a9d161e116fb668a7e4d10d87fb18d02b4b08a17b7e472f465f3c90 - Sigstore transparency entry: 747135195
- Sigstore integration time:
-
Permalink:
pavdmyt/yaspin@8062faf0bdc6854481dab72f49894b65766849de -
Branch / Tag:
refs/tags/v3.4.0 - Owner: https://github.com/pavdmyt
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-to-pypi.yml@8062faf0bdc6854481dab72f49894b65766849de -
Trigger Event:
push
-
Statement type:
File details
Details for the file yaspin-3.4.0-py3-none-any.whl.
File metadata
- Download URL: yaspin-3.4.0-py3-none-any.whl
- Upload date:
- Size: 21.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2a40572a38d39846d0df0a421733459481b7da17789f7a2618c3181bb0a82819
|
|
| MD5 |
c3cb23ce9b09881c477cb48c290ca257
|
|
| BLAKE2b-256 |
936f7403e6ae864a0a7f1cdd8814d39690062766e141339127f2b3469201ff6f
|
Provenance
The following attestation bundles were made for yaspin-3.4.0-py3-none-any.whl:
Publisher:
publish-to-pypi.yml on pavdmyt/yaspin
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
yaspin-3.4.0-py3-none-any.whl -
Subject digest:
2a40572a38d39846d0df0a421733459481b7da17789f7a2618c3181bb0a82819 - Sigstore transparency entry: 747135200
- Sigstore integration time:
-
Permalink:
pavdmyt/yaspin@8062faf0bdc6854481dab72f49894b65766849de -
Branch / Tag:
refs/tags/v3.4.0 - Owner: https://github.com/pavdmyt
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-to-pypi.yml@8062faf0bdc6854481dab72f49894b65766849de -
Trigger Event:
push
-
Statement type: