Textualize / rich

Rich is a Python library for rich text and beautiful formatting in the terminal.
https://rich.readthedocs.io/en/latest/
MIT License
49.48k stars 1.72k forks source link

[BUG] Leftover newlines with progress after using remove_task. #3397

Open Baa14453 opened 4 months ago

Baa14453 commented 4 months ago

Describe the bug

Hi, I'm using asyncio where two independent functions might add a progress bar at any time and they will progress independently. Because of this, I can't use Progress in a context manager such that it destroys the object when the progress is done, instead I am initialising the object and passing it through to both functions at their initialisation.

I didn't want any leftover progress bars to remain, so I delete them on completion by calling remove_task. I then ran into this problem, while the task is removed, either the space it occupied remains, or add_task automatically attempts to compensate for another progress bar that it thinks exists above it, and adds a newline. The result is the same, as time goes on the progress bars descend further and further down, I've recorded a gif to demonstrate: Progress

I've also put together the minimal code to recreate this:

from rich.progress import Progress
import asyncio

async def meow(progress):
    with progress as progress_bar:    
        task = progress_bar.add_task("Meowing...", total = 10)
        meows = 0
        meow_total = 3
        meow_increase = 1
        while meows < meow_total:
            progress_bar.update(task, advance=meow_total / meow_increase)
            meows += meow_increase
            await asyncio.sleep(1)
        progress_bar.remove_task(task)

while True:
    progress = Progress(transient=True)
    asyncio.run(meow(progress))

Platform

Click to expand What platform (Win/Linux/Mac) are you running on? What terminal software are you using? Ubuntu 22.04, Gnome Terminal and the VSCodium / VSCode terminal. If you're using Rich in a terminal: ``` ╭───────────────────────── ─────────────────────────╮ │ A high level console interface. │ │ │ │ ╭──────────────────────────────────────────────────────────────────────────────╮ │ │ │ │ │ │ ╰──────────────────────────────────────────────────────────────────────────────╯ │ │ │ │ color_system = 'truecolor' │ │ encoding = 'utf-8' │ │ file = <_io.TextIOWrapper name='' mode='w' encoding='utf-8'> │ │ height = 25 │ │ is_alt_screen = False │ │ is_dumb_terminal = False │ │ is_interactive = True │ │ is_jupyter = False │ │ is_terminal = True │ │ legacy_windows = False │ │ no_color = False │ │ options = ConsoleOptions( │ │ size=ConsoleDimensions(width=115, height=25), │ │ legacy_windows=False, │ │ min_width=1, │ │ max_width=115, │ │ is_terminal=True, │ │ encoding='utf-8', │ │ max_height=25, │ │ justify=None, │ │ overflow=None, │ │ no_wrap=False, │ │ highlight=None, │ │ markup=None, │ │ height=None │ │ ) │ │ quiet = False │ │ record = False │ │ safe_box = True │ │ size = ConsoleDimensions(width=115, height=25) │ │ soft_wrap = False │ │ stderr = False │ │ style = None │ │ tab_size = 8 │ │ width = 115 │ ╰──────────────────────────────────────────────────────────────────────────────────╯ ╭─── ────╮ │ Windows features available. │ │ │ │ ╭───────────────────────────────────────────────────╮ │ │ │ WindowsConsoleFeatures(vt=False, truecolor=False) │ │ │ ╰───────────────────────────────────────────────────╯ │ │ │ │ truecolor = False │ │ vt = False │ ╰───────────────────────────────────────────────────────╯ ╭────── Environment Variables ───────╮ │ { │ │ 'TERM': 'xterm-256color', │ │ 'COLORTERM': 'truecolor', │ │ 'CLICOLOR': None, │ │ 'NO_COLOR': None, │ │ 'TERM_PROGRAM': 'vscode', │ │ 'COLUMNS': None, │ │ 'LINES': None, │ │ 'JUPYTER_COLUMNS': None, │ │ 'JUPYTER_LINES': None, │ │ 'JPY_PARENT_PID': None, │ │ 'VSCODE_VERBOSE_LOGGING': None │ │ } │ ╰────────────────────────────────────╯ platform="Linux" rich==13.7.1 ```
github-actions[bot] commented 4 months ago

Thank you for your issue. Give us a little time to review it.

PS. You might want to check the FAQ if you haven't done so already.

This is an automated reply, generated by FAQtory

aidaco commented 4 months ago

A simple way to avoid this would be to move the with progress as progress_bar contextmanager to outside of the main while True loop. This avoids invoking the context manager multiple times. Here is a modified version of your minimal code snippet that works as you describe on my machine (WSL2):

from rich.progress import Progress
import asyncio

async def meow(progress_bar):
    task = progress_bar.add_task("Meowing...", total=10)
    meows = 0
    meow_total = 3
    meow_increase = 1
    while meows < meow_total:
        progress_bar.update(task, advance=meow_total / meow_increase)
        meows += meow_increase
        await asyncio.sleep(1)
    progress_bar.remove_task(task)

progress = Progress(transient=True)
with progress as progress_bar:
    while True:
        asyncio.run(meow(progress_bar))