##// END OF EJS Templates
rev-branch-cache: increment the version to "v2"...
rev-branch-cache: increment the version to "v2" We want to ensure no older clients will truncate the file under us. So we need to change their name. We don't change the rest of the format (unfortunaly).

File last commit:

r52756:f4733654 default
r52799:0f26ee69 default
Show More
progress.py
296 lines | 10.2 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.
Matt Harbison
typing: add `from __future__ import annotations` to most files...
r52756 from __future__ import annotations
Gregory Szorc
progress: use absolute_import
r25968
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
Gregory Szorc
py3: use class X: instead of class X(object):...
r49801 class progbar:
Pierre-Yves David
progress: move most extension code into a 'mercurial.progress' module...
r25497 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):
Manuel Jacob
py3: remove retry on EINTR errno...
r50197 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):
Manuel Jacob
py3: remove retry on EINTR errno...
r50197 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):
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 """Adjust starttimes and startvals for topic so ETA works better
Jun Wu
progress: make ETA only consider progress made in the last minute...
r34315
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
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 """
Jun Wu
progress: make ETA only consider progress made in the last minute...
r34315 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