$search
00001 # Software License Agreement (BSD License) 00002 # 00003 # Copyright (c) 2009, Willow Garage, Inc. 00004 # All rights reserved. 00005 # 00006 # Redistribution and use in source and binary forms, with or without 00007 # modification, are permitted provided that the following conditions 00008 # are met: 00009 # 00010 # * Redistributions of source code must retain the above copyright 00011 # notice, this list of conditions and the following disclaimer. 00012 # * Redistributions in binary form must reproduce the above 00013 # copyright notice, this list of conditions and the following 00014 # disclaimer in the documentation and/or other materials provided 00015 # with the distribution. 00016 # * Neither the name of Willow Garage, Inc. nor the names of its 00017 # contributors may be used to endorse or promote products derived 00018 # from this software without specific prior written permission. 00019 # 00020 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 00021 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 00022 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 00023 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 00024 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 00025 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 00026 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 00027 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 00028 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00029 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 00030 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00031 # POSSIBILITY OF SUCH DAMAGE. 00032 00033 import sys 00034 import time 00035 00036 class ProgressMeter: 00037 SUFFIXES = { 1000: ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], 00038 1024: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'] } 00039 00040 def __init__(self, path, bytes_total, refresh_rate=1.0): 00041 self.path = path 00042 self.bytes_total = bytes_total 00043 self.refresh_rate = refresh_rate 00044 00045 self.elapsed = 0.0 00046 self.update_elapsed = 0.0 00047 self.bytes_read = 0.0 00048 00049 self.start_time = time.time() 00050 00051 self._update_progress() 00052 00053 def step(self, bytes_read, force_update=False): 00054 self.bytes_read = bytes_read 00055 self.elapsed = time.time() - self.start_time 00056 00057 if force_update or self.elapsed - self.update_elapsed > self.refresh_rate: 00058 self._update_progress() 00059 self.update_elapsed = self.elapsed 00060 00061 def _update_progress(self): 00062 max_path_len = self.terminal_width() - 36 00063 path = self.path 00064 if len(path) > max_path_len: 00065 path = '...' + self.path[-max_path_len + 3:] 00066 00067 bytes_read_str = self.approximate_size(float(self.bytes_read)) 00068 bytes_total_str = self.approximate_size(float(self.bytes_total)) 00069 00070 if self.bytes_read < self.bytes_total: 00071 complete_fraction = float(self.bytes_read) / self.bytes_total 00072 pct_complete = int(100.0 * complete_fraction) 00073 00074 if complete_fraction > 0.0: 00075 eta = self.elapsed * (1.0 / complete_fraction - 1.0) 00076 eta_min, eta_sec = eta / 60, eta % 60 00077 if eta_min > 99: 00078 eta_str = '--:--' 00079 else: 00080 eta_str = '%02d:%02d' % (eta_min, eta_sec) 00081 else: 00082 eta_str = '--:--' 00083 00084 progress = '%-*s %3d%% %8s / %8s %s ETA' % (max_path_len, path, pct_complete, bytes_read_str, bytes_total_str, eta_str) 00085 else: 00086 progress = '%-*s 100%% %19s %02d:%02d ' % (max_path_len, path, bytes_total_str, self.elapsed / 60, self.elapsed % 60) 00087 00088 print '\r', progress, 00089 sys.stdout.flush() 00090 00091 def finish(self): 00092 self.step(self.bytes_total, force_update=True) 00093 print 00094 00095 @classmethod 00096 def approximate_size(cls, size, a_kilobyte_is_1024_bytes=False): 00097 """ 00098 Convert file size to human-readable form 00099 """ 00100 if size < 0: 00101 raise ValueError('number must be non-negative') 00102 00103 multiple = 1024 if a_kilobyte_is_1024_bytes else 1000 00104 for suffix in cls.SUFFIXES[multiple]: 00105 size /= multiple 00106 if size < multiple: 00107 return '%.1f %s' % (size, suffix) 00108 00109 raise ValueError('number too large') 00110 00111 @staticmethod 00112 def terminal_width(): 00113 """ 00114 Estimate the width of the terminal 00115 """ 00116 width = 0 00117 try: 00118 import struct, fcntl, termios 00119 s = struct.pack('HHHH', 0, 0, 0, 0) 00120 x = fcntl.ioctl(1, termios.TIOCGWINSZ, s) 00121 width = struct.unpack('HHHH', x)[1] 00122 except IOError: 00123 pass 00124 if width <= 0: 00125 try: 00126 width = int(os.environ['COLUMNS']) 00127 except Exception: 00128 pass 00129 if width <= 0: 00130 width = 80 00131 00132 return width