Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

📝 Add docs for using Rich with Typer #421

Merged
merged 4 commits into from
Jul 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ repos:
- --py3-plus
- --keep-runtime-typing
# This file is more readable without yield from
exclude: ^docs_src/progressbar/tutorial002\.py
exclude: ^docs_src/progressbar/tutorial004\.py
- repo: https://github.com/myint/autoflake
rev: v1.4
hooks:
Expand Down
109 changes: 99 additions & 10 deletions docs/tutorial/progressbar.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,91 @@
If you are executing an operation that can take some time, you can inform it to the user with a progress bar.
If you are executing an operation that can take some time, you can inform it to the user. 🤓

For this, you can use `typer.progressbar()`:
## Progress Bar

```Python hl_lines="8"
You can use <a href="https://rich.readthedocs.io/en/stable/progress.html" class="external-link" target="_blank">Rich's Progress Display</a> to show a progress bar, for example:

```Python hl_lines="4 9"
{!../docs_src/progressbar/tutorial001.py!}
```

You use `typer.progressbar()` with a `with` statement, as in:
You put the thing that you want to iterate over inside of Rich's `track()`, and then iterate over that.

Check it:

<div class="termy">

```console
$ python main.py

---> 100%

Processed 100 things.
```

</div>

...actually, it will look a lot prettier. ✨ But I can't show you the animation here in the docs. 😅

The colors and information will look something like this:

<div class="termy">

```console
$ python main.py

Processing... <font color="#F92672">━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╸</font><font color="#3A3A3A">━━━━━━━━━━</font> <font color="#AE81FF"> 74%</font> <font color="#A1EFE4">0:00:01</font>
```

</div>

## Spinner

When you don't know how long the operation will take, you can use a spinner instead.

Rich allows you to display many things in complex and advanced ways.

For example, this will show two spinners:

```Python hl_lines="4 8-15"
{!../docs_src/progressbar/tutorial002.py!}
```

I can't show you the beautiful animation here in the docs. 😅

But at some point in time it will look like this (imagine it's spinning). 🤓

<div class="termy">

```console
$ python main.py

<font color="#A6E22E">⠹</font> Processing...
<font color="#A6E22E">⠹</font> Preparing...
```

</div>

You can learn more about it in the <a href="https://rich.readthedocs.io/en/stable/progress.html" class="external-link" target="_blank">Rich docs for Progress Display</a>.

## Typer `progressbar`

If you can, you should use **Rich** as explained above, it has more features, it's more advanced, and can display information more beautifully. ✨

!!! tip
If you can use Rich, use the information above, the Rich docs, and skip the rest of this page. 😎

But if you can't use Rich, Typer (actually Click) comes with a simple utility to show progress bars.

!!! info
`typer.progressbar()` comes directly from Click, you can read more about it in <a href="https://click.palletsprojects.com/en/8.1.x/utils/#showing-progress-bars" class="external-link" target="_blank">Click's docs</a>.


### Use `typer.progressbar`

!!! tip
Remember, you are much better off using <a href="https://rich.readthedocs.io/" class="external-link" target="_blank">Rich</a> for this. 😎

You can use `typer.progressbar()` with a `with` statement, as in:

```Python
with typer.progressbar(something) as progress:
Expand All @@ -15,6 +94,10 @@ with typer.progressbar(something) as progress:

And you pass as function argument to `typer.progressbar()` the thing that you would normally iterate over.

```Python hl_lines="8"
{!../docs_src/progressbar/tutorial003.py!}
```

So, if you have a list of users, this could be:

```Python
Expand Down Expand Up @@ -58,14 +141,17 @@ Processed 100 things.

</div>

## Setting a Progress Bar `length`
### Setting a Progress Bar `length`

!!! tip
Remember, you are much better off using <a href="https://rich.readthedocs.io/" class="external-link" target="_blank">Rich</a> for this. 😎

The progress bar is generated from the length of the iterable (e.g. the list of users).

But if the length is not available (for example, with something that fetches a new user from a web API each time) you can pass an explicit `length` to `typer.progressbar()`.

```Python hl_lines="14"
{!../docs_src/progressbar/tutorial002.py!}
{!../docs_src/progressbar/tutorial004.py!}
```

Check it:
Expand All @@ -82,7 +168,7 @@ Processed 100 user IDs.

</div>

### About the function with `yield`
#### About the function with `yield`

If you hadn't seen something like that `yield` above, that's a "<a href="https://docs.python.org/3/glossary.html#term-generator" class="external-link" target="_blank">generator</a>".

Expand All @@ -104,12 +190,15 @@ for i in iterate_user_ids():

would print each of the "user IDs" (here it's just the numbers from `0` to `99`).

## Add a `label`
### Add a `label`

!!! tip
Remember, you are much better off using <a href="https://rich.readthedocs.io/" class="external-link" target="_blank">Rich</a> for this. 😎

You can also set a `label`:

```Python hl_lines="8"
{!../docs_src/progressbar/tutorial003.py!}
{!../docs_src/progressbar/tutorial005.py!}
```

Check it:
Expand All @@ -127,7 +216,7 @@ If you need to manually iterate over something and update the progress bar irreg
And then calling the `.update()` method in the object from the `with` statement:

```Python hl_lines="8 12"
{!../docs_src/progressbar/tutorial004.py!}
{!../docs_src/progressbar/tutorial006.py!}
```

Check it:
Expand Down
22 changes: 22 additions & 0 deletions docs/tutorial/prompt.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,25 @@ Aborted!
```

</div>

## Prompt with Rich

If you installed Rich as described in [Printing and Colors](printing.md){.internal-link target=_blank}, you can use Rich to prompt the user for input:

```Python hl_lines="2 6"
{!../docs_src/prompt/tutorial004.py!}
```

And when you run it, it will look like:

<div class="termy">

```console
$ python main.py

# Enter your name 😎:$ Morty

Hello Morty
```

</div>
10 changes: 5 additions & 5 deletions docs_src/progressbar/tutorial001.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import time

import typer
from rich.progress import track


def main():
total = 0
with typer.progressbar(range(100)) as progress:
for value in progress:
# Fake processing time
time.sleep(0.01)
total += 1
for value in track(range(100), description="Processing..."):
# Fake processing time
time.sleep(0.01)
total += 1
print(f"Processed {total} things.")


Expand Down
23 changes: 10 additions & 13 deletions docs_src/progressbar/tutorial002.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
import time

import typer


def iterate_user_ids():
# Let's imagine this is a web API, not a range()
for i in range(100):
yield i
from rich.progress import Progress, SpinnerColumn, TextColumn


def main():
total = 0
with typer.progressbar(iterate_user_ids(), length=100) as progress:
for value in progress:
# Fake processing time
time.sleep(0.01)
total += 1
print(f"Processed {total} user IDs.")
with Progress(
SpinnerColumn(),
TextColumn("[progress.description]{task.description}"),
transient=True,
) as progress:
progress.add_task(description="Processing...", total=None)
progress.add_task(description="Preparing...", total=None)
time.sleep(5)
print("Done!")


if __name__ == "__main__":
Expand Down
2 changes: 1 addition & 1 deletion docs_src/progressbar/tutorial003.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

def main():
total = 0
with typer.progressbar(range(100), label="Processing") as progress:
with typer.progressbar(range(100)) as progress:
for value in progress:
# Fake processing time
time.sleep(0.01)
Expand Down
18 changes: 12 additions & 6 deletions docs_src/progressbar/tutorial004.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,20 @@
import typer


def iterate_user_ids():
# Let's imagine this is a web API, not a range()
for i in range(100):
yield i


def main():
total = 1000
with typer.progressbar(length=total) as progress:
for batch in range(4):
total = 0
with typer.progressbar(iterate_user_ids(), length=100) as progress:
for value in progress:
# Fake processing time
time.sleep(1)
progress.update(250)
print(f"Processed {total} things in batches.")
time.sleep(0.01)
total += 1
print(f"Processed {total} user IDs.")


if __name__ == "__main__":
Expand Down
17 changes: 17 additions & 0 deletions docs_src/progressbar/tutorial005.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import time

import typer


def main():
total = 0
with typer.progressbar(range(100), label="Processing") as progress:
for value in progress:
# Fake processing time
time.sleep(0.01)
total += 1
print(f"Processed {total} things.")


if __name__ == "__main__":
typer.run(main)
17 changes: 17 additions & 0 deletions docs_src/progressbar/tutorial006.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import time

import typer


def main():
total = 1000
with typer.progressbar(length=total) as progress:
for batch in range(4):
# Fake processing time
time.sleep(1)
progress.update(250)
print(f"Processed {total} things in batches.")


if __name__ == "__main__":
typer.run(main)
11 changes: 11 additions & 0 deletions docs_src/prompt/tutorial004.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import typer
from rich.prompt import Prompt


def main():
name = Prompt.ask("Enter your name :sunglasses:")
print(f"Hey there {name}!")


if __name__ == "__main__":
typer.run(main)