##// END OF EJS Templates
py3: fix curses chunkselector fallback (when diffs are too large) on py3...
py3: fix curses chunkselector fallback (when diffs are too large) on py3 Previously we showed the message using Exception.message, which is removed in py3. Since crecordmod.fallbackerror inherits from error.Abort, we can just use `b'%s' % exception` to print the message. This does not print the hint, but that's fine - we don't set one. We inherit from error.Abort so that if a codepath doesn't handle fallback specially, it exits to the terminal with a sane message instead of an unknown exception error. Differential Revision: https://phab.mercurial-scm.org/D7912

File last commit:

r44485:4e0a6d15 stable
r44557:19533e4c default
Show More
progress.py
315 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()))
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