pavdmyt
yaspin
Python

A lightweight terminal spinner for Python ๐ŸŽ

Last updated Jun 21, 2026
912
Stars
44
Forks
9
Issues
0
Stars/day
Attention Score
57
Language breakdown
No language data available.
โ–ธ Files click to expand
README

logo

yaspin: Yet Another Terminal Spinner for Python


Coverage pypi Versions Ask DeepWiki

Wheel Examples DownloadsTot DownloadsW

Yaspin provides a full-featured terminal spinner to show the progress during long-hanging operations.

demo

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)

shark

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 scriptthatuses_yaspin.py > script.log
$ python scriptthatuses_yaspin.py | grep ERROR

Table of Contents

- Basic Example - Spinners from cli-spinners - Colors - Advanced colors usage - Building custom spinners - Changing spinner properties on the fly - Timer - Custom streams - Custom Ellipsis - Dynamic text - Writing messages - Integration with other libraries - Handling POSIX signals - Injecting spinner into a function

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

basic</em>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

cli</em>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 ๐ŸŒˆ

basic</em>colors

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

advanced</em>colors

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", , attrs=["bold", "blink"], ) as sp: sp.text = text time.sleep(3)

Run any spinner you want

custom</em>spinners

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

sp</em>properties

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", warnonclosed_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

write</em>text

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

hide</em>show

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 printformattedtext function allows you even to apply HTML formats and CSS styles to the output:

import sys
import time

from yaspin import yaspin from prompttoolkit import HTML, printformatted_text from prompt_toolkit.styles import Style

override print with feature-rich `printformattedtext from prompt_toolkit

print = printformattedtext

build a basic prompt_toolkit style for styling the HTML wrapped text

style = Style.from_dict({ &#39;msg&#39;: &#39;#4caf50 bold&#39;, &#39;sub-msg&#39;: &#39;#616161 italic&#39; })

with yaspin(text=&#39;Downloading images&#39;) as sp: # task 1 time.sleep(1) with sp.hidden(): print(HTML( u&#39;&lt;b&gt;&gt;&lt;/b&gt; &lt;msg&gt;image 1&lt;/msg&gt; &lt;sub-msg&gt;download complete&lt;/sub-msg&gt;&#39; ), style=style)

# task 2 time.sleep(2) with sp.hidden(): print(HTML( u&#39;&lt;b&gt;&gt;&lt;/b&gt; &lt;msg&gt;image 2&lt;/msg&gt; &lt;sub-msg&gt;download complete&lt;/sub-msg&gt;&#39; ), style=style)

# finalize sp.ok()</code></pre>

Handling POSIX signals

Handling keyboard interrupts (pressing Control-C):

<pre><code class="lang-python">import time

from yaspin import kbisafeyaspin

with kbisafeyaspin(text=&quot;Press Control+C to send SIGINT (Keyboard Interrupt) signal&quot;): time.sleep(5) # time consuming code</code></pre>

Handling other types of signals:

<pre><code class="lang-python">import os import time from signal import SIGTERM, SIGUSR1

from yaspin import yaspin from yaspin.signalhandlers import defaulthandler, fancy_handler

sigmap = {SIGUSR1: defaulthandler, SIGTERM: fancyhandler} with yaspin(sigmap=sigmap, text=&quot;Handling SIGUSR1 and SIGTERM signals&quot;) as sp: sp.write(&quot;Send signals using kill command&quot;) sp.write(&quot;E.g. $ kill -USR1 {0}&quot;.format(os.getpid())) time.sleep(20) # time consuming code</code></pre>

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.

<pre><code class="lang-python">import time from yaspin import inject_spinner from yaspin.core import Yaspin

@inject_spinner() def simple_task(spinner: Yaspin, items: list) -&gt; None: for i, _ in enumerate(items, 1): spinner.text = f&quot;Processing item {i}/{len(items)}&quot; time.sleep(1) spinner.ok(&quot;โœ“&quot;)

simple_task([&quot;item1&quot;, &quot;item2&quot;, &quot;item3&quot;])</code></pre>

More examples.

Development

Clone the repository:

<pre><code class="lang-bash">git clone https://github.com/pavdmyt/yaspin.git</code></pre>

Install dev dependencies:

<pre><code class="lang-bash">poetry install</code></pre>

Lint code:

<pre><code class="lang-bash">make lint</code></pre>

Format code:

<pre><code class="lang-bash">make fmt</code></pre>

Run tests:

<pre><code class="lang-bash">make test</code></pre>

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)

ยฉ 2026 GitRepoTrend ยท pavdmyt/yaspin ยท Updated daily from GitHub