]> git.ipfire.org Git - pakfire.git/commitdiff
Replace python-progressbar by own progressbar module.
authorMichael Tremer <michael.tremer@ipfire.org>
Sat, 28 Sep 2013 15:23:00 +0000 (17:23 +0200)
committerMichael Tremer <michael.tremer@ipfire.org>
Sat, 28 Sep 2013 15:23:00 +0000 (17:23 +0200)
INSTALL
Makefile.am
po/POTFILES.in
src/pakfire/packages/packager.py
src/pakfire/progressbar.py [new file with mode: 0644]
src/pakfire/transaction.py
src/pakfire/util.py

diff --git a/INSTALL b/INSTALL
index 32b5205cfdabefb99e55797ddaa696a6250877ec..e581e7a15ab1e6120ec06ad7ebd23b5a035bd985 100644 (file)
--- 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
index ed352a1eb6ea87bd5c598e1d9783bf653e153a0d..80029b411ed6a2ad27a05e3ae37545b177747682 100644 (file)
@@ -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 \
index b5003fa8b3f5ed292a3475ee9ce75a5a93bbc03e..5e4597650d870d86244e2f186575fd3459e711fd 100644 (file)
@@ -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
index c8e1c7814bccb9d9bbe3f3df1c18684687755f8a..8ee97c47877299fb8d2701e3581466ff3731818f 100644 (file)
@@ -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 (file)
index 0000000..4346768
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+###############################################################################
+
+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()
index 2cf14f7f9aa661d8c4ddd4a3013b0408b14c7b9d..94fdb594ac336339ede64fe0db20e4208a9c4391 100644 (file)
@@ -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:
index 73c62d0660800e1c332e46e10e8226a8e4c691e0..ec6c847db136ceaef37ff488f9c7d9cb22460fb4 100644 (file)
@@ -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)