progress: move most extension code into a 'mercurial.progress' module
authorPierre-Yves David <pierre-yves.david@fb.com>
Sun, 07 Jun 2015 17:19:20 -0700
changeset 25497 93b8b0049932
parent 25496 38fd17bcc083
child 25498 7a5335ed7e1a
progress: move most extension code into a 'mercurial.progress' module This initiate the relocation of progress into core.
hgext/progress.py
mercurial/progress.py
--- a/hgext/progress.py	Tue Jun 09 23:40:13 2015 -0400
+++ b/hgext/progress.py	Sun Jun 07 17:19:20 2015 -0700
@@ -35,252 +35,7 @@
 characters.
 """
 
-import sys
-import time
-import threading
-
-from mercurial.i18n import _
-# Note for extension authors: ONLY specify testedwith = 'internal' for
-# extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
-# be specifying the version(s) of Mercurial they are tested with, or
-# leave the attribute unspecified.
-testedwith = 'internal'
-
-from mercurial import encoding
-
-def spacejoin(*args):
-    return ' '.join(s for s in args if s)
-
-def shouldprint(ui):
-    return not ui.plain() and (ui._isatty(sys.stderr) or
-                               ui.configbool('progress', 'assume-tty'))
-
-def fmtremaining(seconds):
-    if seconds < 60:
-        # i18n: format XX seconds as "XXs"
-        return _("%02ds") % (seconds)
-    minutes = seconds // 60
-    if minutes < 60:
-        seconds -= minutes * 60
-        # i18n: format X minutes and YY seconds as "XmYYs"
-        return _("%dm%02ds") % (minutes, seconds)
-    # 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"
-        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)
-
-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
-        self.lastprint = time.time() + float(self.ui.config(
-            'progress', 'delay', default=3))
-        self.curtopic = None
-        self.lasttopic = None
-        self.indetcount = 0
-        self.refresh = float(self.ui.config(
-            'progress', 'refresh', default=0.1))
-        self.changedelay = max(3 * self.refresh,
-                               float(self.ui.config(
-                                   'progress', 'changedelay', default=1)))
-        self.order = self.ui.configlist(
-            'progress', 'format',
-            default=['topic', 'bar', 'number', 'estimate'])
-
-    def show(self, now, topic, pos, item, unit, total):
-        if not shouldprint(self.ui):
-            return
-        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':
-                    add = encoding.trim(item, wid, leftside=True)
-                else:
-                    add = encoding.trim(item, wid)
-                add += (wid - encoding.colwidth(add)) * ' '
-            elif indicator == 'bar':
-                add = ''
-                needprogress = True
-            elif indicator == 'unit' and unit:
-                add = unit
-            elif indicator == 'estimate':
-                add = self.estimate(topic, pos, total, now)
-            elif indicator == 'speed':
-                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
-                bar = '=' * (amt - 1)
-                if amt > 0:
-                    bar += '>'
-                bar += ' ' * (progwidth - amt)
-            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)
-        sys.stderr.write('\r' + encoding.trim(out, termwidth))
-        self.lasttopic = topic
-        sys.stderr.flush()
-
-    def clear(self):
-        if not shouldprint(self.ui):
-            return
-        sys.stderr.write('\r%s\r' % (' ' * self.width()))
-
-    def complete(self):
-        if not shouldprint(self.ui):
-            return
-        if self.ui.configbool('progress', 'clear-complete', default=True):
-            self.clear()
-        else:
-            sys.stderr.write('\n')
-        sys.stderr.flush()
-
-    def width(self):
-        tw = self.ui.termwidth()
-        return min(int(self.ui.config('progress', 'width', default=tw)), tw)
-
-    def estimate(self, topic, pos, total, now):
-        if total is None:
-            return ''
-        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 ''
-
-    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 ''
-
-    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
-
-    def progress(self, topic, pos, item='', unit='', total=None):
-        now = time.time()
-        self._refreshlock.acquire()
-        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
-                self.curtopic = topic
-                if now - self.lastprint >= self.refresh and self.topics:
-                    if self._oktoprint(now):
-                        self.lastprint = now
-                        self.show(now, topic, *self.topicstates[topic])
-        finally:
-            self._refreshlock.release()
+from mercurial import progress
 
 _singleton = None
 
@@ -311,7 +66,7 @@
     # setconfig('progress', 'disable', 'True') to disable this extension
     if ui.configbool('progress', 'disable'):
         return
-    if shouldprint(ui) and not ui.debugflag and not ui.quiet:
+    if progress.shouldprint(ui) and not ui.debugflag and not ui.quiet:
         dval = object()
         if getattr(ui, '_progbar', dval) is dval:
             ui.__class__ = progressui
@@ -319,7 +74,7 @@
             # competing progress bars when multiple UI objects get created
             if not progressui._progbar:
                 if _singleton is None:
-                    _singleton = progbar(ui)
+                    _singleton = progress.progbar(ui)
                 progressui._progbar = _singleton
 
 def reposetup(ui, repo):
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/progress.py	Sun Jun 07 17:19:20 2015 -0700
@@ -0,0 +1,252 @@
+# 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.
+
+import sys
+import time
+import threading
+from mercurial import encoding
+
+from mercurial.i18n import _
+
+
+def spacejoin(*args):
+    return ' '.join(s for s in args if s)
+
+def shouldprint(ui):
+    return not ui.plain() and (ui._isatty(sys.stderr) or
+                               ui.configbool('progress', 'assume-tty'))
+
+def fmtremaining(seconds):
+    """format a number of remaining seconds in humain readable way
+
+    This will properly display seconds, minutes, hours, days if needed"""
+    if seconds < 60:
+        # i18n: format XX seconds as "XXs"
+        return _("%02ds") % (seconds)
+    minutes = seconds // 60
+    if minutes < 60:
+        seconds -= minutes * 60
+        # i18n: format X minutes and YY seconds as "XmYYs"
+        return _("%dm%02ds") % (minutes, seconds)
+    # 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"
+        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)
+
+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
+        self.lastprint = time.time() + float(self.ui.config(
+            'progress', 'delay', default=3))
+        self.curtopic = None
+        self.lasttopic = None
+        self.indetcount = 0
+        self.refresh = float(self.ui.config(
+            'progress', 'refresh', default=0.1))
+        self.changedelay = max(3 * self.refresh,
+                               float(self.ui.config(
+                                   'progress', 'changedelay', default=1)))
+        self.order = self.ui.configlist(
+            'progress', 'format',
+            default=['topic', 'bar', 'number', 'estimate'])
+
+    def show(self, now, topic, pos, item, unit, total):
+        if not shouldprint(self.ui):
+            return
+        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':
+                    add = encoding.trim(item, wid, leftside=True)
+                else:
+                    add = encoding.trim(item, wid)
+                add += (wid - encoding.colwidth(add)) * ' '
+            elif indicator == 'bar':
+                add = ''
+                needprogress = True
+            elif indicator == 'unit' and unit:
+                add = unit
+            elif indicator == 'estimate':
+                add = self.estimate(topic, pos, total, now)
+            elif indicator == 'speed':
+                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
+                bar = '=' * (amt - 1)
+                if amt > 0:
+                    bar += '>'
+                bar += ' ' * (progwidth - amt)
+            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)
+        sys.stderr.write('\r' + encoding.trim(out, termwidth))
+        self.lasttopic = topic
+        sys.stderr.flush()
+
+    def clear(self):
+        if not shouldprint(self.ui):
+            return
+        sys.stderr.write('\r%s\r' % (' ' * self.width()))
+
+    def complete(self):
+        if not shouldprint(self.ui):
+            return
+        if self.ui.configbool('progress', 'clear-complete', default=True):
+            self.clear()
+        else:
+            sys.stderr.write('\n')
+        sys.stderr.flush()
+
+    def width(self):
+        tw = self.ui.termwidth()
+        return min(int(self.ui.config('progress', 'width', default=tw)), tw)
+
+    def estimate(self, topic, pos, total, now):
+        if total is None:
+            return ''
+        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 ''
+
+    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 ''
+
+    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
+
+    def progress(self, topic, pos, item='', unit='', total=None):
+        now = time.time()
+        self._refreshlock.acquire()
+        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
+                self.curtopic = topic
+                if now - self.lastprint >= self.refresh and self.topics:
+                    if self._oktoprint(now):
+                        self.lastprint = now
+                        self.show(now, topic, *self.topicstates[topic])
+        finally:
+            self._refreshlock.release()
+