From b8bbb75f9c94ec17cd2bd96a6e3cf17218985ea9 Mon Sep 17 00:00:00 2001 From: Nick Sweeting Date: Sat, 31 Oct 2020 07:56:05 -0400 Subject: [PATCH] logarithmic progress bars woohoo --- archivebox/config.py | 2 +- archivebox/logging_util.py | 39 ++++++++++++++++++++++++++++---------- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/archivebox/config.py b/archivebox/config.py index 1ede0b07..d9716113 100644 --- a/archivebox/config.py +++ b/archivebox/config.py @@ -48,7 +48,7 @@ CONFIG_DEFAULTS: Dict[str, ConfigDefaultDict] = { 'SHELL_CONFIG': { 'IS_TTY': {'type': bool, 'default': lambda _: sys.stdout.isatty()}, 'USE_COLOR': {'type': bool, 'default': lambda c: c['IS_TTY']}, - 'SHOW_PROGRESS': {'type': bool, 'default': lambda c: False if platform.system() == 'Darwin' else c['IS_TTY']}, # TODO: remove this temporary hack once progress bars are fixed on macOS + 'SHOW_PROGRESS': {'type': bool, 'default': lambda c: c['IS_TTY']}, 'IN_DOCKER': {'type': bool, 'default': False}, # TODO: 'SHOW_HINTS': {'type: bool, 'default': True}, }, diff --git a/archivebox/logging_util.py b/archivebox/logging_util.py index a43d31c9..b404aa6d 100644 --- a/archivebox/logging_util.py +++ b/archivebox/logging_util.py @@ -4,7 +4,9 @@ import re import os import sys import time +import platform import argparse +from math import log from multiprocessing import Process from pathlib import Path @@ -99,10 +101,24 @@ class TimedProgress: if self.SHOW_PROGRESS: # terminate if we havent already terminated try: + # WARNING: HACKY + # I've spent over 15 hours trying to get rid of this stupid macOS-only + # intermittent (but harmeless) warning when the progress bars end sometimes + # Exception ignored in: <_io.TextIOWrapper name='' mode='w' encoding='utf-8'> + # BrokenPipeError: [Errno 32] Broken pipe + # In the end, this is the only thing I found that makes it + # happen slightly less often: + if platform.system() == 'Darwin': + time.sleep(0.1) + + # kill the progress bar subprocess self.p.terminate() self.p.join() self.p.close() + if platform.system() == 'Darwin': + time.sleep(0.1) + # clear whole terminal line try: sys.stdout.write('\r{}{}\r'.format((' ' * TERM_WIDTH()), ANSI['reset'])) @@ -128,17 +144,18 @@ def progress_bar(seconds: int, prefix: str='') -> None: sys.stdout.write('\r\n') sys.stdout.flush() chunks = max_width - len(prefix) - 20 - progress = s / chunks / seconds * 100 - bar_width = round(progress/(100/chunks)) + pct_complete = s / chunks / seconds * 100 + log_pct = (log(pct_complete or 1, 10) / 2) * 100 # everyone likes faster progress bars ;) + bar_width = round(log_pct/(100/chunks)) last_width = max_width # ████████████████████ 0.9% (1/60sec) sys.stdout.write('\r{0}{1}{2}{3} {4}% ({5}/{6}sec)'.format( prefix, - ANSI['green'], + ANSI['green' if pct_complete < 80 else 'lightyellow'], (chunk * bar_width).ljust(chunks), ANSI['reset'], - round(progress, 1), + round(pct_complete, 1), round(s/chunks), seconds, )) @@ -146,7 +163,7 @@ def progress_bar(seconds: int, prefix: str='') -> None: time.sleep(1 / chunks) # ██████████████████████████████████ 100.0% (60/60sec) - sys.stdout.write('\r{0}{1}{2}{3} {4}% ({5}/{6}sec)\n'.format( + sys.stdout.write('\r{0}{1}{2}{3} {4}% ({5}/{6}sec)'.format( prefix, ANSI['red'], chunk * chunks, @@ -156,6 +173,10 @@ def progress_bar(seconds: int, prefix: str='') -> None: seconds, )) sys.stdout.flush() + # uncomment to have it disappear when it hits 100% instead of staying full red: + # time.sleep(0.5) + # sys.stdout.write('\r{}{}\r'.format((' ' * TERM_WIDTH()), ANSI['reset'])) + # sys.stdout.flush() except (KeyboardInterrupt, BrokenPipeError): print() pass @@ -242,7 +263,7 @@ def log_archiving_started(num_links: int, resume: Optional[float]=None): **ANSI, )) else: - print('{green}[▶] [{}] Collecting content for {} Snapshots in archive...{reset}'.format( + print('{green}[▶] [{}] Starting archiving of {} snapshots in index...{reset}'.format( start_ts.strftime('%Y-%m-%d %H:%M:%S'), num_links, **ANSI, @@ -287,10 +308,8 @@ def log_archiving_finished(num_links: int): print(' - {} links updated'.format(_LAST_RUN_STATS.succeeded + _LAST_RUN_STATS.failed)) print(' - {} links had errors'.format(_LAST_RUN_STATS.failed)) print() - print(' {lightred}Hint:{reset} To view your archive index, run:'.format(**ANSI)) - print(' archivebox server # then visit http://127.0.0.1:8000') - print(' Or run the built-in webserver:') - print(' archivebox server') + print(' {lightred}Hint:{reset} To manage your archive in a Web UI, run:'.format(**ANSI)) + print(' archivebox server 0.0.0.0:8000') def log_link_archiving_started(link: "Link", link_dir: str, is_new: bool):