##// END OF EJS Templates
rust-status: add missing variants to `Dispatch` enum...
rust-status: add missing variants to `Dispatch` enum Differential Revision: https://phab.mercurial-scm.org/D8088

File last commit:

r44485:4e0a6d15 stable
r45013:61709b84 default
Show More
progress.py
316 lines | 10.9 KiB | text/x-python | PythonLexer
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497 # progress.py progress bars related code
#
# Copyright (C) 2010 Augie Fackler <durin42@gmail.com>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
Gregory Szorc
progress: use absolute_import
r25968 from __future__ import absolute_import
Yuya Nishihara
progress: retry ferr.flush() and .write() on EINTR (issue5532)...
r32049 import errno
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497 import threading
Gregory Szorc
progress: use absolute_import
r25968 import time
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497
Gregory Szorc
progress: use absolute_import
r25968 from .i18n import _
from . import encoding
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497
Augie Fackler
formatting: blacken the codebase...
r43346
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497 def spacejoin(*args):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return b' '.join(s for s in args if s)
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497
Augie Fackler
formatting: blacken the codebase...
r43346
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497 def shouldprint(ui):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return not (ui.quiet or ui.plain(b'progress')) and (
ui._isatty(ui.ferr) or ui.configbool(b'progress', b'assume-tty')
Augie Fackler
formatting: blacken the codebase...
r43346 )
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497
def fmtremaining(seconds):
Mads Kiilerich
spelling: trivial spell checking
r26781 """format a number of remaining seconds in human readable way
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497
This will properly display seconds, minutes, hours, days if needed"""
if seconds < 60:
# i18n: format XX seconds as "XXs"
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return _(b"%02ds") % seconds
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497 minutes = seconds // 60
if minutes < 60:
seconds -= minutes * 60
# i18n: format X minutes and YY seconds as "XmYYs"
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return _(b"%dm%02ds") % (minutes, seconds)
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497 # we're going to ignore seconds in this case
minutes += 1
hours = minutes // 60
minutes -= hours * 60
if hours < 30:
# i18n: format X hours and YY minutes as "XhYYm"
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return _(b"%dh%02dm") % (hours, minutes)
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497 # 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"
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return _(b"%dd%02dh") % (days, hours)
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497 # 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"
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return _(b"%dw%02dd") % (weeks, days)
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497 # 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"
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return _(b"%dy%02dw") % (years, weeks)
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
progress: retry ferr.flush() and .write() on EINTR (issue5532)...
r32049 # file_write() and file_flush() of Python 2 do not restart on EINTR if
# the file is attached to a "slow" device (e.g. a terminal) and raise
# IOError. We cannot know how many bytes would be written by file_write(),
# but a progress text is known to be short enough to be written by a
# single write() syscall, so we can just retry file_write() with the whole
# text. (issue5532)
#
# This should be a short-term workaround. We'll need to fix every occurrence
# of write() to a terminal or pipe.
def _eintrretry(func, *args):
while True:
try:
return func(*args)
except IOError as err:
if err.errno == errno.EINTR:
continue
raise
Augie Fackler
formatting: blacken the codebase...
r43346
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497 class progbar(object):
def __init__(self, ui):
self.ui = ui
self._refreshlock = threading.Lock()
self.resetstate()
def resetstate(self):
self.topics = []
self.topicstates = {}
self.starttimes = {}
self.startvals = {}
self.printed = False
Augie Fackler
formatting: blacken the codebase...
r43346 self.lastprint = time.time() + float(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self.ui.config(b'progress', b'delay')
Augie Fackler
formatting: blacken the codebase...
r43346 )
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497 self.curtopic = None
self.lasttopic = None
self.indetcount = 0
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self.refresh = float(self.ui.config(b'progress', b'refresh'))
Augie Fackler
formatting: blacken the codebase...
r43346 self.changedelay = max(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 3 * self.refresh, float(self.ui.config(b'progress', b'changedelay'))
Augie Fackler
formatting: blacken the codebase...
r43346 )
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self.order = self.ui.configlist(b'progress', b'format')
Jun Wu
progress: make ETA only consider progress made in the last minute...
r34315 self.estimateinterval = self.ui.configwith(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 float, b'progress', b'estimateinterval'
Augie Fackler
formatting: blacken the codebase...
r43346 )
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497
def show(self, now, topic, pos, item, unit, total):
if not shouldprint(self.ui):
return
termwidth = self.width()
self.printed = True
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 head = b''
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497 needprogress = False
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 tail = b''
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497 for indicator in self.order:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 add = b''
if indicator == b'topic':
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497 add = topic
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif indicator == b'number':
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497 if total:
Yuya Nishihara
progress: use '%*d' to pad progress value...
r36220 add = b'%*d/%d' % (len(str(total)), pos, total)
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497 else:
Augie Fackler
py3: convert known-int values to bytes using %d...
r36441 add = b'%d' % pos
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif indicator.startswith(b'item') and item:
slice = b'end'
if b'-' in indicator:
wid = int(indicator.split(b'-')[1])
elif b'+' in indicator:
slice = b'beginning'
wid = int(indicator.split(b'+')[1])
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497 else:
wid = 20
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if slice == b'end':
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497 add = encoding.trim(item, wid, leftside=True)
else:
add = encoding.trim(item, wid)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 add += (wid - encoding.colwidth(add)) * b' '
elif indicator == b'bar':
add = b''
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497 needprogress = True
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif indicator == b'unit' and unit:
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497 add = unit
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif indicator == b'estimate':
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497 add = self.estimate(topic, pos, total, now)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif indicator == b'speed':
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497 add = self.speed(topic, pos, unit, now)
if not needprogress:
head = spacejoin(head, add)
else:
tail = spacejoin(tail, add)
if needprogress:
used = 0
if head:
used += encoding.colwidth(head) + 1
if tail:
used += encoding.colwidth(tail) + 1
progwidth = termwidth - used - 3
if total and pos <= total:
amt = pos * progwidth // total
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 bar = b'=' * (amt - 1)
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497 if amt > 0:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 bar += b'>'
bar += b' ' * (progwidth - amt)
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497 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
Augie Fackler
formatting: blacken the codebase...
r43346 bar = (
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b' ' * int(progwidth - abs(amt))
+ b'<=>'
+ b' ' * int(abs(amt))
Augie Fackler
formatting: blacken the codebase...
r43346 )
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 prog = b''.join((b'[', bar, b']'))
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497 out = spacejoin(head, prog, tail)
else:
out = spacejoin(head, tail)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self._writeerr(b'\r' + encoding.trim(out, termwidth))
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497 self.lasttopic = topic
Yuya Nishihara
progress: extract stubs to restart ferr.flush() and .write() on EINTR
r32048 self._flusherr()
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497
def clear(self):
Matt Mackall
progress: stop excessive clearing (issue4801)...
r29089 if not self.printed or not self.lastprint or not shouldprint(self.ui):
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497 return
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self._writeerr(b'\r%s\r' % (b' ' * self.width()))
Kyle Lippincott
progress: flush stderr after clearing...
r44485 self._flusherr()
Augie Fackler
progress: force a repaint of a printed progress bar after a clear()...
r26407 if self.printed:
# force immediate re-paint of progress bar
self.lastprint = 0
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497
def complete(self):
if not shouldprint(self.ui):
return
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if self.ui.configbool(b'progress', b'clear-complete'):
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497 self.clear()
else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self._writeerr(b'\n')
Yuya Nishihara
progress: extract stubs to restart ferr.flush() and .write() on EINTR
r32048 self._flusherr()
def _flusherr(self):
Yuya Nishihara
progress: retry ferr.flush() and .write() on EINTR (issue5532)...
r32049 _eintrretry(self.ui.ferr.flush)
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497
Yuya Nishihara
progress: extract stubs to restart ferr.flush() and .write() on EINTR
r32048 def _writeerr(self, msg):
Yuya Nishihara
progress: retry ferr.flush() and .write() on EINTR (issue5532)...
r32049 _eintrretry(self.ui.ferr.write, msg)
Yuya Nishihara
progress: extract stubs to restart ferr.flush() and .write() on EINTR
r32048
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497 def width(self):
tw = self.ui.termwidth()
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return min(int(self.ui.config(b'progress', b'width', default=tw)), tw)
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497
def estimate(self, topic, pos, total, now):
if total is None:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return b''
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497 initialpos = self.startvals[topic]
target = total - initialpos
delta = pos - initialpos
if delta > 0:
elapsed = now - self.starttimes[topic]
Jun Wu
progress: remove progress.estimate config...
r34314 seconds = (elapsed * (target - delta)) // delta + 1
return fmtremaining(seconds)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return b''
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497
def speed(self, topic, pos, unit, now):
initialpos = self.startvals[topic]
delta = pos - initialpos
elapsed = now - self.starttimes[topic]
Jun Wu
progress: remove progress.estimate config...
r34314 if elapsed > 0:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return _(b'%d %s/sec') % (delta / elapsed, unit)
return b''
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497
def _oktoprint(self, now):
'''Check if conditions are met to print - e.g. changedelay elapsed'''
Augie Fackler
formatting: blacken the codebase...
r43346 if (
self.lasttopic is None # first time we printed
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497 # not a topic change
or self.curtopic == self.lasttopic
# it's been long enough we should print anyway
Augie Fackler
formatting: blacken the codebase...
r43346 or now - self.lastprint >= self.changedelay
):
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497 return True
else:
return False
Jun Wu
progress: make ETA only consider progress made in the last minute...
r34315 def _calibrateestimate(self, topic, now, pos):
'''Adjust starttimes and startvals for topic so ETA works better
If progress is non-linear (ex. get much slower in the last minute),
it's more friendly to only use a recent time span for ETA and speed
calculation.
[======================================> ]
^^^^^^^
estimateinterval, only use this for estimation
'''
interval = self.estimateinterval
if interval <= 0:
return
elapsed = now - self.starttimes[topic]
if elapsed > interval:
delta = pos - self.startvals[topic]
newdelta = delta * interval / elapsed
# If a stall happens temporarily, ETA could change dramatically
# frequently. This is to avoid such dramatical change and make ETA
# smoother.
if newdelta < 0.1:
return
self.startvals[topic] = pos - newdelta
self.starttimes[topic] = now - interval
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 def progress(self, topic, pos, item=b'', unit=b'', total=None):
Martin von Zweigbergk
progress: extract function for closing topic...
r38437 if pos is None:
self.closetopic(topic)
return
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497 now = time.time()
Martin von Zweigbergk
progress: use context manager for lock...
r38436 with self._refreshlock:
Martin von Zweigbergk
progress: extract function for closing topic...
r38437 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
self.curtopic = topic
self._calibrateestimate(topic, now, pos)
if now - self.lastprint >= self.refresh and self.topics:
if self._oktoprint(now):
self.lastprint = now
self.show(now, topic, *self.topicstates[topic])
def closetopic(self, topic):
with self._refreshlock:
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:
Augie Fackler
formatting: blacken the codebase...
r43346 self.topics = self.topics[: self.topics.index(topic)]
Martin von Zweigbergk
progress: extract function for closing topic...
r38437 # 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