|
|
# encoding: UTF-8
|
|
|
import sys
|
|
|
import datetime
|
|
|
from string import Template
|
|
|
from rhodecode.lib.vcs.utils.filesize import filesizeformat
|
|
|
from rhodecode.lib.vcs.utils.helpers import get_total_seconds
|
|
|
|
|
|
|
|
|
class ProgressBarError(Exception):
|
|
|
pass
|
|
|
|
|
|
class AlreadyFinishedError(ProgressBarError):
|
|
|
pass
|
|
|
|
|
|
|
|
|
class ProgressBar(object):
|
|
|
|
|
|
default_elements = ['percentage', 'bar', 'steps']
|
|
|
|
|
|
def __init__(self, steps=100, stream=None, elements=None):
|
|
|
self.step = 0
|
|
|
self.steps = steps
|
|
|
self.stream = stream or sys.stderr
|
|
|
self.bar_char = '='
|
|
|
self.width = 50
|
|
|
self.separator = ' | '
|
|
|
self.elements = elements or self.default_elements
|
|
|
self.started = None
|
|
|
self.finished = False
|
|
|
self.steps_label = 'Step'
|
|
|
self.time_label = 'Time'
|
|
|
self.eta_label = 'ETA'
|
|
|
self.speed_label = 'Speed'
|
|
|
self.transfer_label = 'Transfer'
|
|
|
|
|
|
def __str__(self):
|
|
|
return self.get_line()
|
|
|
|
|
|
def __iter__(self):
|
|
|
start = self.step
|
|
|
end = self.steps + 1
|
|
|
for x in xrange(start, end):
|
|
|
self.render(x)
|
|
|
yield x
|
|
|
|
|
|
def get_separator(self):
|
|
|
return self.separator
|
|
|
|
|
|
def get_bar_char(self):
|
|
|
return self.bar_char
|
|
|
|
|
|
def get_bar(self):
|
|
|
char = self.get_bar_char()
|
|
|
perc = self.get_percentage()
|
|
|
length = int(self.width * perc / 100)
|
|
|
bar = char * length
|
|
|
bar = bar.ljust(self.width)
|
|
|
return bar
|
|
|
|
|
|
def get_elements(self):
|
|
|
return self.elements
|
|
|
|
|
|
def get_template(self):
|
|
|
separator = self.get_separator()
|
|
|
elements = self.get_elements()
|
|
|
return Template(separator.join((('$%s' % e) for e in elements)))
|
|
|
|
|
|
def get_total_time(self, current_time=None):
|
|
|
if current_time is None:
|
|
|
current_time = datetime.datetime.now()
|
|
|
if not self.started:
|
|
|
return datetime.timedelta()
|
|
|
return current_time - self.started
|
|
|
|
|
|
def get_rendered_total_time(self):
|
|
|
delta = self.get_total_time()
|
|
|
if not delta:
|
|
|
ttime = '-'
|
|
|
else:
|
|
|
ttime = str(delta)
|
|
|
return '%s %s' % (self.time_label, ttime)
|
|
|
|
|
|
def get_eta(self, current_time=None):
|
|
|
if current_time is None:
|
|
|
current_time = datetime.datetime.now()
|
|
|
if self.step == 0:
|
|
|
return datetime.timedelta()
|
|
|
total_seconds = get_total_seconds(self.get_total_time())
|
|
|
eta_seconds = total_seconds * self.steps / self.step - total_seconds
|
|
|
return datetime.timedelta(seconds=int(eta_seconds))
|
|
|
|
|
|
def get_rendered_eta(self):
|
|
|
eta = self.get_eta()
|
|
|
if not eta:
|
|
|
eta = '--:--:--'
|
|
|
else:
|
|
|
eta = str(eta).rjust(8)
|
|
|
return '%s: %s' % (self.eta_label, eta)
|
|
|
|
|
|
def get_percentage(self):
|
|
|
return float(self.step) / self.steps * 100
|
|
|
|
|
|
def get_rendered_percentage(self):
|
|
|
perc = self.get_percentage()
|
|
|
return ('%s%%' % (int(perc))).rjust(5)
|
|
|
|
|
|
def get_rendered_steps(self):
|
|
|
return '%s: %s/%s' % (self.steps_label, self.step, self.steps)
|
|
|
|
|
|
def get_rendered_speed(self, step=None, total_seconds=None):
|
|
|
if step is None:
|
|
|
step = self.step
|
|
|
if total_seconds is None:
|
|
|
total_seconds = get_total_seconds(self.get_total_time())
|
|
|
if step <= 0 or total_seconds <= 0:
|
|
|
speed = '-'
|
|
|
else:
|
|
|
speed = filesizeformat(float(step) / total_seconds)
|
|
|
return '%s: %s/s' % (self.speed_label, speed)
|
|
|
|
|
|
def get_rendered_transfer(self, step=None, steps=None):
|
|
|
if step is None:
|
|
|
step = self.step
|
|
|
if steps is None:
|
|
|
steps = self.steps
|
|
|
|
|
|
if steps <= 0:
|
|
|
return '%s: -' % self.transfer_label
|
|
|
total = filesizeformat(float(steps))
|
|
|
if step <= 0:
|
|
|
transferred = '-'
|
|
|
else:
|
|
|
transferred = filesizeformat(float(step))
|
|
|
return '%s: %s / %s' % (self.transfer_label, transferred, total)
|
|
|
|
|
|
def get_context(self):
|
|
|
return {
|
|
|
'percentage': self.get_rendered_percentage(),
|
|
|
'bar': self.get_bar(),
|
|
|
'steps': self.get_rendered_steps(),
|
|
|
'time': self.get_rendered_total_time(),
|
|
|
'eta': self.get_rendered_eta(),
|
|
|
'speed': self.get_rendered_speed(),
|
|
|
'transfer': self.get_rendered_transfer(),
|
|
|
}
|
|
|
|
|
|
def get_line(self):
|
|
|
template = self.get_template()
|
|
|
context = self.get_context()
|
|
|
return template.safe_substitute(**context)
|
|
|
|
|
|
def write(self, data):
|
|
|
self.stream.write(data)
|
|
|
|
|
|
def render(self, step):
|
|
|
if not self.started:
|
|
|
self.started = datetime.datetime.now()
|
|
|
if self.finished:
|
|
|
raise AlreadyFinishedError
|
|
|
self.step = step
|
|
|
self.write('\r%s' % self)
|
|
|
if step == self.steps:
|
|
|
self.finished = True
|
|
|
if step == self.steps:
|
|
|
self.write('\n')
|
|
|
|
|
|
|
|
|
"""
|
|
|
termcolors.py
|
|
|
|
|
|
Grabbed from Django (http://www.djangoproject.com)
|
|
|
"""
|
|
|
|
|
|
color_names = ('black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white')
|
|
|
foreground = dict([(color_names[x], '3%s' % x) for x in range(8)])
|
|
|
background = dict([(color_names[x], '4%s' % x) for x in range(8)])
|
|
|
|
|
|
RESET = '0'
|
|
|
opt_dict = {'bold': '1', 'underscore': '4', 'blink': '5', 'reverse': '7', 'conceal': '8'}
|
|
|
|
|
|
def colorize(text='', opts=(), **kwargs):
|
|
|
"""
|
|
|
Returns your text, enclosed in ANSI graphics codes.
|
|
|
|
|
|
Depends on the keyword arguments 'fg' and 'bg', and the contents of
|
|
|
the opts tuple/list.
|
|
|
|
|
|
Returns the RESET code if no parameters are given.
|
|
|
|
|
|
Valid colors:
|
|
|
'black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'
|
|
|
|
|
|
Valid options:
|
|
|
'bold'
|
|
|
'underscore'
|
|
|
'blink'
|
|
|
'reverse'
|
|
|
'conceal'
|
|
|
'noreset' - string will not be auto-terminated with the RESET code
|
|
|
|
|
|
Examples:
|
|
|
colorize('hello', fg='red', bg='blue', opts=('blink',))
|
|
|
colorize()
|
|
|
colorize('goodbye', opts=('underscore',))
|
|
|
print colorize('first line', fg='red', opts=('noreset',))
|
|
|
print 'this should be red too'
|
|
|
print colorize('and so should this')
|
|
|
print 'this should not be red'
|
|
|
"""
|
|
|
code_list = []
|
|
|
if text == '' and len(opts) == 1 and opts[0] == 'reset':
|
|
|
return '\x1b[%sm' % RESET
|
|
|
for k, v in kwargs.iteritems():
|
|
|
if k == 'fg':
|
|
|
code_list.append(foreground[v])
|
|
|
elif k == 'bg':
|
|
|
code_list.append(background[v])
|
|
|
for o in opts:
|
|
|
if o in opt_dict:
|
|
|
code_list.append(opt_dict[o])
|
|
|
if 'noreset' not in opts:
|
|
|
text = text + '\x1b[%sm' % RESET
|
|
|
return ('\x1b[%sm' % ';'.join(code_list)) + text
|
|
|
|
|
|
def make_style(opts=(), **kwargs):
|
|
|
"""
|
|
|
Returns a function with default parameters for colorize()
|
|
|
|
|
|
Example:
|
|
|
bold_red = make_style(opts=('bold',), fg='red')
|
|
|
print bold_red('hello')
|
|
|
KEYWORD = make_style(fg='yellow')
|
|
|
COMMENT = make_style(fg='blue', opts=('bold',))
|
|
|
"""
|
|
|
return lambda text: colorize(text, opts, **kwargs)
|
|
|
|
|
|
NOCOLOR_PALETTE = 'nocolor'
|
|
|
DARK_PALETTE = 'dark'
|
|
|
LIGHT_PALETTE = 'light'
|
|
|
|
|
|
PALETTES = {
|
|
|
NOCOLOR_PALETTE: {
|
|
|
'ERROR': {},
|
|
|
'NOTICE': {},
|
|
|
'SQL_FIELD': {},
|
|
|
'SQL_COLTYPE': {},
|
|
|
'SQL_KEYWORD': {},
|
|
|
'SQL_TABLE': {},
|
|
|
'HTTP_INFO': {},
|
|
|
'HTTP_SUCCESS': {},
|
|
|
'HTTP_REDIRECT': {},
|
|
|
'HTTP_NOT_MODIFIED': {},
|
|
|
'HTTP_BAD_REQUEST': {},
|
|
|
'HTTP_NOT_FOUND': {},
|
|
|
'HTTP_SERVER_ERROR': {},
|
|
|
},
|
|
|
DARK_PALETTE: {
|
|
|
'ERROR': { 'fg': 'red', 'opts': ('bold',) },
|
|
|
'NOTICE': { 'fg': 'red' },
|
|
|
'SQL_FIELD': { 'fg': 'green', 'opts': ('bold',) },
|
|
|
'SQL_COLTYPE': { 'fg': 'green' },
|
|
|
'SQL_KEYWORD': { 'fg': 'yellow' },
|
|
|
'SQL_TABLE': { 'opts': ('bold',) },
|
|
|
'HTTP_INFO': { 'opts': ('bold',) },
|
|
|
'HTTP_SUCCESS': { },
|
|
|
'HTTP_REDIRECT': { 'fg': 'green' },
|
|
|
'HTTP_NOT_MODIFIED': { 'fg': 'cyan' },
|
|
|
'HTTP_BAD_REQUEST': { 'fg': 'red', 'opts': ('bold',) },
|
|
|
'HTTP_NOT_FOUND': { 'fg': 'yellow' },
|
|
|
'HTTP_SERVER_ERROR': { 'fg': 'magenta', 'opts': ('bold',) },
|
|
|
},
|
|
|
LIGHT_PALETTE: {
|
|
|
'ERROR': { 'fg': 'red', 'opts': ('bold',) },
|
|
|
'NOTICE': { 'fg': 'red' },
|
|
|
'SQL_FIELD': { 'fg': 'green', 'opts': ('bold',) },
|
|
|
'SQL_COLTYPE': { 'fg': 'green' },
|
|
|
'SQL_KEYWORD': { 'fg': 'blue' },
|
|
|
'SQL_TABLE': { 'opts': ('bold',) },
|
|
|
'HTTP_INFO': { 'opts': ('bold',) },
|
|
|
'HTTP_SUCCESS': { },
|
|
|
'HTTP_REDIRECT': { 'fg': 'green', 'opts': ('bold',) },
|
|
|
'HTTP_NOT_MODIFIED': { 'fg': 'green' },
|
|
|
'HTTP_BAD_REQUEST': { 'fg': 'red', 'opts': ('bold',) },
|
|
|
'HTTP_NOT_FOUND': { 'fg': 'red' },
|
|
|
'HTTP_SERVER_ERROR': { 'fg': 'magenta', 'opts': ('bold',) },
|
|
|
}
|
|
|
}
|
|
|
DEFAULT_PALETTE = DARK_PALETTE
|
|
|
|
|
|
# ---------------------------- #
|
|
|
# --- End of termcolors.py --- #
|
|
|
# ---------------------------- #
|
|
|
|
|
|
|
|
|
class ColoredProgressBar(ProgressBar):
|
|
|
|
|
|
BAR_COLORS = (
|
|
|
(10, 'red'),
|
|
|
(30, 'magenta'),
|
|
|
(50, 'yellow'),
|
|
|
(99, 'green'),
|
|
|
(100, 'blue'),
|
|
|
)
|
|
|
|
|
|
def get_line(self):
|
|
|
line = super(ColoredProgressBar, self).get_line()
|
|
|
perc = self.get_percentage()
|
|
|
if perc > 100:
|
|
|
color = 'blue'
|
|
|
for max_perc, color in self.BAR_COLORS:
|
|
|
if perc <= max_perc:
|
|
|
break
|
|
|
return colorize(line, fg=color)
|
|
|
|
|
|
|
|
|
class AnimatedProgressBar(ProgressBar):
|
|
|
|
|
|
def get_bar_char(self):
|
|
|
chars = '-/|\\'
|
|
|
if self.step >= self.steps:
|
|
|
return '='
|
|
|
return chars[self.step % len(chars)]
|
|
|
|
|
|
|
|
|
class BarOnlyProgressBar(ProgressBar):
|
|
|
|
|
|
default_elements = ['bar', 'steps']
|
|
|
|
|
|
def get_bar(self):
|
|
|
bar = super(BarOnlyProgressBar, self).get_bar()
|
|
|
perc = self.get_percentage()
|
|
|
perc_text = '%s%%' % int(perc)
|
|
|
text = (' %s%% ' % (perc_text)).center(self.width, '=')
|
|
|
L = text.find(' ')
|
|
|
R = text.rfind(' ')
|
|
|
bar = ' '.join((bar[:L], perc_text, bar[R:]))
|
|
|
return bar
|
|
|
|
|
|
|
|
|
class AnimatedColoredProgressBar(AnimatedProgressBar,
|
|
|
ColoredProgressBar):
|
|
|
pass
|
|
|
|
|
|
|
|
|
class BarOnlyColoredProgressBar(ColoredProgressBar,
|
|
|
BarOnlyProgressBar):
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
import time
|
|
|
|
|
|
print "Standard progress bar..."
|
|
|
bar = ProgressBar(30)
|
|
|
for x in xrange(1, 31):
|
|
|
bar.render(x)
|
|
|
time.sleep(0.02)
|
|
|
bar.stream.write('\n')
|
|
|
print
|
|
|
|
|
|
print "Empty bar..."
|
|
|
bar = ProgressBar(50)
|
|
|
bar.render(0)
|
|
|
print
|
|
|
print
|
|
|
|
|
|
print "Colored bar..."
|
|
|
bar = ColoredProgressBar(20)
|
|
|
for x in bar:
|
|
|
time.sleep(0.01)
|
|
|
print
|
|
|
|
|
|
print "Animated char bar..."
|
|
|
bar = AnimatedProgressBar(20)
|
|
|
for x in bar:
|
|
|
time.sleep(0.01)
|
|
|
print
|
|
|
|
|
|
print "Animated + colored char bar..."
|
|
|
bar = AnimatedColoredProgressBar(20)
|
|
|
for x in bar:
|
|
|
time.sleep(0.01)
|
|
|
print
|
|
|
|
|
|
print "Bar only ..."
|
|
|
bar = BarOnlyProgressBar(20)
|
|
|
for x in bar:
|
|
|
time.sleep(0.01)
|
|
|
print
|
|
|
|
|
|
print "Colored, longer bar-only, eta, total time ..."
|
|
|
bar = BarOnlyColoredProgressBar(40)
|
|
|
bar.width = 60
|
|
|
bar.elements += ['time', 'eta']
|
|
|
for x in bar:
|
|
|
time.sleep(0.01)
|
|
|
print
|
|
|
print
|
|
|
|
|
|
print "File transfer bar, breaks after 2 seconds ..."
|
|
|
total_bytes = 1024 * 1024 * 2
|
|
|
bar = ProgressBar(total_bytes)
|
|
|
bar.width = 50
|
|
|
bar.elements.remove('steps')
|
|
|
bar.elements += ['transfer', 'time', 'eta', 'speed']
|
|
|
for x in xrange(0, bar.steps, 1024):
|
|
|
bar.render(x)
|
|
|
time.sleep(0.01)
|
|
|
now = datetime.datetime.now()
|
|
|
if now - bar.started >= datetime.timedelta(seconds=2):
|
|
|
break
|
|
|
print
|
|
|
print
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
main()
|
|
|
|