Beginner
You have a data pipeline, a batch file processor, or a training loop that takes minutes to run. You stare at a blank terminal, no idea if it is working, 10% done, or silently crashed three minutes ago. The standard print(f'Processing {i}/{total}') approach floods the screen with output that doesn’t actually tell you the most important thing: how much time is left.
alive-progress is an animated terminal progress bar library that gives you real-time throughput stats, a live spinner, estimated time remaining, and configurable visual styles — all in a few lines of code. It goes well beyond tqdm in terms of visual richness and customization without sacrificing simplicity.
This article covers everything: basic usage with an iterable, the context manager API for manual advancement, customizing bar styles and themes, printing messages while a bar runs, nested bars for multi-stage pipelines, and a real-world batch image processor. After reading this, your terminal scripts will never look like they are frozen again.
Your First Progress Bar: Quick Example
The simplest way to use alive-progress is to wrap your iterable in alive_it() — a drop-in replacement for any for loop:
# quick_bar.py
from alive_progress import alive_it
import time
items = list(range(50))
for item in alive_it(items, title='Processing items'):
time.sleep(0.05) # Simulate work
print('Done!')
Terminal output (animated — static representation):
Processing items |████████████████████████| 50/50 [100%] in 2.6s (19.2/s)
alive_it() wraps any iterable and displays a real-time animated bar as you iterate. It shows the count, percentage, elapsed time, and throughput (items per second). When the loop finishes, the bar snaps to a clean static line showing the final stats. No extra code, no boilerplate — just wrap your existing loop.
What Is alive-progress?
alive-progress is a Python terminal progress bar library focused on visual quality and real-time statistics. Unlike simpler libraries, it uses ANSI escape codes to update the same terminal line in-place rather than printing new lines, creating a true animation effect that looks modern even on basic terminals.
| Feature | alive-progress | tqdm |
|---|---|---|
| Animated spinner | Yes (many styles) | Limited |
| Real-time throughput | Yes (ETA + rate) | Yes |
| Print while running | Yes (bar.text()) | Yes (tqdm.write()) |
| Nested bars | Yes | Yes |
| Custom bar styles | 40+ themes built-in | Limited |
| Unknown total | Yes (spinner mode) | Yes |
| Jupyter notebook | Limited | Better |
Install with: pip install alive-progress. Python 3.6+ is supported. No heavy dependencies — the only requirement is about-time, which is automatically installed.
Context Manager API
When your processing is not a simple loop — for example, you fetch items from an API in batches, or in you update at irregular intervals — use the context manager API with alive_bar() and manually call bar() to advance the counter.
# context_bar.py
from alive_progress import alive_bar
import time
total_files = 40
with alive_bar(total_files, title='Compressing files') as bar:
for i in range(total_files):
time.sleep(0.04) # Simulate compression
bar() # Advance the bar by 1
print('All files compressed!')
Output (static):
Compressing files |████████████████████████| 40/40 [100%] in 1.7s (23.5/s)
You can also advance by more than 1 at a time: bar(5) increments the counter by 5. This is useful when processing variable-size batches where the work unit is a batch, not an individual item. Pass None as the total to create an indeterminate bar that spins without a percentage — appropriate when you do not know the total count upfront.
Printing Messages While the Bar Runs
One of the most useful features is the ability to print status messages without disrupting the animated bar. Use bar.text() to show a live subtitle below the bar, or print() within the context — alive-progress intercepts it and displays it above the bar without breaking the animation.
# bar_messages.py
from alive_progress import alive_bar
import time
files = ['config.yaml', 'data.csv', 'model.pkl', 'report.html', 'archive.zip']
with alive_bar(len(files), title='Uploading') as bar:
for filename in files:
bar.text(f'-> {filename}') # Live subtitle
time.sleep(0.3)
size_kb = len(filename) * 100 # Fake file size
print(f'Uploaded {filename} ({size_kb} KB)')
bar()
Terminal output (sequential):
Uploaded config.yaml (1000 KB)
Uploaded data.csv (800 KB)
Uploaded model.pkl (900 KB)
Uploaded report.html (1100 KB)
Uploaded archive.zip (1000 KB)
Uploading |████████████████████████| 5/5 [100%] in 1.5s (3.3/s)
bar.text() updates the subtitle line in real time — so while the bar is animating, users see which file is currently uploading. When the loop completes, the printed lines stay in the terminal history above the final bar summary. This pattern is ideal for batch jobs where users want to know both overall progress and current activity.
Customizing Bar Styles
alive-progress ships with dozens of built-in bar and spinner styles. You can preview them all with alive_progress.styles.showtime(), or set them directly in the alive_bar() call or via the global config_handler.
# custom_styles.py
from alive_progress import alive_bar, config_handler
import time
# Set global defaults for all bars in this session
config_handler.set_global(
bar='classic',
spinner='classic',
title_length=20,
length=40
)
items = list(range(30))
with alive_bar(len(items), title='Classic bar') as bar:
for _ in items:
time.sleep(0.02)
bar()
# Override per-bar
with alive_bar(len(items), title='Smooth bar', bar='smooth', spinner='waves') as bar:
for _ in items:
time.sleep(0.02)
bar()
# Force mode -- for scripts called from CI/CD without a TTY
with alive_bar(len(items), title='Force mode', force_tty=True) as bar:
for _ in items:
time.sleep(0.02)
bar()
Output (static representations):
Classic bar [######################################] 30/30 in 0.7s
Smooth bar |████████████████████████████████████| 30/30 in 0.7s
Force mode |████████████████████████████████████| 30/30 in 0.7s
The config_handler.set_global() call persists across all subsequent bars in the same Python process — useful for setting a house style once at the start of a script. Individual bars can override global settings by passing keyword arguments. The force_tty=True argument is important for CI/CD pipelines: without it, alive-progress detects a non-interactive terminal and falls back to plain print output, which can look garbled in logs.
Unknown Total: Spinner Mode
Sometimes you do not know how many items there are before you start — streaming API responses, crawling a website, or reading from a queue. Pass None as the total and alive-progress switches to an indefinite spinner with a live count.
# spinner_mode.py
from alive_progress import alive_bar
import time
def stream_events():
# Simulate variable-length stream
for i in range(25):
time.sleep(0.08)
yield {'event': f'event_{i}', 'value': i * 3}
collected = []
with alive_bar(None, title='Streaming events') as bar:
for event in stream_events():
collected.append(event)
bar.text(f'Last: {event["event"]}')
bar()
print(f'Collected {len(collected)} events')
Output:
Streaming events |/| 25 in 2.1s (12.0/s)
Collected 25 events
In spinner mode the bar shows a rotating character instead of a fill animation, along with a raw count and throughput rate. This gives users feedback that the script is alive and working even when the end point is unknown.
Real-Life Example: Batch CSV Processor
# batch_csv_processor.py
import csv
import io
import time
from alive_progress import alive_bar
# Generate fake CSV data in memory
def make_fake_csv(n_rows):
buf = io.StringIO()
writer = csv.DictWriter(buf, fieldnames=['id', 'name', 'score', 'active'])
writer.writeheader()
for i in range(n_rows):
writer.writerow({
'id': i + 1,
'name': f'user_{i+1}',
'score': (i * 37) % 100,
'active': i % 3 != 0
})
buf.seek(0)
return buf
def process_csv(file_obj, bar, report):
reader = csv.DictReader(file_obj)
rows_processed = 0
for row in reader:
# Simulate row-level processing
time.sleep(0.002)
if row['active'] == 'True' and int(row['score']) > 50:
report['high_scorers'] += 1
rows_processed += 1
bar()
return rows_processed
# Simulate 4 CSV files of varying size
file_sizes = [80, 120, 60, 100]
file_names = [f'batch_{i+1}.csv' for i in range(len(file_sizes))]
report = {'high_scorers': 0, 'total_rows': 0}
total_rows = sum(file_sizes)
with alive_bar(total_rows, title='Processing batches') as bar:
for name, size in zip(file_names, file_sizes):
bar.text(f'-> {name}')
fake_csv = make_fake_csv(size)
rows = process_csv(fake_csv, bar, report)
report['total_rows'] += rows
print(f' Finished {name}: {rows} rows')
print(f'\nSummary: {report["total_rows"]} rows, {report["high_scorers"]} high scorers')
Output:
Finished batch_1.csv: 80 rows
Finished batch_2.csv: 120 rows
Finished batch_3.csv: 60 rows
Finished batch_4.csv: 100 rows
Processing batches |████████████████████████| 360/360 [100%] in 0.8s (450.0/s)
Summary: 360 rows, 48 high scorers
The bar advances once per row, so the animation accurately reflects actual progress across all files. bar.text() shows which file is being processed in real time. The print() calls inside the loop appear above the bar without disrupting the animation. Adapt this pattern to process real CSV files from disk, replace the fake data with csv.DictReader(open(path)), and add error handling for malformed rows.
Frequently Asked Questions
When should I use alive-progress vs tqdm?
Use alive-progress when you want richer animations and real-time text messages in interactive terminal scripts. Use tqdm when you need Jupyter notebook integration, pandas integration (df.progress_apply()), or when you are in a CI environment where animation is less important. Both are excellent; the choice is largely aesthetic.
alive-progress looks broken in my CI/CD logs. Why?
In non-TTY environments (CI logs, redirected output), alive-progress falls back to plain output by default, which can include ANSI escape codes that look garbled in log viewers. Fix it by adding force_tty=False to suppress the bar entirely in CI, or use enrich_print=False to prevent print interception. Alternatively, wrap the bar call in a check: if sys.stdout.isatty():.
What happens if I call bar() more times than the total?
The bar will exceed 100% — it does not hard-cap at the total. This is actually useful for cases where your estimate was wrong. If you need a strict cap, track the count manually and stop calling bar() when you reach the expected total. The final stats line will show the actual count regardless.
Does alive-progress work with multithreaded code?
Yes. The bar() call is thread-safe. You can call it from multiple threads concurrently and the counter will advance correctly. The display is updated in the main thread, so the animation stays smooth even under high call frequency from background threads.
What is the calibrate parameter?
The calibrate parameter controls the throughput animation speed. When your processing rate is very fast (thousands of items per second), the animation might update faster than it renders smoothly. Passing calibrate=100 tells alive-progress to treat 100 items/s as “full speed” for animation purposes, slowing down the visual effect to look natural. For most use cases the default auto-calibration works fine.
Conclusion
You have covered the full alive-progress toolkit: alive_it() for simple loop wrapping, the context manager alive_bar() for manual advancement, bar.text() for live subtitles, global and per-bar style configuration with config_handler, spinner mode for unknown totals, and the batch CSV example that ties everything together.
The best immediate use of this library is replacing any long-running script that currently outputs nothing — data import jobs, batch API calls, file processing pipelines — with a single with alive_bar(total) as bar: wrapper. The visual feedback improves debugging and gives users confidence that the script is working. Extend the CSV processor by adding a second nested bar for per-row sub-tasks, or integrate it with asyncio for parallel batch processing.
Browse all available styles with from alive_progress.styles import showtime; showtime(), and find the full documentation at github.com/rsalmei/alive-progress.