From: Michael Tremer Date: Sat, 28 Sep 2013 15:23:00 +0000 (+0200) Subject: Replace python-progressbar by own progressbar module. X-Git-Tag: 0.9.27~14^2~5 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=dccd65eae86e39200973bc000bb87e1afadcd5b9;p=pakfire.git Replace python-progressbar by own progressbar module. --- diff --git a/INSTALL b/INSTALL index 32b5205cf..e581e7a15 100644 --- a/INSTALL +++ b/INSTALL @@ -3,7 +3,6 @@ For general information about pakfire see README. Requirements: * Python 2.6 or greater (not Python 3.x) - * python-progressbar * python-argsparse (included in Python 2.7) * libcap * libsolv diff --git a/Makefile.am b/Makefile.am index ed352a1eb..80029b411 100644 --- a/Makefile.am +++ b/Makefile.am @@ -107,6 +107,7 @@ pakfire_PYTHON = \ src/pakfire/keyring.py \ src/pakfire/logger.py \ src/pakfire/lzma.py \ + src/pakfire/progressbar.py \ src/pakfire/satsolver.py \ src/pakfire/server.py \ src/pakfire/shell.py \ diff --git a/po/POTFILES.in b/po/POTFILES.in index b5003fa8b..5e4597650 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -18,6 +18,7 @@ src/pakfire/packages/lexer.py src/pakfire/packages/make.py src/pakfire/packages/packager.py src/pakfire/packages/tar.py +src/pakfire/progressbar.py src/pakfire/repository/__init__.py src/pakfire/repository/database.py src/pakfire/repository/local.py diff --git a/src/pakfire/packages/packager.py b/src/pakfire/packages/packager.py index c8e1c7814..8ee97c478 100644 --- a/src/pakfire/packages/packager.py +++ b/src/pakfire/packages/packager.py @@ -24,7 +24,6 @@ import fnmatch import glob import hashlib import os -import progressbar import re import shutil import sys diff --git a/src/pakfire/progressbar.py b/src/pakfire/progressbar.py new file mode 100644 index 000000000..434676822 --- /dev/null +++ b/src/pakfire/progressbar.py @@ -0,0 +1,330 @@ +#!/usr/bin/python +############################################################################### +# # +# Pakfire - The IPFire package management system # +# Copyright (C) 2013 Pakfire development team # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +############################################################################### + +from __future__ import division + +import datetime +import fcntl +import math +import signal +import struct +import sys +import termios +import time + +import util + +from i18n import _ + +DEFAULT_VALUE_MAX = 100 +DEFAULT_TERM_WIDTH = 80 + +class ProgressBar(object): + def __init__(self, value_max=None): + self.value_max = value_max or DEFAULT_VALUE_MAX + self.value_cur = 0 + + self.time_start = None + self.finished = False + + # Use the error console as default output. + self.fd = sys.stderr + + # Determine the width of the terminal. + self.term_width = self.get_terminal_width() + self.register_terminal_resize_signal() + + self.widgets = [] + + # Update at max. every poll seconds. + self.poll = 0.5 + + def add(self, widget): + self.widgets.append(widget) + + def start(self): + self.num_intervals = max(self.term_width, 100) + self.next_update = 0 + self.update_interval = self.value_max / self.num_intervals + + # Save the time when we started. + self.time_start = self.time_last_updated = time.time() + + # Initialize the bar. + self.update(0) + + return self + + def finish(self): + if self.finished: + return + + self.finished = True + + # Complete the progress bar. + self.update(self.value_max) + + # End the line. + self.fd.write("\n") + + self.unregister_terminal_resize_signal() + + def update(self, value): + if not self.time_start: + raise RuntimeError("You need to execute start() first") + + self.value_cur = value + + if not self._need_update(): + return + + self.next_update = self.value_cur + self.update_interval + self.last_update_time = time.time() + + self.fd.write(self._format_line()) + self.fd.write("\r") + + def _need_update(self): + if self.value_cur >= self.next_update or self.finished: + return True + + delta = time.time() - self.last_update_time + return delta > self.poll + + def _format_line(self): + result = [] + expandables = [] + + width = self.term_width - (len(self.widgets) - 1) - 4 + + for index, widget in enumerate(self.widgets): + if isinstance(widget, Widget) and widget.expandable: + result.append(widget) + expandables.append(index) + continue + + widget = format_updatable(widget, self) + result.append(widget) + + # Subtract the consumed space by this widget + width -= len(widget) + + while expandables: + portion = int(math.ceil(width / len(expandables))) + index = expandables.pop() + + widget = result[index].update(self, portion) + result[index] = widget + + # Subtract the consumed space by this widget + width -= len(widget) + + return " %s " % " ".join(result) + + def get_terminal_width(self): + cr = util.ioctl_GWINSZ(self.fd) + if cr: + return cr[1] + + # If the ioctl command failed, use the environment data. + columns = os.environ.get("COLUMNS", None) + try: + return int(columns) - 1 + except (TypeError, ValueError): + pass + + return DEFAULT_TERM_WIDTH + + def handle_terminal_resize(self, *args, **kwargs): + """ + Catches terminal resize signals. + """ + self.term_width = self.get_terminal_width() + + def register_terminal_resize_signal(self): + signal.signal(signal.SIGWINCH, self.handle_terminal_resize) + + def unregister_terminal_resize_signal(self): + signal.signal(signal.SIGWINCH, signal.SIG_DFL) + + @property + def percentage(self): + if self.value_cur >= self.value_max: + return 100.0 + + return self.value_cur * 100.0 / self.value_max + + @property + def seconds_elapsed(self): + if self.time_start: + return time.time() - self.time_start + + return 0 + + +def format_updatable(widget, pbar): + if hasattr(widget, "update"): + return widget.update(pbar) + + return widget + + +class Widget(object): + expandable = False + + def update(self, pbar): + pass + +class WidgetFill(Widget): + expandable = True + + def update(self, pbar, width): + return "#" * width + + +class WidgetTimer(Widget): + def __init__(self, format_string=None): + if format_string is None: + format_string = _("Elapsed Time: %s") + + self.format_string = format_string + + @staticmethod + def format_time(seconds): + try: + seconds = int(seconds) + except ValueError: + pass + + return "%s" % datetime.timedelta(seconds=seconds) + + def update(self, pbar): + return self.format_string % self.format_time(pbar.seconds_elapsed) + + +class WidgetETA(WidgetTimer): + def update(self, pbar): + fmt = "%-5s: %s" + + if pbar.value_cur == 0: + return fmt % (_("ETA"), "--:--:--") + + elif pbar.finished: + return fmt % (_("Time"), self.format_time(pbar.seconds_elapsed)) + + else: + eta = pbar.seconds_elapsed * pbar.value_max / pbar.value_cur - pbar.seconds_elapsed + return fmt % (_("ETA"), self.format_time(eta)) + + +class WidgetAnimatedMarker(Widget): + def __init__(self): + self.markers = "|/-\\" + self.marker_cur = -1 + + def update(self, pbar): + if pbar.finished: + return self.markers[0] + + self.marker_cur = (self.marker_cur + 1) % len(self.markers) + return self.markers[self.marker_cur] + + +class WidgetCounter(Widget): + def __init__(self, format_string="%d"): + self.format_string = format_string + + def update(self, pbar): + return self.format_string % pbar.value_cur + + +class WidgetPercentage(Widget): + def update(self, pbar): + return "%3d%%" % pbar.percentage + + +class WidgetBar(WidgetFill): + def __init__(self): + self.marker = "#" + self.marker_inactive = "-" + + self.marker_left = "[" + self.marker_right = "]" + + def update(self, pbar, width): + # Clear the screen if the progress has finished. + if pbar.finished: + return " " * width + + marker_left, marker, marker_inactive, marker_right = (format_updatable(w, pbar) + for w in (self.marker_left, self.marker, self.marker_inactive, self.marker_right)) + + width -= len(marker_left) + len(marker_right) + + if pbar.value_max: + marker *= pbar.value_cur * width // pbar.value_max + else: + marker = "" + + return "".join((marker_left, marker.ljust(width, marker_inactive), marker_right)) + + +class WidgetFileTransferSpeed(Widget): + def update(self, pbar): + speed = 0 + + if pbar.seconds_elapsed >= 1 and pbar.value_cur > 0: + speed = pbar.value_cur / pbar.seconds_elapsed + + return util.format_speed(speed) + + +if __name__ == "__main__": + pbar = ProgressBar(100) + + counter = WidgetCounter() + pbar.add(counter) + + timer = WidgetTimer() + pbar.add(timer) + + bar = WidgetBar() + pbar.add(bar) + + fill = WidgetFill() + pbar.add(fill) + + eta = WidgetETA() + pbar.add(eta) + + percentage = WidgetPercentage() + pbar.add(percentage) + + speed = WidgetFileTransferSpeed() + pbar.add(speed) + + pbar.start() + + for i in range(100): + pbar.update(i) + time.sleep(0.25) + + pbar.finish() diff --git a/src/pakfire/transaction.py b/src/pakfire/transaction.py index 2cf14f7f9..94fdb594a 100644 --- a/src/pakfire/transaction.py +++ b/src/pakfire/transaction.py @@ -498,12 +498,17 @@ class Transaction(object): actions.append(action) # Make a nice progressbar. - p = util.make_progress(_("Verifying signatures..."), len(actions)) + p = progressbar.ProgressBar(len(actions)) + p.add(_("Verifying signatures...")) + p.add(progressbar.WidgetBar()) + p.add(progressbar.WidgetPercentage()) # Collect all errors. errors = [] try: + p.start() + # Do the verification for every action. i = 0 for action in actions: diff --git a/src/pakfire/util.py b/src/pakfire/util.py index 73c62d066..ec6c847db 100644 --- a/src/pakfire/util.py +++ b/src/pakfire/util.py @@ -84,30 +84,27 @@ def make_progress(message, maxval, eta=True, speed=False): if not sys.stdout.isatty(): return - widgets = [ - " ", - "%-64s" % message, - " ", - progressbar.Bar(left="[", right="]"), - " ", - ] + if not maxval: + maxval = 1 - if speed: - widgets += [ - progressbar.Percentage(), " ", - progressbar.FileTransferSpeed(), " " - ] + pb = progressbar.ProgressBar(maxval) + pb.add("%-50s" % message) - if eta: - widgets += [progressbar.ETA(), " ",] + bar = progressbar.WidgetBar() + pb.add(bar) - if not maxval: - maxval = 1 + if speed: + percentage = progressbar.WidgetPercentage() + pb.add(percentage) - pb = progressbar.ProgressBar(widgets=widgets, maxval=maxval) - pb.start() + filetransfer = progressbar.WidgetFileTransferSpeed() + pb.add(filetransfer) - return pb + if eta: + eta = progressbar.WidgetETA() + pb.add(eta) + + return pb.start() def rm(path, *args, **kargs): """ @@ -134,11 +131,9 @@ def rm(path, *args, **kargs): def ioctl_GWINSZ(fd): try: - cr = struct.unpack("hh", fcntl.ioctl(fd, termios.TIOCGWINSZ, "1234")) + return struct.unpack("hh", fcntl.ioctl(fd, termios.TIOCGWINSZ, "1234")) except: - return None - - return cr + pass def terminal_size(): cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2)