From 7fd5d7a5df3b8bef142a6a05bc22eb4d75c153d0 Mon Sep 17 00:00:00 2001 From: Lucas Meneghel Rodrigues Date: Wed, 29 Jul 2015 16:15:36 -0300 Subject: [PATCH] avocado.utils: Add output module Add avocado.utils.output, a library with utility functions for user friendly display of information. Signed-off-by: Lucas Meneghel Rodrigues --- avocado/utils/output.py | 150 ++++++++++++++++++ .../all/unit/avocado/utils_output_unittest.py | 35 ++++ 2 files changed, 185 insertions(+) create mode 100644 avocado/utils/output.py create mode 100644 selftests/all/unit/avocado/utils_output_unittest.py diff --git a/avocado/utils/output.py b/avocado/utils/output.py new file mode 100644 index 00000000..014d9e7f --- /dev/null +++ b/avocado/utils/output.py @@ -0,0 +1,150 @@ +# 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 2 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 LICENSE for more details. +# +# This code was inspired in the autotest project, +# +# client/shared/utils.py +# Original author: Cleber Rosa +# +# Copyright: Red Hat Inc. 2015 +# Author: Lucas Meneghel Rodrigues + +""" +Utility functions for user friendly display of information. +""" + +import sys + + +def display_data_size(size): + """ + Display data size in human readable units (SI). + + :param size: Data size, in Bytes. + :type size: int + :return: Human readable string with data size, using SI prefixes. + """ + prefixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'] + factor = float(1000) + i = 0 + while size >= factor: + if i >= len(prefixes) - 1: + break + size /= factor + i += 1 + + return '%.2f %s' % (size, prefixes[i]) + + +class ProgressBar(object): + + """ + Displays interactively the progress of a given task + + Inspired/adapted from https://gist.github.com/t0xicCode/3306295 + """ + + def __init__(self, minimum=0, maximum=100, width=75, title=''): + """ + Initializes a new progress bar + + :type minimum: integer + :param minimum: minimum (initial) value on the progress bar + :type maximum: integer + :param maximum: maximum (final) value on the progress bar + :type width: integer + :param with: number of columns, that is screen width + """ + assert maximum > minimum + + self.prog_bar = '' + self.old_prog_bar = '' + + if title: + width -= len(title) + + self.minimum = minimum + self.maximum = maximum + self.range = maximum - minimum + self.width = width + self.title = title + + self.current_amount = minimum + self.update_amount(minimum) + + def append_amount(self, amount): + """ + Increments the current amount value. + """ + self.update_amount(self.current_amount + amount) + + def update_percentage(self, percentage): + """ + Updates the progress bar to the new percentage. + """ + self.update_amount((percentage * float(self.maximum)) / 100.0) + + def update_amount(self, amount): + """ + Performs sanity checks and update the current amount. + """ + if amount < self.minimum: + amount = self.minimum + if amount > self.maximum: + amount = self.maximum + self.current_amount = amount + + self._update_progress_bar() + self.draw() + + def _update_progress_bar(self): + """ + Builds the actual progress bar text. + """ + diff = float(self.current_amount - self.minimum) + done = (diff / float(self.range)) * 100.0 + done = int(round(done)) + + all_full = self.width - 2 + hashes = (done / 100.0) * all_full + hashes = int(round(hashes)) + + if hashes == 0: + screen_text = "[>%s]" % (' '*(all_full-1)) + elif hashes == all_full: + screen_text = "[%s]" % ('='*all_full) + else: + screen_text = "[%s>%s]" % ('='*(hashes-1), ' '*(all_full-hashes)) + + percent_string = str(done) + "%" + + # slice the percentage into the bar + screen_text = ' '.join([screen_text, percent_string]) + + if self.title: + screen_text = '%s: %s' % (self.title, + screen_text) + self.prog_bar = screen_text + + def draw(self): + """ + Prints the updated text to the screen. + """ + if self.prog_bar != self.old_prog_bar: + self.old_prog_bar = self.prog_bar + sys.stdout.write('\r' + self.prog_bar) + sys.stdout.flush() + + def __str__(self): + """ + Returns the current progress bar. + """ + return str(self.prog_bar) diff --git a/selftests/all/unit/avocado/utils_output_unittest.py b/selftests/all/unit/avocado/utils_output_unittest.py new file mode 100644 index 00000000..573fb0da --- /dev/null +++ b/selftests/all/unit/avocado/utils_output_unittest.py @@ -0,0 +1,35 @@ +import unittest +import os +import sys + +# simple magic for using scripts within a source tree +basedir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +basedir = os.path.dirname(basedir) +if os.path.isdir(os.path.join(basedir, 'avocado')): + sys.path.append(basedir) + +from avocado.utils import output + + +class UtilsOutputTest(unittest.TestCase): + + def testDisplayDataSizeFactor1024(self): + self.assertEqual(output.display_data_size(103), '103.00 B') + self.assertEqual(output.display_data_size(1024**1), '1.02 KB') + self.assertEqual(output.display_data_size(1024**2), '1.05 MB') + self.assertEqual(output.display_data_size(1024**3), '1.07 GB') + self.assertEqual(output.display_data_size(1024**4), '1.10 TB') + self.assertEqual(output.display_data_size(1024**5), '1.13 PB') + self.assertEqual(output.display_data_size(1024**6), '1152.92 PB') + + def testDisplayDataSizeFactor1000(self): + self.assertEqual(output.display_data_size(1000**1), '1.00 KB') + self.assertEqual(output.display_data_size(1000**2), '1.00 MB') + self.assertEqual(output.display_data_size(1000**3), '1.00 GB') + self.assertEqual(output.display_data_size(1000**4), '1.00 TB') + self.assertEqual(output.display_data_size(1000**5), '1.00 PB') + self.assertEqual(output.display_data_size(1000**6), '1000.00 PB') + + +if __name__ == '__main__': + unittest.main() -- GitLab