Skip to main content

progressbar2

The fastest progress bar in Python, maintained since 2012.

test status Coverage on master PyPI version Monthly downloads supported Python versions license

Wrapping a fast loop in a progress bar can cost more than the loop itself, so the overhead per iteration is the number that matters. This race replays the measured wall-clock times of the same 1,000,000-iteration loop, wrapped with each library's default settings:

Benchmark race: progress bars filling in proportion to each library's measured time for an identical million-iteration loop, with progressbar2 finishing first

Library Overhead per iteration Same loop, wall clock
progressbar2[fast] 3.6 ns 9.2 ms
rich 18.2 ns 23.8 ms
progressbar2 26.0 ns 31.5 ms
tqdm 52.1 ns 57.6 ms
alive-progress 243.3 ns 248.9 ms
click 1829.6 ns 1835.2 ms

The [fast] row is pip install 'progressbar2[fast]': an optional C iterator from the speedups package that counts natively and only calls back into Python when the bar actually needs a redraw. A plain install runs the pure-Python gate at 26 ns, still twice as light as tqdm. Import weight follows the same pattern: import progressbar costs 1.5 ms where tqdm takes 21.6 ms and rich 45.6 ms, which your command-line tools will notice.

Note: These numbers come from one machine (Apple Silicon, CPython 3.13, output to a real pty) and from this exact test, measured by benchmarks/bench.py. Your numbers will differ. click is measured but left out of the race animation because its 1.8 s total would flatten every other lane. Reproduce with python benchmarks/bench.py, full details in benchmarks/report.md.

Install

pip install progressbar2           # pure Python
pip install 'progressbar2[fast]'   # adds the native accelerator

Quick start

The common case needs one line around the iterable:

import time
import progressbar

for item in progressbar.progressbar(range(100), desc='Loading'):
    time.sleep(0.02)

Every example in the documentation runs live in the page. Press Run on any code block, no install required.

Colors, gradients and animated markers

A bar can carry a color gradient that shifts with progress, a fixed color, or an animated marker, and several styled bars can share the terminal through one MultiBar:

three styled progress bars: a red to green gradient, a blue to fuchsia gradient, and a cyan animated spinner

"""Three styled bars at once: two color gradients and a fixed-color spinner.

`gradient_colors` shifts a bar's fill color as its percentage grows, so
the download bar sweeps red through gold to green and the render bar
sweeps sky blue into fuchsia. The scan spinner normally cycles through
colours with its frames. Passing a single cyan colour keeps its colour
unchanged while the marker spins.
"""

import sys
import time

import progressbar
from progressbar.terminal import ColorGradient, colors
from progressbar.widgets import TGradientColors

STEPS = 24

HEALTH = ColorGradient(colors.red, colors.gold1, colors.green)
NEON = ColorGradient(colors.deep_sky_blue1, colors.fuchsia)


def main() -> None:
    with progressbar.MultiBar(fd=sys.stdout) as multibar:
        multibar['download'] = progressbar.ProgressBar(
            max_value=STEPS,
            widgets=[
                progressbar.Percentage(),
                ' ',
                progressbar.Bar(
                    gradient_colors=TGradientColors(fg=HEALTH, bg=None),
                ),
            ],
        )
        multibar['render'] = progressbar.ProgressBar(
            max_value=STEPS,
            widgets=[
                progressbar.Percentage(),
                ' ',
                progressbar.Bar(
                    gradient_colors=TGradientColors(fg=NEON, bg=None),
                ),
            ],
        )
        multibar['scan'] = progressbar.ProgressBar(
            max_value=progressbar.UnknownLength,
            widgets=[
                progressbar.AnimatedMarker(
                    gradient_colors=TGradientColors(fg=colors.cyan1, bg=None),
                ),
            ],
        )

        for step in range(STEPS):
            multibar['download'].update(step + 1)
            multibar['render'].update(max(0, step - 4))
            multibar['scan'].update(step + 1)
            # Longer than the bars' 0.05s update gate, so every step
            # lands as a visible redraw.
            time.sleep(0.1)

        multibar['render'].update(STEPS)
        for bar in multibar.values():
            bar.finish()


if __name__ == '__main__':
    main()

Logs above a live bar

Anything the program prints while a bar is running would normally tear the bar apart. With redirect_stdout the output lands cleanly above the bar instead:

progressbar2 showing clean log output appearing above a moving progress bar

"""A build log printing above a progress bar without corrupting it."""

import time

import progressbar

STEPS = 24


def main() -> None:
    with progressbar.ProgressBar(
        max_value=STEPS,
        prefix='Build ',
        redirect_stdout=True,
    ) as bar:
        for step in range(STEPS):
            if step in {8, 16}:
                print(f'log: completed step {step}')
            bar.update(step + 1)
            # Longer than the bar's 0.05s update gate, so every step
            # lands as a visible redraw.
            time.sleep(0.1)


if __name__ == '__main__':
    main()

Logging integrates the same way, covered in the logging how-to.

Many bars at once

MultiBar lays out any number of named bars, updates them from any thread, and waits for all of them on exit:

multiple progress bars updating together in one terminal

"""Two named bars progressing at different rates in one terminal."""

import sys
import time

import progressbar

STEPS = 24


def main() -> None:
    with progressbar.MultiBar(fd=sys.stdout) as multibar:
        build = multibar['build']
        test = multibar['test']
        build.max_value = STEPS
        test.max_value = STEPS
        for step in range(STEPS):
            build.update(step + 1)
            test.update(min(STEPS, max(0, round((step - 3) * 1.2))))
            # Longer than the bars' 0.05s update gate, so every step
            # lands as a visible redraw.
            time.sleep(0.1)

        # Reaching max_value doesn't finish a bar -- only finish() does.
        # A MultiBar waits for every bar to report finished() before its
        # context manager can exit, so without these calls the block
        # above would hang forever on exit.
        build.finish()
        test.finish()


if __name__ == '__main__':
    main()

Parallel execution

Running a function over a batch of items usually means wiring an executor, a results collection, and a progress display together by hand. One call does all three, on threads, processes, or asyncio:

import progressbar

results = progressbar.map(fetch, urls, workers=8)  # threads
results = progressbar.map(crunch, files, pool='process')  # processes
results = await progressbar.amap(fetch, urls)  # asyncio

# A progress-bar'd xargs -P:
progressbar.run('gzip -k {}', files, workers=4)

# An overall bar plus one bar per in-flight task:
progressbar.map(crunch, files, workers=4, bar='multi')

Results come back in input order, imap/imap_unordered stream them instead, and gather is a drop-in asyncio.gather with a bar. The animation below runs eight tasks on a thread pool, each reporting sub-progress through its own bar:

an overall progress bar plus one bar per running task, driven by progressbar.map

"""One call fans work out to threads and renders every bar for you.

`progressbar.map` runs `crunch` over the batch on a thread pool and
drives one overall bar plus a live bar per running task. The everyday
spelling is `bar='multi'`. Handing it a `MultiBar` instance instead, as
here, lets the finished overall bar stay on screen when the run ends.
Inside a task, `progressbar.current_task_bar()` hands back that task's
own bar so it can report sub-progress too.
"""

import sys
import time

import progressbar

FILES = [
    'alpha.dat',
    'bravo.dat',
    'charlie.dat',
    'delta.dat',
    'echo.dat',
    'foxtrot.dat',
    'golf.dat',
    'hotel.dat',
]


def crunch(path: str) -> str:
    # Deterministic, per-file amount of work: later files take longer,
    # so the bars visibly finish one after another.
    bar = progressbar.current_task_bar()
    for block in range(4 + FILES.index(path)):
        if bar is not None:
            bar.update(block + 1)
        time.sleep(0.05)
    return path


def main() -> None:
    multibar = progressbar.MultiBar(fd=sys.stdout, sort_reverse=False)
    results = progressbar.map(crunch, FILES, workers=8, bar=multibar)
    assert results == FILES
    # One more beat before the interpreter exits, so the finished
    # overall bar's final redraw reaches the terminal.
    time.sleep(0.1)


if __name__ == '__main__':
    main()

Errors, timeouts, pools, and the decorator form are covered in the parallel execution guide.

Replace pv on the command line

Installing the package also installs a progressbar command and its bar shorthand, a Python implementation of the classic Unix pv: it copies input to output and draws the transfer on stderr, so it drops into pipelines:

# File to file, with percentage, timer, ETA, rate and byte count:
progressbar --progress --timer --eta --rate --bytes data.bin -o copy.bin

# In a pipeline, data on stdout and progress on stderr. `bar` is the
# same command with three keystrokes:
tar cf - src/ | bar --bytes --rate > backup.tar

the progressbar command copying a file with percentage, timer, ETA, rate and byte count displays

The animation is this exact transfer, driven in-process by docs/examples/readme/cli.py: 4 MiB copied at a rate-limited 2 MiB/s. Rate limiting (--rate-limit), line counting (--line-mode), and most other pv flags are there, progressbar --help lists the full set.

Unknown length and animated bars

Work without a known total still gets a useful bar, an animated marker with a counter instead of a percentage:

unknown length progress with an animated marker

"""A bar for work whose total is not known up front."""

import time

import progressbar


def main() -> None:
    with progressbar.ProgressBar(
        max_value=progressbar.UnknownLength,
    ) as bar:
        for value in range(0, 120, 10):
            bar.update(value)
            # Longer than the bar's 0.05s update gate, so every step
            # lands as a visible redraw.
            time.sleep(0.1)


if __name__ == '__main__':
    main()

Boring where it counts

The features above are the flashy half. The other half is why the package has survived since 2012:

  • Supported non-stop since 2012, with 111 releases on PyPI, and since 2008 before that as the original progressbar library, which progressbar2 still replaces drop-in.
  • 1133 tests at 100% branch coverage, enforced in CI on every commit.
  • Fully typed (PEP 561 py.typed), so your type checker sees the real signatures.
  • Python 3.10 through 3.14, plus PyPy, with 3.15 pre-releases already in CI.
  • The animated demos on this page are rendered from real captured terminal output and re-verified byte-for-byte in CI, so what you see is what the library actually draws.

Known terminal caveats

  • JetBrains IDEs need "Enable terminal in output console" for advanced terminal behavior such as MultiBar.
  • IDLE does not support terminal progress bars.
  • Jupyter buffers stdout, so call sys.stdout.flush() when output appears late.

Project history

progressbar2 is based on the old Python progressbar package that was published on the now defunct Google Code. Since that project was completely abandoned by its developer and the developer did not respond to email, I decided to fork the package.

This package is still backwards compatible with the original progressbar package so you can use it as a drop-in replacement for existing projects.

Support

progressbar2 is maintained by Rick van Hattem in his own time. Most of that time goes on terminals nobody tests against and on keeping the bar out of your logging.

If it saved you an afternoon, a tip covers an hour of issue triage: Ko-fi or GitHub Sponsors.

If your company funds its dependencies, this package is on thanks.dev.

ko-fi

Metadata

Release files for progressbar2 4.7.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for progressbar2 4.7.0
File Size Uploaded Provenance Actions
progressbar2-4.7.0.tar.gz 751.5 kB With provenance Details for progressbar2-4.7.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for progressbar2 4.7.0
File Interpreter ABI Platform Actions
progressbar2-4.7.0-py3-none-any.whl Python 3 none any Details for progressbar2-4.7.0-py3-none-any.whl

Total release size: 902.6 kB

Release files / progressbar2-4.7.0.tar.gz

Download URL progressbar2-4.7.0.tar.gz
Size 751.5 kB
Tags Source
SHA-256 checksum
How to use checksums
f4122439b00dca41424f787ea5148478685d265742078c1c7c3ede31f06bd001
BLAKE2b-256 checksum
How to use checksums
1edd63ac006e821594bf6ec5cd7557f16c44d8c209108578ea01bc1f715baafc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 5, 2026.

Transparency log

Release files / progressbar2-4.7.0-py3-none-any.whl

Download URL progressbar2-4.7.0-py3-none-any.whl
Size 151.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ff929d767c1fd7656bcefa7f2132aa174aa8746d7de580edfcfc1c6a738825da
BLAKE2b-256 checksum
How to use checksums
3c914e41878c890ff425b33135df7e97a992a55c8e96044a0d931c39d21f30bc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 5, 2026.

Transparency log

Security for progressbar2 4.7.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for progressbar2-4.7.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 5, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

4.7.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page