We are going to build a spinning status indicator that runs while other code is executing.
It will look like this:

Why?
You’ve got some code that takes a while to run.
import time
import random
def slow_func():
seconds = random.randint(2, 5)
time.sleep(seconds)
print("Done!")
if __name__ == '__main__':
slow_func()
Now, when you execute this, you’ll see the following:

You’ll wonder whether your code or your system is working correctly or frozen. Who knows?
Introducing a spinner
Ideally, we’d like to show some sort of activity while our code is executing. We can do that with a spinner. To create a spinner, we can use:
import time
import itertools
def spin():
spinners = ["|", "/", "-", "\\"]
for c in itertools.cycle(spinners):
print(f"\r{c}", end="")
time.sleep(0.1)
- We introduce a list of spinners (
| / - \). The double backslash is used because of escaping. - Using
itertools.cycle, we can create an endless cycle of our spinner elements. - In each iteration, we print one of characters.
- By default, Python ends a print statement with a newline. We disable that by printing an empty string (
end="") - By putting
\rin front of our character, we move our cursor back to the start of the line. This is called a carriage return. - We sleep for 100ms.
- By default, Python ends a print statement with a newline. We disable that by printing an empty string (
Combine the spinner with the code
Now, combining them can be done like this:
spin()
slow_func()
But obviously this does not work, since our code executes sequentially.
First the spinner runs to completion, then slow_func will run.
Due to the endless nature of itertools.cycle, our code in spin() never stops.
To solve this, we can run our spinner in its own thread, which allows us to run code in parallel:
import threading
if __name__ == '__main__':
thread = threading.Thread(target=spin)
thread.daemon = True
thread.start()
slow_func()
- We start a new thread, with the
spinfunction as its target. - We set
thread.daemonto True, to make the thread run in the background. - We start the thread.
- We call our slow function
Here’s what it looks like:

Making it awesome
If you want to reuse your code, it wouldn’t be so nice. To fix that, we can introduce a context manager. This will make usage look like this:
with Spinner():
slow_func()
Here’s how we write the context manager:
class Spinner:
def __init__(self):
self.running = False
self.thread = threading.Thread(target=self.spin)
self.thread.daemon = True
def spin(self):
spinners = ["|", "/", "-", "\\"]
for c in itertools.cycle(spinners):
if not self.running:
print("\r", end="")
break
print(f"\r{c}", end="")
time.sleep(0.1)
def __enter__(self):
self.running = True
self.thread.start()
def __exit__(self, exc_type, exc_val, exc_tb):
self.running = False
The logic is:
- We initialize the instance with
Spinner(). This calls__init__(), which sets running toFalseand creates the thread. - After the
with Spinner():line,Spinner.__enter__()gets called. We now enter the context and the thread starts running. - Our slow function runs. Meanwhile, every 100ms, a spin character gets printed.
- Our slow function ends and we exit the
withblock. Now,Spinner.__exit__()gets called. Running will be set toFalse, which means thespin()method will break out of its loop, once it detectsself.runningisFalse.
Further improvements
We can make our code even more dynamic, by allowing you to set the spin timeout and the spinners during class initialization. Here’s the full code:
import itertools
import threading
import time
class Spinner:
def __init__(self, timeout: float = 0.1, spinners: list = ["|", "/", "-", "\\"]):
self.timeout = timeout
self.spinners = spinners
self.running = False
self.thread = threading.Thread(target=self.spin)
self.thread.daemon = True
def spin(self):
for c in itertools.cycle(self.spinners):
if not self.running:
print("\r", end="")
break
print(f"\r{c}", end="")
time.sleep(self.timeout)
def __enter__(self):
self.running = True
self.thread.start()
def __exit__(self, exc_type, exc_val, exc_tb):
self.running = False
You could use any spinners you like, for example:
["⢿", "⣻", "⣽", "⣾", "⣷", "⣯", "⣟", "⡿"]
["👆", "👉", "👇", "👈"]
["| ", " | ", " | ", " |"]
You can find many more examples online, or you can simply make your own.
Final thoughts
I hoped you learned something about how we can indicate activity while you are running your program interactively. If you need a more extensive approach, you can use a library like rich.