[PATCH 1 of 3] progress: move from extension to core

Martin Geisler mrtn.gslr at gmail.com
Sat May 12 18:09:15 UTC 2012


# HG changeset patch
# User Martin Geisler <martin at geisler.net>
# Date 1336829943 -7200
# Node ID 136fc47bfbafb39a599937974d05aaa345ed79a9
# Parent  654b9e1966f76d38b11fbd1d518efce5d58caa88
progress: move from extension to core

The old progress extension is now ignored, so people can keep loading
it in their existing config.

Some WSGI servers will throw an exception if sys.stdout is touched
even the slightest (such as calling sys.stdout.isatty()), so the hgweb
code now disables the progress extension. This also means 'hg serve'
will stop printing progress bars when it's bundling changesets.

The hgweb code doesn't have easy access to the main ui, which is the
ui that the progress bar references for its settings. So to let the
hgweb code update the settings, ui.setconfig will now redirect calls
in the 'progress' section directly to the progress bar's ui. Since
there is only a singleton progress bar, it makes little sense for the
settings in the 'progress' section to diverge anyway.

Moving the code into core somehow fixed a problem with doubled
progress bars: two progress bars would be printed, though they had the
same information. This was therefore not visible in normal usage, it
only showed up in the test suite.

diff --git a/mercurial/extensions.py b/mercurial/extensions.py
--- a/mercurial/extensions.py
+++ b/mercurial/extensions.py
@@ -11,7 +11,7 @@
 
 _extensions = {}
 _order = []
-_ignore = ['hbisect', 'bookmarks', 'parentrevspec']
+_ignore = ['hbisect', 'bookmarks', 'parentrevspec', 'progress']
 
 def extensions():
     for name in _order:
diff --git a/mercurial/help/config.txt b/mercurial/help/config.txt
--- a/mercurial/help/config.txt
+++ b/mercurial/help/config.txt
@@ -987,6 +987,49 @@
     file exists, it is replaced. Default: None, data is printed on
     stderr
 
+``progress``
+""""""""""""
+
+Most commands log progress information and Mercurial will use this to
+draw progress bars that are as informative as possible. Some progress
+bars only offer indeterminate information, while others have a
+definite end point.
+
+The following settings are available to control progress bars:
+
+``delay``
+  Number of seconds (float) before showing the progress bar. Default: 3.
+
+``changedelay``
+  Minimum delay before showing a new topic. If set to less than 3 *
+  ``refresh``, that value will be used instead. Default: 1.
+
+``refresh``
+  Time in seconds between refreshes of the progress bar. Default: 0.1.
+
+``format``
+  Format of the progress bar. Valid entries are ``topic``, ``bar``,
+  ``number``, ``unit``, ``estimate``, ``speed``, and ``item``. The
+  ``item`` defaults to the last 20 characters of the item, but this
+  can be changed by adding either ``-<num>`` which would take the last
+  num characters, or ``+<num>`` for the first num characters. Default:
+  ``topic bar number estimate``.
+
+``width``
+  If set, the maximum width of the progress information (that is,
+  min(width, term width) will be used). Default: none.
+
+``clear-complete``
+  Clear the progress bar after it's done. Default: True.
+
+``disable``
+  If true, don't show a progress bar. Default: False.
+
+``assume-tty``
+  If true, *always* show a progress bar, unless disable is given.
+  Default: False.
+
+
 ``revsetalias``
 """""""""""""""
 
diff --git a/mercurial/hgweb/hgweb_mod.py b/mercurial/hgweb/hgweb_mod.py
--- a/mercurial/hgweb/hgweb_mod.py
+++ b/mercurial/hgweb/hgweb_mod.py
@@ -37,6 +37,7 @@
 
         self.repo.ui.setconfig('ui', 'report_untrusted', 'off')
         self.repo.ui.setconfig('ui', 'interactive', 'off')
+        self.repo.ui.setconfig('progress', 'disable', True)
         hook.redirect(True)
         self.mtime = -1
         self.size = -1
diff --git a/mercurial/hgweb/hgwebdir_mod.py b/mercurial/hgweb/hgwebdir_mod.py
--- a/mercurial/hgweb/hgwebdir_mod.py
+++ b/mercurial/hgweb/hgwebdir_mod.py
@@ -98,6 +98,7 @@
             u = ui.ui()
             u.setconfig('ui', 'report_untrusted', 'off')
             u.setconfig('ui', 'interactive', 'off')
+            u.setconfig('progress', 'disable', True)
 
         if not isinstance(self.conf, (dict, list, tuple)):
             map = {'paths': 'hgweb-paths'}
diff --git a/hgext/progress.py b/mercurial/progress.py
rename from hgext/progress.py
rename to mercurial/progress.py
--- a/hgext/progress.py
+++ b/mercurial/progress.py
@@ -5,47 +5,19 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-"""show progress bars for some actions
-
-This extension uses the progress information logged by hg commands
-to draw progress bars that are as informative as possible. Some progress
-bars only offer indeterminate information, while others have a definite
-end point.
-
-The following settings are available::
-
-  [progress]
-  delay = 3 # number of seconds (float) before showing the progress bar
-  changedelay = 1 # changedelay: minimum delay before showing a new topic.
-                  # If set to less than 3 * refresh, that value will
-                  # be used instead.
-  refresh = 0.1 # time in seconds between refreshes of the progress bar
-  format = topic bar number estimate # format of the progress bar
-  width = <none> # if set, the maximum width of the progress information
-                 # (that is, min(width, term width) will be used)
-  clear-complete = True # clear the progress bar after it's done
-  disable = False # if true, don't show a progress bar
-  assume-tty = False # if true, ALWAYS show a progress bar, unless
-                     # disable is given
-
-Valid entries for the format field are topic, bar, number, unit,
-estimate, speed, and item. item defaults to the last 20 characters of
-the item, but this can be changed by adding either ``-<num>`` which
-would take the last num characters, or ``+<num>`` for the first num
-characters.
-"""
-
 import sys
 import time
 
-from mercurial import util
-from mercurial.i18n import _
+import util
+from i18n import _
 
 def spacejoin(*args):
     return ' '.join(s for s in args if s)
 
 def shouldprint(ui):
-    return util.isatty(sys.stderr) or ui.configbool('progress', 'assume-tty')
+    return (not ui.configbool('progress', 'disable')
+            and (util.isatty(sys.stderr)
+                 or ui.configbool('progress', 'assume-tty')))
 
 def fmtremaining(seconds):
     if seconds < 60:
@@ -255,41 +227,9 @@
 
 _singleton = None
 
-def uisetup(ui):
+def progressbar(ui):
+    """return the singleton progressbar"""
     global _singleton
-    class progressui(ui.__class__):
-        _progbar = None
-
-        def _quiet(self):
-            return self.debugflag or self.quiet
-
-        def progress(self, *args, **opts):
-            if not self._quiet():
-                self._progbar.progress(*args, **opts)
-            return super(progressui, self).progress(*args, **opts)
-
-        def write(self, *args, **opts):
-            if not self._quiet() and self._progbar.printed:
-                self._progbar.clear()
-            return super(progressui, self).write(*args, **opts)
-
-        def write_err(self, *args, **opts):
-            if not self._quiet() and self._progbar.printed:
-                self._progbar.clear()
-            return super(progressui, self).write_err(*args, **opts)
-
-    # Apps that derive a class from ui.ui() can use
-    # 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:
-        ui.__class__ = progressui
-        # we instantiate one globally shared progress bar to avoid
-        # competing progress bars when multiple UI objects get created
-        if not progressui._progbar:
-            if _singleton is None:
-                _singleton = progbar(ui)
-            progressui._progbar = _singleton
-
-def reposetup(ui, repo):
-    uisetup(repo.ui)
+    if _singleton is None:
+        _singleton = progbar(ui)
+    return _singleton
diff --git a/mercurial/ui.py b/mercurial/ui.py
--- a/mercurial/ui.py
+++ b/mercurial/ui.py
@@ -7,7 +7,7 @@
 
 from i18n import _
 import errno, getpass, os, socket, sys, tempfile, traceback
-import config, scmutil, util, error, formatter
+import config, scmutil, util, error, formatter, progress
 
 class ui(object):
     def __init__(self, src=None):
@@ -19,6 +19,7 @@
         self._ucfg = config.config() # untrusted
         self._trustusers = set()
         self._trustgroups = set()
+        self._progbar = progress.progressbar(self)
 
         if src:
             self.fout = src.fout
@@ -145,6 +146,9 @@
             self._trustusers.update(self.configlist('trusted', 'users'))
             self._trustgroups.update(self.configlist('trusted', 'groups'))
 
+        if section in (None, 'progress'):
+            self._progbar.resetstate()
+
     def backupconfig(self, section, item):
         return (self._ocfg.backup(section, item),
                 self._tcfg.backup(section, item),
@@ -155,6 +159,12 @@
         self._ucfg.restore(data[2])
 
     def setconfig(self, section, name, value, overlay=True):
+        if self._progbar.ui is not self and section == 'progress':
+            # Settings in 'progress' section are passed on to the ui
+            # associated with the singleton progress bar.
+            self._progbar.ui.setconfig(section, name, value, overlay)
+            return
+
         if overlay:
             self._ocfg.set(section, name, value)
         self._tcfg.set(section, name, value)
@@ -466,6 +476,8 @@
         "cmdname.type" is recommended. For example, status issues
         a label of "status.modified" for modified files.
         '''
+        if not self._quiet() and self._progbar.printed:
+            self._progbar.clear()
         if self._buffers:
             self._buffers[-1].extend([str(a) for a in args])
         else:
@@ -473,6 +485,8 @@
                 self.fout.write(str(a))
 
     def write_err(self, *args, **opts):
+        if not self._quiet() and self._progbar.printed:
+            self._progbar.clear()
         try:
             if not getattr(self.fout, 'closed', False):
                 self.fout.flush()
@@ -699,6 +713,9 @@
                 os.environ.get("VISUAL") or
                 os.environ.get("EDITOR", editor))
 
+    def _quiet(self):
+        return self.debugflag or self.quiet
+
     def progress(self, topic, pos, item="", unit="", total=None):
         '''show a progress message
 
@@ -715,6 +732,8 @@
         All topics should be marked closed by setting pos to None at
         termination.
         '''
+        if not self._quiet():
+            self._progbar.progress(topic, pos, item, unit, total)
 
         if pos is None or not self.debugflag:
             return
diff --git a/tests/test-archive.t b/tests/test-archive.t
--- a/tests/test-archive.t
+++ b/tests/test-archive.t
@@ -224,14 +224,9 @@
   $ hg archive ../with-progress 2>&1 | "$TESTDIR/filtercr.py"
   
   archiving [                                           ] 0/4
-  archiving [                                           ] 0/4
-  archiving [=========>                                 ] 1/4
   archiving [=========>                                 ] 1/4
   archiving [====================>                      ] 2/4
-  archiving [====================>                      ] 2/4
   archiving [===============================>           ] 3/4
-  archiving [===============================>           ] 3/4
-  archiving [==========================================>] 4/4
   archiving [==========================================>] 4/4
                                                               \r (esc)
 
diff --git a/tests/test-debugbuilddag.t b/tests/test-debugbuilddag.t
--- a/tests/test-debugbuilddag.t
+++ b/tests/test-debugbuilddag.t
@@ -13,37 +13,20 @@
   
   building [                                          ]  0/12
   building [                                          ]  0/12
-  building [                                          ]  0/12
-  building [                                          ]  0/12
-  building [==>                                       ]  1/12
-  building [==>                                       ]  1/12
   building [==>                                       ]  1/12
   building [==>                                       ]  1/12
   building [======>                                   ]  2/12
-  building [======>                                   ]  2/12
-  building [=========>                                ]  3/12
   building [=========>                                ]  3/12
   building [=============>                            ]  4/12
   building [=============>                            ]  4/12
   building [=============>                            ]  4/12
-  building [=============>                            ]  4/12
-  building [=============>                            ]  4/12
-  building [=============>                            ]  4/12
-  building [================>                         ]  5/12
   building [================>                         ]  5/12
   building [====================>                     ]  6/12
-  building [====================>                     ]  6/12
-  building [=======================>                  ]  7/12
   building [=======================>                  ]  7/12
   building [===========================>              ]  8/12
   building [===========================>              ]  8/12
-  building [===========================>              ]  8/12
-  building [===========================>              ]  8/12
-  building [==============================>           ]  9/12
   building [==============================>           ]  9/12
   building [==================================>       ] 10/12
-  building [==================================>       ] 10/12
-  building [=====================================>    ] 11/12
   building [=====================================>    ] 11/12
                                                               \r (esc)
 
diff --git a/tests/test-patchbomb.t b/tests/test-patchbomb.t
--- a/tests/test-patchbomb.t
+++ b/tests/test-patchbomb.t
@@ -162,14 +162,9 @@
   
   
   sending [                                             ] 0/3
-  sending [                                             ] 0/3
-                                                              
                                                               
   sending [==============>                              ] 1/3
-  sending [==============>                              ] 1/3
                                                               
-                                                              
-  sending [=============================>               ] 2/3
   sending [=============================>               ] 2/3
                                                               \r (esc)
   Sending [PATCH 0 of 2] test ...
diff --git a/tests/test-progress.t b/tests/test-progress.t
--- a/tests/test-progress.t
+++ b/tests/test-progress.t
@@ -37,7 +37,6 @@
 
   $ cp $HGRCPATH $HGRCPATH.orig
   $ echo "[extensions]" >> $HGRCPATH
-  $ echo "progress=" >> $HGRCPATH
   $ echo "loop=`pwd`/loop.py" >> $HGRCPATH
   $ echo "[progress]" >> $HGRCPATH
   $ echo "format = topic bar number" >> $HGRCPATH
@@ -161,12 +160,14 @@
   > 
   > def uisetup(ui):
   >     time.time = mocktime(int(os.environ.get('MOCKTIME', '11')))
+  >     # The extension is loaded after time.time() is called by the
+  >     # progessbar, so we need to reset it again.
+  >     ui._progbar.resetstate()
   > EOF
 
   $ cp $HGRCPATH.orig $HGRCPATH
   $ echo "[extensions]" >> $HGRCPATH
   $ echo "mocktime=`pwd`/mocktime.py" >> $HGRCPATH
-  $ echo "progress=" >> $HGRCPATH
   $ echo "loop=`pwd`/loop.py" >> $HGRCPATH
   $ echo "[progress]" >> $HGRCPATH
   $ echo "assume-tty=1" >> $HGRCPATH
diff --git a/tests/test-subrepo-recursion.t b/tests/test-subrepo-recursion.t
--- a/tests/test-subrepo-recursion.t
+++ b/tests/test-subrepo-recursion.t
@@ -257,32 +257,21 @@
   > width = 60
   > EOF
 
-Test archiving to a directory tree (the doubled lines in the output
-only show up in the test output, not in real usage):
+Test archiving to a directory tree:
 
   $ hg archive --subrepos ../archive 2>&1 | "$TESTDIR/filtercr.py"
   
   archiving [                                           ] 0/3
-  archiving [                                           ] 0/3
-  archiving [=============>                             ] 1/3
   archiving [=============>                             ] 1/3
   archiving [===========================>               ] 2/3
-  archiving [===========================>               ] 2/3
-  archiving [==========================================>] 3/3
   archiving [==========================================>] 3/3
                                                               
   archiving (foo) [                                     ] 0/3
-  archiving (foo) [                                     ] 0/3
-  archiving (foo) [===========>                         ] 1/3
   archiving (foo) [===========>                         ] 1/3
   archiving (foo) [=======================>             ] 2/3
-  archiving (foo) [=======================>             ] 2/3
-  archiving (foo) [====================================>] 3/3
   archiving (foo) [====================================>] 3/3
                                                               
   archiving (foo/bar) [                                 ] 0/1 (glob)
-  archiving (foo/bar) [                                 ] 0/1 (glob)
-  archiving (foo/bar) [================================>] 1/1 (glob)
   archiving (foo/bar) [================================>] 1/1 (glob)
                                                               \r (esc)
   $ find ../archive | sort
@@ -303,26 +292,16 @@
   $ hg archive --subrepos ../archive.zip 2>&1 | "$TESTDIR/filtercr.py"
   
   archiving [                                           ] 0/3
-  archiving [                                           ] 0/3
-  archiving [=============>                             ] 1/3
   archiving [=============>                             ] 1/3
   archiving [===========================>               ] 2/3
-  archiving [===========================>               ] 2/3
-  archiving [==========================================>] 3/3
   archiving [==========================================>] 3/3
                                                               
   archiving (foo) [                                     ] 0/3
-  archiving (foo) [                                     ] 0/3
-  archiving (foo) [===========>                         ] 1/3
   archiving (foo) [===========>                         ] 1/3
   archiving (foo) [=======================>             ] 2/3
-  archiving (foo) [=======================>             ] 2/3
-  archiving (foo) [====================================>] 3/3
   archiving (foo) [====================================>] 3/3
                                                               
   archiving (foo/bar) [                                 ] 0/1 (glob)
-  archiving (foo/bar) [                                 ] 0/1 (glob)
-  archiving (foo/bar) [================================>] 1/1 (glob)
   archiving (foo/bar) [================================>] 1/1 (glob)
                                                               \r (esc)
 
@@ -334,26 +313,16 @@
   $ hg archive --subrepos -r tip ../archive.tar.gz 2>&1 | "$TESTDIR/filtercr.py"
   
   archiving [                                           ] 0/3
-  archiving [                                           ] 0/3
-  archiving [=============>                             ] 1/3
   archiving [=============>                             ] 1/3
   archiving [===========================>               ] 2/3
-  archiving [===========================>               ] 2/3
-  archiving [==========================================>] 3/3
   archiving [==========================================>] 3/3
                                                               
   archiving (foo) [                                     ] 0/3
-  archiving (foo) [                                     ] 0/3
-  archiving (foo) [===========>                         ] 1/3
   archiving (foo) [===========>                         ] 1/3
   archiving (foo) [=======================>             ] 2/3
-  archiving (foo) [=======================>             ] 2/3
-  archiving (foo) [====================================>] 3/3
   archiving (foo) [====================================>] 3/3
                                                               
   archiving (foo/bar) [                                 ] 0/1 (glob)
-  archiving (foo/bar) [                                 ] 0/1 (glob)
-  archiving (foo/bar) [================================>] 1/1 (glob)
   archiving (foo/bar) [================================>] 1/1 (glob)
                                                               
   cloning subrepo foo from $TESTTMP/repo/foo


More information about the Mercurial-devel mailing list