mercurial/progress.py
changeset 43077 687b865b95ad
parent 43076 2372284d9457
child 44033 4e0a6d157910
equal deleted inserted replaced
43076:2372284d9457 43077:687b865b95ad
    14 from .i18n import _
    14 from .i18n import _
    15 from . import encoding
    15 from . import encoding
    16 
    16 
    17 
    17 
    18 def spacejoin(*args):
    18 def spacejoin(*args):
    19     return ' '.join(s for s in args if s)
    19     return b' '.join(s for s in args if s)
    20 
    20 
    21 
    21 
    22 def shouldprint(ui):
    22 def shouldprint(ui):
    23     return not (ui.quiet or ui.plain('progress')) and (
    23     return not (ui.quiet or ui.plain(b'progress')) and (
    24         ui._isatty(ui.ferr) or ui.configbool('progress', 'assume-tty')
    24         ui._isatty(ui.ferr) or ui.configbool(b'progress', b'assume-tty')
    25     )
    25     )
    26 
    26 
    27 
    27 
    28 def fmtremaining(seconds):
    28 def fmtremaining(seconds):
    29     """format a number of remaining seconds in human readable way
    29     """format a number of remaining seconds in human readable way
    30 
    30 
    31     This will properly display seconds, minutes, hours, days if needed"""
    31     This will properly display seconds, minutes, hours, days if needed"""
    32     if seconds < 60:
    32     if seconds < 60:
    33         # i18n: format XX seconds as "XXs"
    33         # i18n: format XX seconds as "XXs"
    34         return _("%02ds") % seconds
    34         return _(b"%02ds") % seconds
    35     minutes = seconds // 60
    35     minutes = seconds // 60
    36     if minutes < 60:
    36     if minutes < 60:
    37         seconds -= minutes * 60
    37         seconds -= minutes * 60
    38         # i18n: format X minutes and YY seconds as "XmYYs"
    38         # i18n: format X minutes and YY seconds as "XmYYs"
    39         return _("%dm%02ds") % (minutes, seconds)
    39         return _(b"%dm%02ds") % (minutes, seconds)
    40     # we're going to ignore seconds in this case
    40     # we're going to ignore seconds in this case
    41     minutes += 1
    41     minutes += 1
    42     hours = minutes // 60
    42     hours = minutes // 60
    43     minutes -= hours * 60
    43     minutes -= hours * 60
    44     if hours < 30:
    44     if hours < 30:
    45         # i18n: format X hours and YY minutes as "XhYYm"
    45         # i18n: format X hours and YY minutes as "XhYYm"
    46         return _("%dh%02dm") % (hours, minutes)
    46         return _(b"%dh%02dm") % (hours, minutes)
    47     # we're going to ignore minutes in this case
    47     # we're going to ignore minutes in this case
    48     hours += 1
    48     hours += 1
    49     days = hours // 24
    49     days = hours // 24
    50     hours -= days * 24
    50     hours -= days * 24
    51     if days < 15:
    51     if days < 15:
    52         # i18n: format X days and YY hours as "XdYYh"
    52         # i18n: format X days and YY hours as "XdYYh"
    53         return _("%dd%02dh") % (days, hours)
    53         return _(b"%dd%02dh") % (days, hours)
    54     # we're going to ignore hours in this case
    54     # we're going to ignore hours in this case
    55     days += 1
    55     days += 1
    56     weeks = days // 7
    56     weeks = days // 7
    57     days -= weeks * 7
    57     days -= weeks * 7
    58     if weeks < 55:
    58     if weeks < 55:
    59         # i18n: format X weeks and YY days as "XwYYd"
    59         # i18n: format X weeks and YY days as "XwYYd"
    60         return _("%dw%02dd") % (weeks, days)
    60         return _(b"%dw%02dd") % (weeks, days)
    61     # we're going to ignore days and treat a year as 52 weeks
    61     # we're going to ignore days and treat a year as 52 weeks
    62     weeks += 1
    62     weeks += 1
    63     years = weeks // 52
    63     years = weeks // 52
    64     weeks -= years * 52
    64     weeks -= years * 52
    65     # i18n: format X years and YY weeks as "XyYYw"
    65     # i18n: format X years and YY weeks as "XyYYw"
    66     return _("%dy%02dw") % (years, weeks)
    66     return _(b"%dy%02dw") % (years, weeks)
    67 
    67 
    68 
    68 
    69 # file_write() and file_flush() of Python 2 do not restart on EINTR if
    69 # file_write() and file_flush() of Python 2 do not restart on EINTR if
    70 # the file is attached to a "slow" device (e.g. a terminal) and raise
    70 # the file is attached to a "slow" device (e.g. a terminal) and raise
    71 # IOError. We cannot know how many bytes would be written by file_write(),
    71 # IOError. We cannot know how many bytes would be written by file_write(),
    96         self.topicstates = {}
    96         self.topicstates = {}
    97         self.starttimes = {}
    97         self.starttimes = {}
    98         self.startvals = {}
    98         self.startvals = {}
    99         self.printed = False
    99         self.printed = False
   100         self.lastprint = time.time() + float(
   100         self.lastprint = time.time() + float(
   101             self.ui.config('progress', 'delay')
   101             self.ui.config(b'progress', b'delay')
   102         )
   102         )
   103         self.curtopic = None
   103         self.curtopic = None
   104         self.lasttopic = None
   104         self.lasttopic = None
   105         self.indetcount = 0
   105         self.indetcount = 0
   106         self.refresh = float(self.ui.config('progress', 'refresh'))
   106         self.refresh = float(self.ui.config(b'progress', b'refresh'))
   107         self.changedelay = max(
   107         self.changedelay = max(
   108             3 * self.refresh, float(self.ui.config('progress', 'changedelay'))
   108             3 * self.refresh, float(self.ui.config(b'progress', b'changedelay'))
   109         )
   109         )
   110         self.order = self.ui.configlist('progress', 'format')
   110         self.order = self.ui.configlist(b'progress', b'format')
   111         self.estimateinterval = self.ui.configwith(
   111         self.estimateinterval = self.ui.configwith(
   112             float, 'progress', 'estimateinterval'
   112             float, b'progress', b'estimateinterval'
   113         )
   113         )
   114 
   114 
   115     def show(self, now, topic, pos, item, unit, total):
   115     def show(self, now, topic, pos, item, unit, total):
   116         if not shouldprint(self.ui):
   116         if not shouldprint(self.ui):
   117             return
   117             return
   118         termwidth = self.width()
   118         termwidth = self.width()
   119         self.printed = True
   119         self.printed = True
   120         head = ''
   120         head = b''
   121         needprogress = False
   121         needprogress = False
   122         tail = ''
   122         tail = b''
   123         for indicator in self.order:
   123         for indicator in self.order:
   124             add = ''
   124             add = b''
   125             if indicator == 'topic':
   125             if indicator == b'topic':
   126                 add = topic
   126                 add = topic
   127             elif indicator == 'number':
   127             elif indicator == b'number':
   128                 if total:
   128                 if total:
   129                     add = b'%*d/%d' % (len(str(total)), pos, total)
   129                     add = b'%*d/%d' % (len(str(total)), pos, total)
   130                 else:
   130                 else:
   131                     add = b'%d' % pos
   131                     add = b'%d' % pos
   132             elif indicator.startswith('item') and item:
   132             elif indicator.startswith(b'item') and item:
   133                 slice = 'end'
   133                 slice = b'end'
   134                 if '-' in indicator:
   134                 if b'-' in indicator:
   135                     wid = int(indicator.split('-')[1])
   135                     wid = int(indicator.split(b'-')[1])
   136                 elif '+' in indicator:
   136                 elif b'+' in indicator:
   137                     slice = 'beginning'
   137                     slice = b'beginning'
   138                     wid = int(indicator.split('+')[1])
   138                     wid = int(indicator.split(b'+')[1])
   139                 else:
   139                 else:
   140                     wid = 20
   140                     wid = 20
   141                 if slice == 'end':
   141                 if slice == b'end':
   142                     add = encoding.trim(item, wid, leftside=True)
   142                     add = encoding.trim(item, wid, leftside=True)
   143                 else:
   143                 else:
   144                     add = encoding.trim(item, wid)
   144                     add = encoding.trim(item, wid)
   145                 add += (wid - encoding.colwidth(add)) * ' '
   145                 add += (wid - encoding.colwidth(add)) * b' '
   146             elif indicator == 'bar':
   146             elif indicator == b'bar':
   147                 add = ''
   147                 add = b''
   148                 needprogress = True
   148                 needprogress = True
   149             elif indicator == 'unit' and unit:
   149             elif indicator == b'unit' and unit:
   150                 add = unit
   150                 add = unit
   151             elif indicator == 'estimate':
   151             elif indicator == b'estimate':
   152                 add = self.estimate(topic, pos, total, now)
   152                 add = self.estimate(topic, pos, total, now)
   153             elif indicator == 'speed':
   153             elif indicator == b'speed':
   154                 add = self.speed(topic, pos, unit, now)
   154                 add = self.speed(topic, pos, unit, now)
   155             if not needprogress:
   155             if not needprogress:
   156                 head = spacejoin(head, add)
   156                 head = spacejoin(head, add)
   157             else:
   157             else:
   158                 tail = spacejoin(tail, add)
   158                 tail = spacejoin(tail, add)
   163             if tail:
   163             if tail:
   164                 used += encoding.colwidth(tail) + 1
   164                 used += encoding.colwidth(tail) + 1
   165             progwidth = termwidth - used - 3
   165             progwidth = termwidth - used - 3
   166             if total and pos <= total:
   166             if total and pos <= total:
   167                 amt = pos * progwidth // total
   167                 amt = pos * progwidth // total
   168                 bar = '=' * (amt - 1)
   168                 bar = b'=' * (amt - 1)
   169                 if amt > 0:
   169                 if amt > 0:
   170                     bar += '>'
   170                     bar += b'>'
   171                 bar += ' ' * (progwidth - amt)
   171                 bar += b' ' * (progwidth - amt)
   172             else:
   172             else:
   173                 progwidth -= 3
   173                 progwidth -= 3
   174                 self.indetcount += 1
   174                 self.indetcount += 1
   175                 # mod the count by twice the width so we can make the
   175                 # mod the count by twice the width so we can make the
   176                 # cursor bounce between the right and left sides
   176                 # cursor bounce between the right and left sides
   177                 amt = self.indetcount % (2 * progwidth)
   177                 amt = self.indetcount % (2 * progwidth)
   178                 amt -= progwidth
   178                 amt -= progwidth
   179                 bar = (
   179                 bar = (
   180                     ' ' * int(progwidth - abs(amt))
   180                     b' ' * int(progwidth - abs(amt))
   181                     + '<=>'
   181                     + b'<=>'
   182                     + ' ' * int(abs(amt))
   182                     + b' ' * int(abs(amt))
   183                 )
   183                 )
   184             prog = ''.join(('[', bar, ']'))
   184             prog = b''.join((b'[', bar, b']'))
   185             out = spacejoin(head, prog, tail)
   185             out = spacejoin(head, prog, tail)
   186         else:
   186         else:
   187             out = spacejoin(head, tail)
   187             out = spacejoin(head, tail)
   188         self._writeerr('\r' + encoding.trim(out, termwidth))
   188         self._writeerr(b'\r' + encoding.trim(out, termwidth))
   189         self.lasttopic = topic
   189         self.lasttopic = topic
   190         self._flusherr()
   190         self._flusherr()
   191 
   191 
   192     def clear(self):
   192     def clear(self):
   193         if not self.printed or not self.lastprint or not shouldprint(self.ui):
   193         if not self.printed or not self.lastprint or not shouldprint(self.ui):
   194             return
   194             return
   195         self._writeerr('\r%s\r' % (' ' * self.width()))
   195         self._writeerr(b'\r%s\r' % (b' ' * self.width()))
   196         if self.printed:
   196         if self.printed:
   197             # force immediate re-paint of progress bar
   197             # force immediate re-paint of progress bar
   198             self.lastprint = 0
   198             self.lastprint = 0
   199 
   199 
   200     def complete(self):
   200     def complete(self):
   201         if not shouldprint(self.ui):
   201         if not shouldprint(self.ui):
   202             return
   202             return
   203         if self.ui.configbool('progress', 'clear-complete'):
   203         if self.ui.configbool(b'progress', b'clear-complete'):
   204             self.clear()
   204             self.clear()
   205         else:
   205         else:
   206             self._writeerr('\n')
   206             self._writeerr(b'\n')
   207         self._flusherr()
   207         self._flusherr()
   208 
   208 
   209     def _flusherr(self):
   209     def _flusherr(self):
   210         _eintrretry(self.ui.ferr.flush)
   210         _eintrretry(self.ui.ferr.flush)
   211 
   211 
   212     def _writeerr(self, msg):
   212     def _writeerr(self, msg):
   213         _eintrretry(self.ui.ferr.write, msg)
   213         _eintrretry(self.ui.ferr.write, msg)
   214 
   214 
   215     def width(self):
   215     def width(self):
   216         tw = self.ui.termwidth()
   216         tw = self.ui.termwidth()
   217         return min(int(self.ui.config('progress', 'width', default=tw)), tw)
   217         return min(int(self.ui.config(b'progress', b'width', default=tw)), tw)
   218 
   218 
   219     def estimate(self, topic, pos, total, now):
   219     def estimate(self, topic, pos, total, now):
   220         if total is None:
   220         if total is None:
   221             return ''
   221             return b''
   222         initialpos = self.startvals[topic]
   222         initialpos = self.startvals[topic]
   223         target = total - initialpos
   223         target = total - initialpos
   224         delta = pos - initialpos
   224         delta = pos - initialpos
   225         if delta > 0:
   225         if delta > 0:
   226             elapsed = now - self.starttimes[topic]
   226             elapsed = now - self.starttimes[topic]
   227             seconds = (elapsed * (target - delta)) // delta + 1
   227             seconds = (elapsed * (target - delta)) // delta + 1
   228             return fmtremaining(seconds)
   228             return fmtremaining(seconds)
   229         return ''
   229         return b''
   230 
   230 
   231     def speed(self, topic, pos, unit, now):
   231     def speed(self, topic, pos, unit, now):
   232         initialpos = self.startvals[topic]
   232         initialpos = self.startvals[topic]
   233         delta = pos - initialpos
   233         delta = pos - initialpos
   234         elapsed = now - self.starttimes[topic]
   234         elapsed = now - self.starttimes[topic]
   235         if elapsed > 0:
   235         if elapsed > 0:
   236             return _('%d %s/sec') % (delta / elapsed, unit)
   236             return _(b'%d %s/sec') % (delta / elapsed, unit)
   237         return ''
   237         return b''
   238 
   238 
   239     def _oktoprint(self, now):
   239     def _oktoprint(self, now):
   240         '''Check if conditions are met to print - e.g. changedelay elapsed'''
   240         '''Check if conditions are met to print - e.g. changedelay elapsed'''
   241         if (
   241         if (
   242             self.lasttopic is None  # first time we printed
   242             self.lasttopic is None  # first time we printed
   273             if newdelta < 0.1:
   273             if newdelta < 0.1:
   274                 return
   274                 return
   275             self.startvals[topic] = pos - newdelta
   275             self.startvals[topic] = pos - newdelta
   276             self.starttimes[topic] = now - interval
   276             self.starttimes[topic] = now - interval
   277 
   277 
   278     def progress(self, topic, pos, item='', unit='', total=None):
   278     def progress(self, topic, pos, item=b'', unit=b'', total=None):
   279         if pos is None:
   279         if pos is None:
   280             self.closetopic(topic)
   280             self.closetopic(topic)
   281             return
   281             return
   282         now = time.time()
   282         now = time.time()
   283         with self._refreshlock:
   283         with self._refreshlock: