##// END OF EJS Templates
bookmarks: rename bookmarkcurrent to activebookmark (API)...
bookmarks: rename bookmarkcurrent to activebookmark (API) Today, the terms 'active' and 'current' are interchangeably used throughout the codebase in reference to the active bookmark (the bookmark that will be updated with the next commit). This leads to confusion among developers and users. This patch is part of a series to standardize the usage to 'active' throughout the mercurial codebase and user interface.

File last commit:

r23908:5502bd79 default
r24947:a02d293a default
Show More
progress.py
320 lines | 11.6 KiB | text/x-python | PythonLexer
Augie Fackler
Progress bar extension
r10434 # progress.py show progress bars for some actions
#
# Copyright (C) 2010 Augie Fackler <durin42@gmail.com>
#
Augie Fackler
progress: Use the same GPL boilerplate as most hg files
r15772 # This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
Augie Fackler
Progress bar extension
r10434
"""show progress bars for some actions
timeless
progress: fix description
r10450 This extension uses the progress information logged by hg commands
to draw progress bars that are as informative as possible. Some progress
Augie Fackler
Progress bar extension
r10434 bars only offer indeterminate information, while others have a definite
end point.
The following settings are available::
[progress]
delay = 3 # number of seconds (float) before showing the progress bar
Augie Fackler
progress: add a changedelay to prevent parallel topics from flapping (issue2698)...
r14838 changedelay = 1 # changedelay: minimum delay before showing a new topic.
# If set to less than 3 * refresh, that value will
# be used instead.
Augie Fackler
Progress bar extension
r10434 refresh = 0.1 # time in seconds between refreshes of the progress bar
Augie Fackler
progress: include time estimate as part of the default progress format
r13148 format = topic bar number estimate # format of the progress bar
Augie Fackler
Progress bar extension
r10434 width = <none> # if set, the maximum width of the progress information
# (that is, min(width, term width) will be used)
clear-complete = True # clear the progress bar after it's done
Augie Fackler
progress: document progress.disable config option
r10656 disable = False # if true, don't show a progress bar
Augie Fackler
progress: use stderr instead of stdout; check stderr.isatty()...
r10788 assume-tty = False # if true, ALWAYS show a progress bar, unless
# disable is given
Augie Fackler
Progress bar extension
r10434
Augie Fackler
progress: only show time estimate when progress format contains 'estimate'
r13147 Valid entries for the format field are topic, bar, number, unit,
Martin Geisler
progress: add speed format...
r14280 estimate, speed, and item. item defaults to the last 20 characters of
the item, but this can be changed by adding either ``-<num>`` which
would take the last num characters, or ``+<num>`` for the first num
Augie Fackler
progress: only show time estimate when progress format contains 'estimate'
r13147 characters.
Augie Fackler
Progress bar extension
r10434 """
import sys
import time
Solomon Matthews
progress: add a lock to prepare for introducing a thread
r23908 import threading
Augie Fackler
Progress bar extension
r10434
timeless
progress: Add estimated time remaining for long tasks...
r13131 from mercurial.i18n import _
Augie Fackler
hgext: mark all first-party extensions as such
r16743 testedwith = 'internal'
Augie Fackler
Progress bar extension
r10434
FUJIWARA Katsunori
progress: use 'encoding.trim' to trim output line correctly...
r21859 from mercurial import encoding
Augie Fackler
Progress bar extension
r10434 def spacejoin(*args):
Brodie Rao
progress: simplify spacejoin()
r10452 return ' '.join(s for s in args if s)
Augie Fackler
Progress bar extension
r10434
Augie Fackler
progress: check stderr.isatty() before each print...
r11458 def shouldprint(ui):
Matt Mackall
progress: respect HGPLAIN
r19404 return not ui.plain() and (ui._isatty(sys.stderr) or
ui.configbool('progress', 'assume-tty'))
Augie Fackler
progress: check stderr.isatty() before each print...
r11458
Augie Fackler
progress: refactor for readability and show XXs instead of 0mXXs.
r13132 def fmtremaining(seconds):
if seconds < 60:
Martin Geisler
progress: explain format strings to translators
r13139 # i18n: format XX seconds as "XXs"
Augie Fackler
progress: refactor for readability and show XXs instead of 0mXXs.
r13132 return _("%02ds") % (seconds)
minutes = seconds // 60
if minutes < 60:
seconds -= minutes * 60
Martin Geisler
progress: explain format strings to translators
r13139 # i18n: format X minutes and YY seconds as "XmYYs"
Augie Fackler
progress: refactor for readability and show XXs instead of 0mXXs.
r13132 return _("%dm%02ds") % (minutes, seconds)
# we're going to ignore seconds in this case
minutes += 1
hours = minutes // 60
minutes -= hours * 60
timeless
progress: handle days, weeks and years...
r13236 if hours < 30:
# i18n: format X hours and YY minutes as "XhYYm"
return _("%dh%02dm") % (hours, minutes)
# we're going to ignore minutes in this case
hours += 1
days = hours // 24
hours -= days * 24
if days < 15:
# i18n: format X days and YY hours as "XdYYh"
return _("%dd%02dh") % (days, hours)
# we're going to ignore hours in this case
days += 1
weeks = days // 7
days -= weeks * 7
if weeks < 55:
# i18n: format X weeks and YY days as "XwYYd"
return _("%dw%02dd") % (weeks, days)
# we're going to ignore days and treat a year as 52 weeks
weeks += 1
years = weeks // 52
weeks -= years * 52
# i18n: format X years and YY weeks as "XyYYw"
return _("%dy%02dw") % (years, weeks)
Augie Fackler
progress: refactor for readability and show XXs instead of 0mXXs.
r13132
Augie Fackler
Progress bar extension
r10434 class progbar(object):
def __init__(self, ui):
self.ui = ui
Solomon Matthews
progress: add a lock to prepare for introducing a thread
r23908 self._refreshlock = threading.Lock()
Augie Fackler
Progress bar extension
r10434 self.resetstate()
def resetstate(self):
self.topics = []
Augie Fackler
progress: react more reasonably to nested progress topics...
r13130 self.topicstates = {}
timeless
progress: Add estimated time remaining for long tasks...
r13131 self.starttimes = {}
self.startvals = {}
Augie Fackler
Progress bar extension
r10434 self.printed = False
self.lastprint = time.time() + float(self.ui.config(
'progress', 'delay', default=3))
Solomon Matthews
progress: move current topic to member variable
r23906 self.curtopic = None
Augie Fackler
progress: add a changedelay to prevent parallel topics from flapping (issue2698)...
r14838 self.lasttopic = None
Augie Fackler
Progress bar extension
r10434 self.indetcount = 0
self.refresh = float(self.ui.config(
'progress', 'refresh', default=0.1))
Augie Fackler
progress: add a changedelay to prevent parallel topics from flapping (issue2698)...
r14838 self.changedelay = max(3 * self.refresh,
float(self.ui.config(
'progress', 'changedelay', default=1)))
Augie Fackler
Progress bar extension
r10434 self.order = self.ui.configlist(
'progress', 'format',
Augie Fackler
progress: include time estimate as part of the default progress format
r13148 default=['topic', 'bar', 'number', 'estimate'])
Augie Fackler
Progress bar extension
r10434
timeless
progress: Add estimated time remaining for long tasks...
r13131 def show(self, now, topic, pos, item, unit, total):
Augie Fackler
progress: check stderr.isatty() before each print...
r11458 if not shouldprint(self.ui):
return
Augie Fackler
Progress bar extension
r10434 termwidth = self.width()
self.printed = True
head = ''
needprogress = False
tail = ''
for indicator in self.order:
add = ''
if indicator == 'topic':
add = topic
elif indicator == 'number':
if total:
add = ('% ' + str(len(str(total))) +
's/%s') % (pos, total)
else:
add = str(pos)
elif indicator.startswith('item') and item:
slice = 'end'
if '-' in indicator:
wid = int(indicator.split('-')[1])
elif '+' in indicator:
slice = 'beginning'
wid = int(indicator.split('+')[1])
else:
wid = 20
if slice == 'end':
FUJIWARA Katsunori
progress: use 'encoding.trim' to trim items in output line correctly...
r21862 add = encoding.trim(item, wid, leftside=True)
Augie Fackler
Progress bar extension
r10434 else:
FUJIWARA Katsunori
progress: use 'encoding.trim' to trim items in output line correctly...
r21862 add = encoding.trim(item, wid)
FUJIWARA Katsunori
progress: use 'encoding.colwidth' to get column width of items correctly...
r21863 add += (wid - encoding.colwidth(add)) * ' '
Augie Fackler
Progress bar extension
r10434 elif indicator == 'bar':
add = ''
needprogress = True
elif indicator == 'unit' and unit:
add = unit
Augie Fackler
progress: only show time estimate when progress format contains 'estimate'
r13147 elif indicator == 'estimate':
add = self.estimate(topic, pos, total, now)
Martin Geisler
progress: add speed format...
r14280 elif indicator == 'speed':
add = self.speed(topic, pos, unit, now)
Augie Fackler
Progress bar extension
r10434 if not needprogress:
head = spacejoin(head, add)
else:
Augie Fackler
progress: fix adding format elements after the progress bar...
r13146 tail = spacejoin(tail, add)
Augie Fackler
Progress bar extension
r10434 if needprogress:
used = 0
if head:
FUJIWARA Katsunori
progress: use 'encoding.colwidth' to get column width of output line correctly...
r21860 used += encoding.colwidth(head) + 1
Augie Fackler
Progress bar extension
r10434 if tail:
FUJIWARA Katsunori
progress: use 'encoding.colwidth' to get column width of output line correctly...
r21860 used += encoding.colwidth(tail) + 1
Augie Fackler
Progress bar extension
r10434 progwidth = termwidth - used - 3
Augie Fackler
progress: fall back to indeterminate progress if position is >= total
r10891 if total and pos <= total:
Augie Fackler
Progress bar extension
r10434 amt = pos * progwidth // total
Brodie Rao
progress: make determinate bar more like wget progress bar...
r10453 bar = '=' * (amt - 1)
if amt > 0:
bar += '>'
bar += ' ' * (progwidth - amt)
Augie Fackler
Progress bar extension
r10434 else:
progwidth -= 3
self.indetcount += 1
# mod the count by twice the width so we can make the
# cursor bounce between the right and left sides
amt = self.indetcount % (2 * progwidth)
amt -= progwidth
bar = (' ' * int(progwidth - abs(amt)) + '<=>' +
' ' * int(abs(amt)))
prog = ''.join(('[', bar , ']'))
out = spacejoin(head, prog, tail)
else:
out = spacejoin(head, tail)
FUJIWARA Katsunori
progress: use 'encoding.trim' to trim output line correctly...
r21859 sys.stderr.write('\r' + encoding.trim(out, termwidth))
Augie Fackler
progress: add a changedelay to prevent parallel topics from flapping (issue2698)...
r14838 self.lasttopic = topic
Augie Fackler
progress: use stderr instead of stdout; check stderr.isatty()...
r10788 sys.stderr.flush()
Augie Fackler
Progress bar extension
r10434
def clear(self):
Augie Fackler
progress: check stderr.isatty() before each print...
r11458 if not shouldprint(self.ui):
return
Augie Fackler
progress: use stderr instead of stdout; check stderr.isatty()...
r10788 sys.stderr.write('\r%s\r' % (' ' * self.width()))
Augie Fackler
Progress bar extension
r10434
Benoit Boissinot
progress: correctly handle empty progress topic
r10439 def complete(self):
Augie Fackler
progress: check stderr.isatty() before each print...
r11458 if not shouldprint(self.ui):
return
Benoit Boissinot
progress: correctly handle empty progress topic
r10439 if self.ui.configbool('progress', 'clear-complete', default=True):
self.clear()
else:
Augie Fackler
progress: use stderr instead of stdout; check stderr.isatty()...
r10788 sys.stderr.write('\n')
sys.stderr.flush()
Benoit Boissinot
progress: correctly handle empty progress topic
r10439
Augie Fackler
Progress bar extension
r10434 def width(self):
Augie Fackler
termwidth: move to ui.ui from util
r12689 tw = self.ui.termwidth()
Augie Fackler
Progress bar extension
r10434 return min(int(self.ui.config('progress', 'width', default=tw)), tw)
Augie Fackler
progress: only show time estimate when progress format contains 'estimate'
r13147 def estimate(self, topic, pos, total, now):
Augie Fackler
progress: don't compute estimate without a total...
r13154 if total is None:
return ''
Augie Fackler
progress: only show time estimate when progress format contains 'estimate'
r13147 initialpos = self.startvals[topic]
target = total - initialpos
delta = pos - initialpos
if delta > 0:
elapsed = now - self.starttimes[topic]
if elapsed > float(
self.ui.config('progress', 'estimate', default=2)):
seconds = (elapsed * (target - delta)) // delta + 1
return fmtremaining(seconds)
return ''
Martin Geisler
progress: add speed format...
r14280 def speed(self, topic, pos, unit, now):
initialpos = self.startvals[topic]
delta = pos - initialpos
elapsed = now - self.starttimes[topic]
if elapsed > float(
self.ui.config('progress', 'estimate', default=2)):
return _('%d %s/sec') % (delta / elapsed, unit)
return ''
Solomon Matthews
progress: move update check into helper method
r23907 def _oktoprint(self, now):
'''Check if conditions are met to print - e.g. changedelay elapsed'''
if (self.lasttopic is None # first time we printed
# not a topic change
or self.curtopic == self.lasttopic
# it's been long enough we should print anyway
or now - self.lastprint >= self.changedelay):
return True
else:
return False
Brodie Rao
color/progress: subclass ui instead of using wrapfunction (issue2096)...
r11555 def progress(self, topic, pos, item='', unit='', total=None):
timeless
progress: Add estimated time remaining for long tasks...
r13131 now = time.time()
Solomon Matthews
progress: add a lock to prepare for introducing a thread
r23908 self._refreshlock.acquire()
Solomon Matthews
progress: add try/finally to make the diffs for the next commit more readable...
r23905 try:
if pos is None:
self.starttimes.pop(topic, None)
self.startvals.pop(topic, None)
self.topicstates.pop(topic, None)
# reset the progress bar if this is the outermost topic
if self.topics and self.topics[0] == topic and self.printed:
self.complete()
self.resetstate()
# truncate the list of topics assuming all topics within
# this one are also closed
if topic in self.topics:
self.topics = self.topics[:self.topics.index(topic)]
# reset the last topic to the one we just unwound to,
# so that higher-level topics will be stickier than
# lower-level topics
if self.topics:
self.lasttopic = self.topics[-1]
else:
self.lasttopic = None
else:
if topic not in self.topics:
self.starttimes[topic] = now
self.startvals[topic] = pos
self.topics.append(topic)
self.topicstates[topic] = pos, item, unit, total
Solomon Matthews
progress: move current topic to member variable
r23906 self.curtopic = topic
Solomon Matthews
progress: add try/finally to make the diffs for the next commit more readable...
r23905 if now - self.lastprint >= self.refresh and self.topics:
Solomon Matthews
progress: move update check into helper method
r23907 if self._oktoprint(now):
Solomon Matthews
progress: add try/finally to make the diffs for the next commit more readable...
r23905 self.lastprint = now
self.show(now, topic, *self.topicstates[topic])
finally:
Solomon Matthews
progress: add a lock to prepare for introducing a thread
r23908 self._refreshlock.release()
Augie Fackler
Progress bar extension
r10434
Augie Fackler
progress: make progress bar a singleton to avoid double-progress ui bugs...
r14837 _singleton = None
Augie Fackler
Progress bar extension
r10434 def uisetup(ui):
Augie Fackler
progress: make progress bar a singleton to avoid double-progress ui bugs...
r14837 global _singleton
Brodie Rao
color/progress: subclass ui instead of using wrapfunction (issue2096)...
r11555 class progressui(ui.__class__):
_progbar = None
David Soria Parra
progress: check for ui.quiet and ui.debugflag before we write...
r15662 def _quiet(self):
return self.debugflag or self.quiet
Brodie Rao
color/progress: subclass ui instead of using wrapfunction (issue2096)...
r11555 def progress(self, *args, **opts):
David Soria Parra
progress: check for ui.quiet and ui.debugflag before we write...
r15662 if not self._quiet():
self._progbar.progress(*args, **opts)
Brodie Rao
color/progress: subclass ui instead of using wrapfunction (issue2096)...
r11555 return super(progressui, self).progress(*args, **opts)
def write(self, *args, **opts):
David Soria Parra
progress: check for ui.quiet and ui.debugflag before we write...
r15662 if not self._quiet() and self._progbar.printed:
Brodie Rao
color/progress: subclass ui instead of using wrapfunction (issue2096)...
r11555 self._progbar.clear()
return super(progressui, self).write(*args, **opts)
def write_err(self, *args, **opts):
David Soria Parra
progress: check for ui.quiet and ui.debugflag before we write...
r15662 if not self._quiet() and self._progbar.printed:
Brodie Rao
color/progress: subclass ui instead of using wrapfunction (issue2096)...
r11555 self._progbar.clear()
return super(progressui, self).write_err(*args, **opts)
Steve Borho
progress: provide an explicit disable method for developers...
r10540 # Apps that derive a class from ui.ui() can use
# setconfig('progress', 'disable', 'True') to disable this extension
if ui.configbool('progress', 'disable'):
return
Augie Fackler
progress: check stderr.isatty() before each print...
r11458 if shouldprint(ui) and not ui.debugflag and not ui.quiet:
Brodie Rao
color/progress: subclass ui instead of using wrapfunction (issue2096)...
r11555 ui.__class__ = progressui
Augie Fackler
Progress bar extension
r10434 # we instantiate one globally shared progress bar to avoid
# competing progress bars when multiple UI objects get created
Brodie Rao
color/progress: subclass ui instead of using wrapfunction (issue2096)...
r11555 if not progressui._progbar:
Augie Fackler
progress: make progress bar a singleton to avoid double-progress ui bugs...
r14837 if _singleton is None:
_singleton = progbar(ui)
progressui._progbar = _singleton
Augie Fackler
Progress bar extension
r10434
def reposetup(ui, repo):
uisetup(repo.ui)