alive-progress 3.3.0
A new kind of Progress Bar, with real-time throughput, ETA, and very cool animations!
License: MIT
Requires Python: <4,>=3.9
Keywords: progress, bar, progress-bar, progressbar, spinner, eta, monitoring, python, terminal, multithreaded, REPL, alive, animated, visual, feedback, simple, live, efficient, monitor, stats, elapsed, time, throughput

[<img align="right" src="https://cdn.buymeacoffee.com/buttons/default-orange.png" width="217px" height="51x">](https://www.buymeacoffee.com/rsalmei)
[<img align="right" alt="Donate with PayPal button" src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif">](https://www.paypal.com/donate?business=6SWSHEB5ZNS5N&no_recurring=0&item_name=I%27m+the+author+of+alive-progress%2C+clearly+and+about-time.+Thank+you+for+appreciating+my+work%21&currency_code=USD)

![alive-progress logo](https://raw.githubusercontent.com/rsalmei/alive-progress/main/img/alive-logo.gif)

# alive-progress

[![Maintenance](https://img.shields.io/badge/maintained-yes-brightgreen.svg)](https://gitHub.com/rsalmei/alive-progress/graphs/commit-activity)
[![PyPI version](https://img.shields.io/pypi/v/alive-progress.svg)](https://pypi.python.org/pypi/alive-progress/)
[![PyPI pyversions](https://img.shields.io/pypi/pyversions/alive-progress.svg)](https://pypi.python.org/pypi/alive-progress/)
[![Downloads](https://static.pepy.tech/personalized-badge/alive-progress?period=month&units=international_system&left_color=grey&right_color=orange&left_text=downloads/month)](https://pepy.tech/project/alive-progress)
[![Downloads](https://static.pepy.tech/personalized-badge/alive-progress?period=total&units=international_system&left_color=grey&right_color=orange&left_text=downloads)](https://pepy.tech/project/alive-progress)
![GitHub Sponsors](https://img.shields.io/github/sponsors/rsalmei)

Have you ever wondered where your lengthy processing was at, and when would it finish? Do you usually hit `RETURN` several times to make sure it didn't crash, or the SSH connection didn't freeze? Have you ever thought it'd be awesome to be able to _pause some processing_ without hassle, return to the Python prompt to manually fix some items, then _seamlessly resume_ it? I did...

I've started this new progress bar thinking about all that, behold the **alive-progress**! 😃

![alive-progress demo](https://raw.githubusercontent.com/rsalmei/alive-progress/main/img/alive-demo.gif)

Introducing the newest concept in progress bars for Python! `alive-progress` is in a class of its own, with an array of cool features that set it apart. Here are a few highlights:

- A mesmerizing **live spinner** that reacts to your actual processing speed, i.e., it dynamically gets faster or slower with your throughput, and clearly shows a lengthy task is in progress, i.e., did not crash even if taking too long (and your SSH connection did not freeze if remote).
- An efficient **multithreaded** bar that updates itself at a fraction of the actual processing speed to keep **CPU usage low** and avoid terminal spamming (1,000,000 iterations per second equates to roughly 60 updates per second), and you can also calibrate it to your liking.
- An accurate **ETA** (Expected Time of Arrival) with an intelligent _Exponential Smoothing Algorithm_ that shows the time to completion, allowing you to plan your time and manage your workload more effectively.
- Automatic **print** and **logging** hooks that provide seamless and efortless integration with any code, even enriching these outputs with the current bar position when they occurred.
- It prints a nice **final receipt** when your processing ends, including the total items, the elapsed time, and the observed throughput.
- It detects **under** and **overflows**, enabling you to track hits, misses, or any desired counts, not necessarily the actually performed iterations.
- You can **suspend** it! That's right, you heard it here first! No other progress bar anywhere in any language has this feature! You can suspend your processing and get back to the Python prompt, adjust or fix some items, and then get back into the same processing as if it had never stopped!
- It is **highly customizable**, with a smorgasbord of ready-to-use spinner and bar styles, as well as several factories to easily create yours! There's even a super powerful `check()` tool that helps you design your own animations! You can see how the generated frames and animation cycles will look like, exploded on your screen, and even see it _alive_ before installing in `alive-progress`! It's the coolest tool in the world! Unleash your creativity!

---

## Table of contents

This README is always evolving, so do take a more comprehensive look from time to time... You might find great new details in other sections! 😊

<!-- TOC -->
* [alive-progress](#alive-progress)
  * [Table of contents](#table-of-contents)
  * [📌 What's new in 3.3 series](#-whats-new-in-33-series)
    * [Previous releases](#previous-releases)
  * [Using `alive-progress`](#using-alive-progress)
    * [Get it](#get-it)
    * [Try it](#try-it)
    * [Awake it](#awake-it)
    * [Master it](#master-it)
  * [The `bar` handler](#the-bar-handler)
  * [Auto-iterating](#auto-iterating)
  * [Modes of operation](#modes-of-operation)
    * [Auto and Unknown: Counter](#auto-and-unknown-counter)
    * [Manual: Percentage](#manual-percentage)
    * [Widgets available](#widgets-available)
    * [The different `bar()` handler semantics](#the-different-bar-handler-semantics)
  * [Styles](#styles)
  * [Configuration](#configuration)
  * [Create your own animations](#create-your-own-animations)
    * [Intro: How do they work?](#intro-how-do-they-work)
    * [A Spinner Compiler, really?](#a-spinner-compiler-really)
    * [Spinner Factories](#spinner-factories)
    * [Bar Factories](#bar-factories)
  * [Advanced](#advanced)
    * [The Pause Mechanism](#the-pause-mechanism)
    * [Loop-less use](#loop-less-use)
    * [FPS Calibration](#fps-calibration)
    * [Forcing animations on PyCharm, Jupyter, etc.](#forcing-animations-on-pycharm-jupyter-etc)
  * [Interesting facts](#interesting-facts)
  * [To do](#to-do)
  * [Python End of Life notice](#python-end-of-life-notice)
    * [For Python 2.7 and 3.5](#for-python-27-and-35)
    * [For Python 3.6](#for-python-36)
    * [For Python 3.7 and 3.8](#for-python-37-and-38)
  * [License](#license)
<!-- TOC -->

## 📌 What's new in 3.3 series

The latest `alive-progress` release is finally here, and it brings some exciting improvements! 🎉

Here's a summary of the most notable changes:

- Now the final receipt is available in the alive_bar handle, even after the bar has finished!
    - This allows you to access the final receipt data and use it in your code, such as logging or displaying it in a custom way.
- You can change the bar title and text even after it has finished, so you can update the final receipt with new information or context.
- Included the elapsed time in the alive_bar handle, with full precision, so you can access the exact time it took to complete your processing.

Technical changes:

- Changed the `grapheme` dependency which was no longer maintained to `graphemeu`, which is a maintained fork of the original one.
- Added `py.typed` in the distribution to satisfy mypy and other type checking tools.
- Included Python 3.14 support (in CI for now).

### Previous releases

<details>
<summary>New in 3.2 series</summary>

After about a year of reassuring stability, the new `alive-progress` has finally landed!

The main features and improvements are:

- The print/logging hooks now support multithreading => a highly requested feature: everything is synchronized now so you can print from different threads without issues! No more queues to send messages to the main thread!
- Rounded ETAs for long tasks => now the ETA automatically decreases its precision the higher it gets, making it smoother and easier to read!
- Support for zero and even negative bar increments => now on par with the manual mode, you can call `bar()` with `0` and even `-N` to make it go backwards! Useful when you couldn't make any progress in an iteration or had to roll back something!

And more!

- Custom offset for the enriched print/logging messages => now you can specify a custom `enrich_offset` to use for printed or logged messages, allowing you to start with `on 1:` or continue where you left from previous computations!
- Improved compatibility with PyInstaller => now custom bars, unknown bars, and spinners work when bundled!
- Improved compatibility with Celery => it will just work within Celery tasks!
- drop python 3.7 and 3.8, hello 3.12 and 3.13!

</details>

<details>
<summary>New in 3.1 series</summary>

A very cool update here! In addition to polishing things up and improving terminal support, now `alive-progress` supports resuming computations!

When processing huge datasets or things that take a long time, you might either use batches or cache partial results. Then, in case it stops and is restarted, you end up skipping all those already done items very quickly, which makes the `alive_bar` think you're processing thousands of items per second, which in turn completely ruins the ETA... But not anymore! Just tell `bar()` that you've skipped items... 👏

You can use it in two ways:

<strong>1.</strong> If you do know where you've stopped:

```python
with alive_bar(120000) as bar:
    bar(60000, skipped=True)
    for i in range(60000, 120000):
        # process item
        bar()
```

Yep, just call `bar(N, skipped=True)` once, with the number of items.

<strong>2.</strong> If you do not know or the items are scattered:

```python
with alive_bar(120000) as bar:
    for i in range(120000):
        if done(i):
            bar(skipped=True)
            continue

        # process item
        bar()
```

Yep, it's as simple as that! Just call `bar(skipped=True)` when an item is already done, or `bar()` as usual otherwise. You could also share a single `bar(skipped=?)` call at the end, with a bool saying whether you did skip that item or not. Cool, huh?

Also in this version:

- new `max_cols` config setting, the number of columns to use if not possible to fetch it, like in jupyter and other platforms which doesn't support size
- fix fetching the size of the terminal when using stderr
- officially supports Python 3.11
- included [ruff](https://github.com/charliermarsh/ruff) linter before building

</details>

<details>
<summary>New in 3.0 series</summary>

Yep, I could finally get this version out! These are the new goodies:

- Units support! You can now label the data you're processing, like `B`, `bytes`, or even `°C`!
- Automatic scaling! With support for SI (base 1000), IEC (base 1024), and even an alternate SI with base 1024, you'll be well served!
- Configurable precision! When your numbers are scaled, you get to choose how many decimals they display!
- Automatic stats scaling for slow throughputs! If your processing takes minutes or more, now you'll see rates per minute, per hour, and even per day! (It works within the auto-scaling system!)
- Support for using `sys.stderr` and other files instead of `sys.stdout`!
- Smoothed out the rate estimation with the same Exponential Smoothing Algorithm that powers the ETA, so the bar returns a more realistic ETA!
- Query the currently running widgets' data, like the monitor, rate, and ETA!
- New help system on configuration errors, which explains why a value was not accepted, and what were the expected ones!

Highly anticipated fixes:

- Support for reusing logging handlers! No more `TypeError: unhashable type: 'types.SimpleNamespace'`.
- Support for logging when using `RotatingFileHandler`s! Yep, seek support is here.
- Fix unknown mode always ending with a warning (!)

And last but not least, a more polished layout for you to enjoy your progress!
![alive-progress 3.0](https://raw.githubusercontent.com/rsalmei/alive-progress/main/img/alive3.0.png)
</details>

<details>
<summary>New in 2.4 series</summary>

Now, `alive_bar` supports *Dual Line* text mode!

If you ever wanted to include longer situational messages within the bar, you probably felt squeezed into one line. You had to shrink the beautifully animated bar or, even worse, remove widgets (!) to be able to see what you needed...
<br>Not anymore!! You can now make the bar *Dual Line*, and put text below it!

Yes, there's a message below the whole bar, and any other print/logging messages scroll above it!

```python
letters = [chr(ord('A') + x) for x in range(26)]
with alive_bar(26, dual_line=True, title='Alphabet') as bar:
    for c in letters:
        bar.text = f'-> Teaching the letter: {c}, please wait...'
        if c in 'HKWZ':
            print(f'fail "{c}", retry later')
        time.sleep(0.3)
        bar()
```

Output:

```
on 7: fail "H", retry later
on 10: fail "K", retry later
Alphabet |███████████████████████████▊            | ▃▅▇ 18/26 [69%] in 6s (3.2/s, eta: 3s)
-> Teaching the letter: S, please wait...
```

There's also a new `finalize` function parameter in `alive_it` which enables you to set the title and/or text of the final receipt, and improved logging support which detects customized loggers.
</details>

<details>
<summary>New in 2.3 series</summary>

This is all about customization; the core widgets can now be changed:

- send a string to the `monitor`, `elapsed`, and `stats` widgets to make them look anyway you want!

> It's incredible that these strings support all Python format features, so you can e.g., `{percent:.1%}` 😉.

They can be further customized when on the **final receipt**!

- new `monitor_end`, `elapsed_end`, and `stats_end`, with dynamic formats inherited from the standard ones!

> If you've hidden some widgets before, just so they wouldn't appear on the receipt, now you can see them in all their running glory, and hide just the receipt ones! Or the other way around 😜

Another addition, now `alive-progress` beautifully renders its cool final receipt whenever it is stopped, even if you CTRL+C it prematurely! I don't know why I haven't thought about that before...

```
Download |██████████████████⚠︎                     | (!) 45/100 [45%] in 4.8s (9.43/s)
```

And finally, you can choose to disable CTRL+C at all! The default is the safer `ctrl_c=True`, which does make CTRL-C work as usual.
<br>Disable it `ctrl_c=False`, to make your interactive `alive_bar` much smoother to use (there are no stack traces if you stop it), and/or if it is at the top-level of your program!

> Beware: If it is e.g. inside a for-loop, it will just continue to the next iteration, which may or may not be what you want...

```python
for i in range(10):
    with alive_bar(100, ctrl_c=False, title=f'Download {i}') as bar:
        for i in range(100):
            time.sleep(0.02)
            bar()
```

Output:

```
Download 0 |████████▊⚠︎                              | (!) 22/100 [22%] in 0.6s (36.40/s)
Download 1 |████████████████▊⚠︎                      | (!) 42/100 [42%] in 1.0s (41.43/s)
Download 2 |██████▍⚠︎                                | (!) 16/100 [16%] in 0.4s (39.29/s)
Download 3 |█████▋⚠︎                                 | (!) 14/100 [14%] in 0.4s (33.68/s)
Download 4 |█████████████▎⚠︎                         | (!) 33/100 [33%] in 0.8s (39.48/s)
Download 5 |███████▎⚠︎                               | (!) 18/100 [18%] in 0.5s (37.69/s)
Download 6 |█████▎⚠︎                                 | (!) 13/100 [13%] in 0.3s (37.28/s)
Download 7 |████████████⚠︎                           | (!) 30/100 [30%] in 0.8s (38.43/s)
Download 8 |██████⚠︎                                 | (!) 15/100 [15%] in 0.4s (36.26/s)
...
```

</details>

<details>
<summary>New in 2.2 series</summary>

Some major new features, often requested, have finally landed!

- bar title can be dynamically set, changed, or even removed after being displayed
- new custom fps system, which enables very slow refresh rates (to let it run on those k8s for long periods)
- the final receipt can be totally hidden (great for special effects, like using the cool spinners standalone)
- new support for `click.echo()` printing
- terminal columns detection is safer for exotic environments
- requires Python 3.7+

</details>

<details>
<summary>New in 2.1 series</summary>

YES! Now `alive-progress` has support for Jupyter Notebooks and also includes a _Disabled_ state! Both were highly sought after, and have finally landed!
<br>And better, I've implemented an auto-detection mechanism for jupyter notebooks, so it just works, out of the box, without any changes in your code!!

See for yourself:

![alive-progress demo](https://raw.githubusercontent.com/rsalmei/alive-progress/main/img/alive-jupyter.gif)

> It seems to work very well, but at this moment, it should be considered **experimental**.
> <br>There were instances in which some visual glitches have appeared, like two `alive_bar` refreshes being concatenated together instead of over one another... And it's something I think I can't possibly work around: it seems Jupyter sometimes refresh the canvas at odd times, which makes it lose some data. Please let me know on the issues if something funnier arises.
</details>

<details>
<summary>New in 2.0 series</summary>

This is a major breakthrough in `alive-progress`!
<br>I took 1 year developing it, and I'm very proud of what I've accomplished \o/

- now, there's complete support for Emojis 🤩 and exotic Unicode chars in general, which required MAJOR refactoring deep within the project, giving rise to what I called "**Cell Architecture**" => now, all internal components use and generate streams of cells instead of characters, and correctly interpret grapheme clusters — those so-called wide chars, which are encoded with a variable number of chars, but always take two cells on screen!! This has enabled us to render complex multi-chars symbols as if they were one, thus making them work on any spinners, bars, texts, borders and backgrounds, even when fractured!!! Pretty advanced stuff 🤓
- new super cool spinner compiler and runner, which generates complete animations ahead of time, and plays these ready-to-go animations seamlessly, with no overhead at all! 🚀
- the spinner compiler also includes advanced extra commands to generate and modify animations, like reshape, replace, transpose, or randomize the animation cycles!
- new powerful and polished `.check()` tools that compile and beautifully render all frames from all animation cycles of spinners and bars! they can even include complete frame data, internal codepoints, and even their animations! 👏
- bars engine revamp, with invisible fills, advanced support for multi-char tips (which gradually enter and leave the bar), borders, tips and errors of any length, and underflow errors that can leap into the border if they can't fit!
- spinners engine revamp, with standardized factory signatures, improved performance, new types, and new features: smoother bouncing spinners (with an additional frame at the edges), optimized scrolling of text messages (which go slower and pause for a moment at the edges), new alongside and sequential spinners, nicer effect in alongside spinners (which use weighted spreading over the available space), smoother animation in scrolling spinners (when the input is longer than the available space)
- new builtin spinners, bars, and themes, which make use of the new animation features
- new showtime that displays themes and is dynamic => it does not scroll the screen when it can't fit vertically or horizontally, and can even filter for patterns!
- improved support for logging into files, which gets enriched as the print hook is!
- several new configuration options for customizing appearance, including support for disabling any `alive-progress` widgets!
- includes a new iterator adapter, `alive_it`, that accepts an iterable and calls `bar()` for you!
- requires Python 3.6+ (and officially supports Python 3.9 and 3.10)

> Since this is a major version change, direct backward compatibility is not guaranteed. If something does not work at first, just check the new imports and functions' signatures, and you should be good to go. All previous features should still work here! 👍
</details>

---

## Using `alive-progress`

### Get it

Just install with pip:

```sh
❯ pip install alive-progress
```

### Try it

If you're wondering what styles are builtin, it's `showtime`! ;)

```python
from alive_progress.styles import showtime

showtime()
```

> Note: Please disregard the path in the animated gif below, the correct one is above. These long gifs are very time-consuming to generate, so I can't make another on every single change. Thanks for your understanding.

![alive-progress spinners](https://raw.githubusercontent.com/rsalmei/alive-progress/main/img/showtime-spinners.gif)

I've made these styles just to try all the animation factories I've created, but I think some of them ended up very, very cool! Use them at will, and mix them to your heart's content!

Do you want to see actual `alive-progress` bars gloriously running in your system before trying them yourself?

```sh
❯ python -m alive_progress.tools.demo
```

![alive-progress demo-tool](https://raw.githubusercontent.com/rsalmei/alive-progress/main/img/alive-demo-tool.png)

### Awake it

Cool, huh?? Now enter an `ipython` REPL and try this:

```python
from alive_progress import alive_bar
import time

for x in 1000, 1500, 700, 0:
    with alive_bar(x) as bar:
        for i in range(1000):
            time.sleep(.005)
            bar()
```

You'll see something like this, with cool animations throughout the process 😜:

```
|████████████████████████████████████████| 1000/1000 [100%] in 5.8s (171.62/s)
|██████████████████████████▋⚠︎            | (!) 1000/1500 [67%] in 5.8s (172.62/s)
|████████████████████████████████████████✗︎ (!) 1000/700 [143%] in 5.8s (172.06/s)
|████████████████████████████████████████| 1000 in 5.8s (172.45/s)
```

Nice, huh? Loved it? I knew you would, thank you 😊.

To actually use it, just wrap your normal loop in an `alive_bar` context manager like this:

```python
with alive_bar(total) as bar:  # declare your expected total
    for item in items:  # <<-- your original loop
        print(item)  # process each item
        bar()  # call `bar()` at the end
```

And it's alive! 👏

So, in a nutshell: retrieve the items as always, enter the `alive_bar` context manager with the number of items, and then iterate/process those items, calling `bar()` at the end! It's that simple! :)

### Master it

- The `items` argument can be any iterable, like for example, a queryset.
- The first argument of the `alive_bar` is the expected total, like `qs.count()` for querysets, `len(items)` for bounded iterables, or even some static number.
- Calling `bar()` is what makes the bar go forward — you usually call it once in every iteration, just after finishing an item.
- If you call `bar()` too much or too few, the bar will graphically render that deviation from the expected `total`, making it very easy to notice under and overflows.
- The usual Python `print()` is automatically hooked, so you can effortlessly display messages tightly integrated with the current progress bar on display! It won't break the fast bar refreshes in any way and will even enrich your message! The bar nicely cleans up the line, prints the current bar position alongside your message, and continues refreshing right below it!
- The standard Python `logging` framework is also automatically hooked and enriched, exactly like the `print()` above!
- If you're using the `click` CLI lib, you can even use `click.echo()` to print styled text.

![alive-progress printing messages](https://raw.githubusercontent.com/rsalmei/alive-progress/main/img/print-hook.gif)

> You can get creative! Since the bar goes forward only when you call `bar()`, it is **independent of the loop** it is in! So you can use it to monitor unrelated things like pending transactions, broken items, etc., or even call it more than once in the same iteration! That way, you'll get to know how many of these "special" events there were, including their percentage relative to the total!

Awesome right? And everything work the same both in real terminals and in Jupyter notebooks!

## The `bar` handler

After a bar has finished (or even while running), you have a plethora of methods available to grab information about the bar, and to control it. Here are they:

- `bar.text('message')` or `bar.text = 'message'`: set a situational message at the end of the bar, where you can display information about the current item or the phase the processing is in.
- `bar.title('Title')` or `bar.title = 'Title'`: set a title at the beginning of the bar; can be set right when starting it or while it's running or even after finished to affect the receipt on demand — mix it with `title_length` config to keep the bar from changing its length while running.
- `bar.current`: retrieve the current bar count or percentage — more details below on Modes of Operation.
- `bar.monitor` returns the current monitor widget text, which is the current bar position formatted according to the current configuration.
- `bar.eta`: returns the current ETA widget text, formatted according to the current configuration.
- `bar.rate`: returns the current throughput widget text, formatted according to the current configuration.
- `bar.elapsed`: returns the current elapsed time in seconds, with full precision.
- `bar.receipt`: returns an on-demand receipt, which can be used however you want in your code, such as logging or displaying it in a custom way.
- `bar.pause()`: pauses the bar without losing its state — more details below on the Pause Mechanism.

## Auto-iterating

You now have a quicker way to monitor anything! Here, the items are automatically tracked for you!
<br>Behold the `alive_it` => the `alive_bar` iterator adapter!

Simply wrap your items with it, and loop over them as usual!
<br>The bar will just work; it's that simple!

```python
from alive_progress import alive_it

for item in alive_it(items):  # <<-- wrapped items
    print(item)  # process each item
```

HOW COOL IS THAT?! 😜

All `alive_bar` parameters apply but `total`, which is smarter (if not supplied, it will be auto-inferred from your data using `len` or `length_hint`), and `manual` that does not make sense here.

Note there isn't any `bar` handle at all in there. But what if you do want it, e.g. to set text messages or retrieve the current progress?
<br>You can interact with the internal `alive_bar` by just assigning `alive_it` to a variable like this:

```python
bar = alive_it(items)  # <<-- bar with wrapped items
for item in bar:  # <<-- iterate on bar
    print(item)  # process each item
    bar.text(f'ok: {item}')  # WOW, it works!
```

Note that this is a slightly special `bar`, which does not support `bar()`, since the iterator adapter tracks items automatically for you. Also, it supports `finalize`, which enables you to set the title and/or text of the final receipt:

```python
alive_it(items, finalize=lambda bar: bar.text('Success!'))
...
```

> In a nutshell:
> - full use is always `with alive_bar() as bar`, where you iterate and call `bar()` whenever you want;
> - quick adapter use is `for item in alive_it(items)`, where items are automatically tracked;
> - full adapter use is `bar = alive_it(items)`, where in addition to items being automatically tracked, you get a special iterable `bar` able to customize the inner `alive_progress` however you want.

## Modes of operation

### Auto and Unknown: Counter

The default modes are **auto** and **unknown**, which use internally a **counter** to track the progress. They count the number of items processed, and use it to update the progress bar accordingly.

The `total` argument is optional. If you do provide it, the bar enters in **auto mode**. In this mode, the progress of the operation is automatically tracked, and all the widgets `alive-progress` has to offer are available: precise bar, spinner, percentage, counter, throughput, and ETA.

If you don't provide `total`, the bar enters in **unknown mode**. In this mode, the progress is indeterminable, and therefore the ETA, so the whole progress bar is continuously animated. The widgets available are: animated bar, spinner, counter, and throughput.
> The cool spinner runs completely independently of the animated bar, both running their own animations concurrently, rendering a unique show in your terminal! 😜

Last but not least, the **auto** mode has a unique ability: mark items as skipped, making the throughput and ETA much more accurate! More on that later.

### Manual: Percentage

The **manual mode**, manually activated by the `manual=True` argument, uses internally a **percentage** to track the progress. It enables you to get complete control of the bar position. It's usually used to monitor processes that only feed you a percentage of completion, or to generate some random special effects.

You can use it directly with `alive_bar` or via `config_handler`, and it allows you to send percentages to the `bar()` handler! For example, to set it to 15% completion, just call `bar(0.15)` — which is 15 / 100.

You can also provide `total` here. If you do, `alive-progress` will automatically infer an internal counter, and will thus be able to offer you all the same widgets available in auto mode!

If you don't provide `total`, you'll at least get rough versions of the throughput and ETA widgets, computed as "%/s" (percentage per second) and until 100%, respectively. Neither of them are very accurate, but they are better than nothing.

### Widgets available

When `total` is provided all is cool:

|  mode  |    counter    |  percentage  | throughput | ETA | over/underflow |
|:------:|:-------------:|:------------:|:----------:|:---:|:--------------:|
|  auto  | ✅ (user tick) | ✅ (inferred) |     ✅      |  ✅  |       ✅        |
| manual | ✅ (inferred)  | ✅ (user set) |     ✅      |  ✅  |       ✅        |

When it isn't, some compromises have to be made:

|  mode   |    counter    |  percentage  |  throughput  |    ETA     | over/underflow |
|:-------:|:-------------:|:------------:|:------------:|:----------:|:--------------:|
| unknown | ✅ (user tick) |      ❌       |      ✅       |     ❌      |       ❌        |
| manual  |       ❌       | ✅ (user set) | ⚠️ (simpler) | ⚠️ (rough) |       ✅        |

But it's actually simple to understand: you do not need to think about which mode you should use!
- Just always send the `total` if you have it, and use `manual` if you need it!

That's it! It will just work the best it can! 👏 \o/

### The different `bar()` handler semantics

The `bar()` handlers support either relative or absolute semantics, depending on the mode:

- _auto_ and _unknown_ modes use **optional relative** positioning, so you can just call `bar()` to increment the counter by one, or send any other increment like `bar(200)` to increment by 200 at once;
  > they even support `bar(0)` and `bar(-5)` to hold or decrement if needed!
- _manual_ mode uses **mandatory absolute** positioning, so you can call `bar(0.35)` to make the bar instantly jump to 35% progress.

> Both modes enable you to get creative! Since you can just make the bar go instantly to whatever position you want, you can:
> - make it go backwards — e.g. to graphically display the timeout of something;
> - create special effects — e.g. to mimic a real-time analog gauge of some sort.

You can call `bar()` as many times as you want! The terminal refresh rate will always be asynchronously computed according to the current throughput and progress, so you won't risk spamming the terminal with more updates than needed.

In any case, to retrieve the current counter/percentage, just call: `bar.current`:

- in _auto_ and _unknown_ modes, this provides an **integer** — the actual internal counter;
- in _manual_ mode, this provides a **float** in the interval [0, 1] — the last percentage set.

Finally, the `bar()` handler in **auto** mode leverages a unique ability: skip items! Just call `bar(skipped=True)` or `bar(10, skipped=True)` to exclude those items from the throughput calculations, and thus preventing them from inaccurately affecting the ETA.

---
Maintaining an open source project is hard and time-consuming, and I've put much ❤️ and effort into this.

If you've appreciated my work, you can back me up with a donation! Thank you 😊

[<img align="right" src="https://cdn.buymeacoffee.com/buttons/default-orange.png" width="217px" height="51x">](https://www.buymeacoffee.com/rsalmei)
[<img align="right" alt="Donate with PayPal button" src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif">](https://www.paypal.com/donate?business=6SWSHEB5ZNS5N&no_recurring=0&item_name=I%27m+the+author+of+alive-progress%2C+clearly+and+about-time.+Thank+you+for+appreciating+my+work%21&currency_code=USD)

---

## Styles

The `showtime` exhibit has an optional argument to choose which show to present, `Show.SPINNERS` (default), `Show.BARS` or `Show.THEMES`, do take a look at them! ;)

```python
from alive_progress.styles import showtime, Show

showtime(Show.BARS)
showtime(Show.THEMES)
```

> Note: Please disregard the path in the animated gif below, the correct one is above. These long gifs are very time-consuming to generate, so I can't make another on every single change. Thanks for your understanding.

![alive-progress bars](https://raw.githubusercontent.com/rsalmei/alive-progress/main/img/showtime-bars.gif)

And the themes one (📌 new in 2.0):

![alive-progress themes](https://raw.githubusercontent.com/rsalmei/alive-progress/main/img/showtime-themes.gif)

The `showtime` exhibit also accepts some customization options:

- **fps**: the frames per second rate refresh rate, default is 15;
- **length**: the length of the bars, default is 40;
- **pattern**: a filter to choose which ones to display.

For example to get a marine show, you can `showtime(pattern='boat|fish|crab')`:

![alive-progress filtered spinners](https://raw.githubusercontent.com/rsalmei/alive-progress/main/img/showtime-marine-spinners.gif)

> You can also access these shows with the shorthands `show_bars()`, `show_spinners()`, and `show_themes()`!

> There's also a small utility called `print_chars()`, to help find that cool character to put in your customized spinners and bars, or to determine if your terminal does support Unicode characters.

## Configuration

There are several options to customize both appearance and behavior!
<br>All of them can be set both directly in the `alive_bar` or globally in the `config_handler`!

These are the options - default values in brackets:

- `title`: an optional, always visible bar title
- `length`: [`40`] the number of cols to render the animated progress bar
- `max_cols`: [`80`] the maximum cols to use if not possible to fetch it, like in jupyter
- `spinner`: the spinner style to be rendered next to the bar
  <br> ↳ accepts a predefined spinner name, a custom spinner factory, or None
- `bar`: the bar style to be rendered in known modes
  <br> ↳ accepts a predefined bar name, a custom bar factory, or None
- `unknown`: the bar style to be rendered in the unknown mode
  <br> ↳ accepts a predefined spinner name, or a custom spinner factory (cannot be None)
- `theme`: [`'smooth'`] a set of matching spinner, bar, and unknown
  <br> ↳ accepts a predefined theme name
- `force_tty`: [`None`] forces animations to be on, off, or according to the tty (more details [here](#forcing-animations-on-pycharm-jupyter-etc))
  <br> ↳ None -> auto select, according to the terminal/Jupyter
  <br> ↳ True -> unconditionally enables animations, but still auto-detects Jupyter Notebooks
  <br> ↳ False -> unconditionally disables animations, keeping only the final receipt
- `file`: [`sys.stdout`] the file object to use: `sys.stdout`, `sys.stderr`, or a similar `TextIOWrapper`
- `disable`: [`False`] if True, completely disables all output, do not install hooks
- `manual`: [`False`] set to manually control the bar position
- `enrich_print`: [`True`] enriches print() and logging messages with the bar position
- `enrich_offset`: [`0`] the offset to apply to enrich_print
- `receipt`: [`True`] prints the nice final receipt, disables if False
- `receipt_text`: [`False`] set to repeat the last text message in the final receipt
- `monitor` (bool|str): [`True`] configures the monitor widget `152/200 [76%]`
  <br> ↳ send a string with `{count}`, `{total}` and `{percent}` to customize it
- `elapsed` (bool|str): [`True`] configures the elapsed time widget `in 12s`
  <br> ↳ send a string with `{elapsed}` to customize it
- `stats` (bool|str): [`True`] configures the stats widget `(123.4/s, eta: 12s)`
  <br> ↳ send a string with `{rate}` and `{eta}` to customize it
- `monitor_end` (bool|str): [`True`] configures the monitor widget within final receipt
  <br> ↳ same as monitor, the default format is dynamic, it inherits `monitor`'s one
- `elapsed_end` (bool|str): [`True`] configures the elapsed time widget within final receipt
  <br> ↳ same as elapsed, the default format is dynamic, it inherits `elapsed`'s one
- `stats_end` (bool|str): [`True`] configures the stats widget within final receipt
  <br> ↳ send a string with `{rate}` to customize it (no relation to stats)
- `title_length`: [`0`] fixes the length of titles, or 0 for unlimited
  <br> ↳ title will be truncated if longer, and a cool ellipsis "…" will appear at the end
- `spinner_length`: [`0`] forces the spinner length, or `0` for its natural one
- `refresh_secs`: [`0`] forces the refresh period to this, `0` is the reactive visual feedback
- `ctrl_c`: [`True`] if False, disables CTRL+C (captures it)
- `dual_line`: [`False`] if True, places the text below the bar
- `unit`: any text that labels your entities
- `scale`: the scaling to apply to units: `None`, `SI`, `IEC`, or `SI2`
  <br> ↳ supports aliases: `False` or `''` -> `None`, `True` -> `SI`, `10` or `'10'` -> `SI`, `2` or `'2'` -> `IEC`
- `precision`: [`1`] how many decimals do display when scaling

And there's also one that can only be set locally in the `alive_bar` context:

- `calibrate`: maximum theoretical throughput to calibrate the animation speed (more details [here](#fps-calibration))

To set them locally, just send them as keyword arguments to `alive_bar`:

```python
with alive_bar(total, title='Processing', length=20, bar='halloween') as bar:
    ...
```

To use them globally, send them to `config_handler`, and any `alive_bar` created after that will include those options! And you can mix and match them, local options always have precedence over global ones:

```python
from alive_progress import config_handler

config_handler.set_global(length=20, spinner='wait')

with alive_bar(total, bar='blocks', spinner='twirls') as bar:
    # the length is 20, the bar is 'blocks' and the spinner is 'twirls'.
    ...
```

## Create your own animations

Yes, you can assemble your own spinners! And it's easy!
<br>I've created a plethora of special effects, so you can just mix and match them any way you want! There are frames, scrolling, bouncing, sequential, alongside, and delayed spinners! Get creative! 😍

### Intro: How do they work?

The spinners' animations are engineered by very advanced generator expressions, deep within several layers of meta factories, factories and generators 🤯!

- the meta factory (public interface) receives the styling parameters from you, the user, and processes/stores them inside a closure to create the actual factory => this