Blame SOURCES/progressbar.py

c05548
#!/usr/bin/python3
c05548
# -*- coding: utf-8 -*-
c05548
#
c05548
# A simple text-based progress bar, compatible with the basic API of:
c05548
# https://github.com/WoLpH/python-progressbar
c05548
#
c05548
# Copyright (C) 2021  Red Hat, Inc.
c05548
# 
c05548
# This program is free software; you can redistribute it and/or
c05548
# modify it under the terms of the GNU General Public License
c05548
# as published by the Free Software Foundation; either version 2
c05548
# of the License, or (at your option) any later version.
c05548
# 
c05548
# This program is distributed in the hope that it will be useful,
c05548
# but WITHOUT ANY WARRANTY; without even the implied warranty of
c05548
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
c05548
# GNU General Public License for more details.
c05548
# 
c05548
# You should have received a copy of the GNU General Public License
c05548
# along with this program; if not, write to the Free Software
c05548
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
c05548
# USA.
c05548
c05548
c05548
import shutil
c05548
import sys
c05548
import time
c05548
c05548
c05548
class ProgressBar:
c05548
    FORMAT = '{value:>10} / {max_value:<10} [{bars}]'
c05548
    BARS = '= '
c05548
    SPINLEN = 5
c05548
c05548
    def __init__(self, stream=sys.stderr, max_width=80, fps=10):
c05548
        self._stream = stream
c05548
        self._max_width = max_width
c05548
        self._min_delay = 1 / fps
c05548
c05548
    @staticmethod
c05548
    def _format_value(value):
c05548
        raise NotImplementedError()
c05548
c05548
    def start(self, max_value):
c05548
        self._value = 0
c05548
        self._max_value = max_value or 0
c05548
        self._status = dict()
c05548
        self._spinner = 0
c05548
        self._timestamp = 0
c05548
        self.update(0)
c05548
c05548
    def update(self, value):
c05548
        self._value = value
c05548
        if value > self._max_value:
c05548
            self._max_value = 0
c05548
c05548
        ts = time.time()
c05548
        if (ts - self._timestamp) < self._min_delay:
c05548
            return
c05548
        self._timestamp = ts
c05548
c05548
        status = {'value': self._format_value(value),
c05548
                  'max_value': self._format_value(self._max_value) \
c05548
                               if self._max_value else '???',
c05548
                  'bars': ''}
c05548
c05548
        termw = min(shutil.get_terminal_size()[0], self._max_width)
c05548
        nbars = max(termw - len(self.FORMAT.format(**status)), 0)
c05548
        nfill = nskip = 0
c05548
c05548
        if self._max_value:
c05548
            nfill = round(nbars * value / self._max_value)
c05548
        elif nbars > self.SPINLEN:
c05548
            nfill = self.SPINLEN
c05548
            nskip = self._spinner % (nbars - self.SPINLEN)
c05548
            self._spinner = nskip + 1
c05548
c05548
        status['bars'] = self.BARS[1] * nskip + \
c05548
                         self.BARS[0] * nfill + \
c05548
                         self.BARS[1] * (nbars - nfill - nskip)
c05548
c05548
        if status == self._status:
c05548
            return
c05548
        self._status = status
c05548
c05548
        self._stream.write('\r')
c05548
        self._stream.write(self.FORMAT.format(**self._status))
c05548
        self._stream.flush()
c05548
c05548
    def finish(self):
c05548
        self._max_value = self._value
c05548
        self._timestamp = 0  # Force an update
c05548
        self.update(self._value)
c05548
c05548
        self._stream.write('\n')
c05548
        self._stream.flush()
c05548
c05548
c05548
class DataTransferBar(ProgressBar):
c05548
    @staticmethod
c05548
    def _format_value(value):
c05548
        symbols = ' KMGTPEZY'
c05548
        depth = 0
c05548
        max_depth = len(symbols) - 1
c05548
        unit = 1024.0
c05548
c05548
        # 1023.95 should be formatted as 1.0 (not 1024.0)
c05548
        # More info: https://stackoverflow.com/a/63839503
c05548
        thres = unit - 0.05
c05548
c05548
        while value >= thres and depth < max_depth:
c05548
            depth += 1
c05548
            value /= unit
c05548
        symbol = ' %siB' % symbols[depth] if depth > 0 else ''
c05548
c05548
        return '%.1f%s' % (value, symbol)
c05548
c05548
c05548
if __name__ == '__main__':
c05548
    # Show a dummy bar for debugging purposes
c05548
c05548
    bar = DataTransferBar()
c05548
    size = 50*1024*1024
c05548
    chunk = 1024*1234
c05548
    recvd = 0
c05548
c05548
    bar.start(size)
c05548
    while recvd < (size - chunk):
c05548
        recvd += chunk
c05548
        bar.update(recvd)
c05548
        time.sleep(0.1)
c05548
    bar.update(size)
c05548
    bar.finish()