|
|
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()
|