Skip to content

Instantly share code, notes, and snippets.

@mosquito
Last active April 10, 2023 09:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mosquito/9e2d9ddb82de6abe4c048f27768509f1 to your computer and use it in GitHub Desktop.
Save mosquito/9e2d9ddb82de6abe4c048f27768509f1 to your computer and use it in GitHub Desktop.
Pure python progressbar without dependencies
import sys
from os import get_terminal_size
from typing import Sequence, TypeVar, Generator, Any
__license__ = "MIT"
__doc__ = """
The point of this project is to give you some code you can easily copy and paste
into your own stuff so you don't have to go through the trouble of dragging in
an any dependency like tqdm or rich.
"""
class ProgressBar:
NO_TTY_COLUMNS: int = 80
TTY_DONE_SYMBOL: str = "\u2588"
TTY_LEFT_SYMBOL: str = "\u2591"
NO_TTY_DONE_SYMBOL: str = '#'
NO_TTY_LEFT_SYMBOL: str = '.'
TTY_FILENO: int = sys.stderr.fileno()
BAR: str = "{done}{left} {percent:>3}% {suffix}"
def __init__(
self, total: int = 0, suffix: str = '', use_tty: bool = True,
updates_delay: float = 0.15
):
"""
Simple progressbar implementation
:param total: The value that will mean 100%
:param suffix: The additional information which printed
right or percents
:param use_tty: use interactive tty mode instead prints progress line
by line
:param updates_delay: the minimum time between updates to avoid
updating too often
"""
self.total: int = total
self.suffix: str = suffix
self.value: int = 0
self.delay = updates_delay
self._last_update: float = 0.
self._last_percent: int = 0
self.interactive: bool = False
self.tty_columns: int = self.NO_TTY_COLUMNS
self.done_symbol: str = self.NO_TTY_DONE_SYMBOL
self.left_symbol: str = self.NO_TTY_LEFT_SYMBOL
if use_tty:
try:
self.tty_columns, _ = get_terminal_size(self.TTY_FILENO)
self.done_symbol = self.TTY_DONE_SYMBOL
self.left_symbol = self.TTY_LEFT_SYMBOL
self.interactive = True
except OSError:
self.interactive = False
self.offset: int = 6
def draw(self) -> None:
# simple optimization not redraw the bar when percentage was not changed
if self._last_percent == self.percent:
return
now = time()
if (
self._last_update + self.delay > now and
self.value < self.total
):
return
self._last_percent = self.percent
self._last_update = now
sys.stderr.write("\r" if self.interactive else "\n")
sys.stderr.write(self.line)
if self.interactive:
sys.stderr.flush()
def update(self, value: int) -> None:
self.value = value
self.draw()
def inc(self, value: int) -> None:
self.value += value
self.draw()
@property
def percent(self) -> int:
return round(self.value / self.total * 100)
@property
def columns(self) -> int:
return self.tty_columns - len(self.suffix) - self.offset
@property
def line(self) -> str:
columns = self.columns
percent = self.percent
done = round((percent / 100) * columns)
left = columns - done
return self.BAR.format(
done=self.done_symbol * done,
left=self.left_symbol * left,
percent=percent, suffix=self.suffix,
).strip()
T = TypeVar("T")
def progress(seq: Sequence[T], **kwargs) -> Generator[T, Any, None]:
if 'total' not in kwargs:
kwargs['total'] = len(seq)
bar: ProgressBar = ProgressBar(**kwargs)
for item in seq:
bar.inc(1)
yield item
if __name__ == '__main__':
from time import sleep, time
progress_bar = ProgressBar(total=100, use_tty=True,
suffix="[Use interactive tty mode]")
for _ in range(100):
progress_bar.inc(1)
sleep(0.01)
progress_bar = ProgressBar(total=100, use_tty=False,
suffix="[Print line by line]")
for _ in range(100):
progress_bar.inc(1)
sleep(0.01)
asterisks = ["*"] * 100
for asterisk in progress(asterisks, suffix='asterisks'):
sleep(0.01)
@mosquito
Copy link
Author

mosquito commented Apr 10, 2023

███████████████████████████████████████████████████████████████████████████████ 100% [Use interactive tty mode]
#.....................................................   1% [Print line by line]
########..............................................  14% [Print line by line]
###############.......................................  27% [Print line by line]
######################................................  40% [Print line by line]
#############################.........................  53% [Print line by line]
####################################..................  66% [Print line by line]
###########################################...........  79% [Print line by line]
##################################################....  92% [Print line by line]
###################################################### 100% [Print line by line]
████████████████████████████████████████████████████████████████████████████████████████████████ 100% asterisks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment