Commits

Paul Nathan committed c6a9e12

Fix: Now performs a friendly interop with other extensions that modify the ui,
such as progress.

Enhancement: grstate is now documented

  • Participants
  • Parent commits 15a3616

Comments (0)

Files changed (8)

+syntax: glob
+*.pyc
+*~
+dist/*
+build/*
+guestrepo.egg-info/*
+tests/*.err
+*.orig
     $ hg grfreeze
     $ hg commit -m Release
 
+###State
+
+`hg grstate`
+
+Print the exact changeset of all guests to stdout, as in .hgguestrepo.
+
+The state command outputs the state of the guest repositories to standard out.
+It uses same format as the .hgguestrepo file, but only outputs the absolute
+changeset hash
+
+State is essentially the freeze command directed to standard out rather than 
+.hgguestrepo.
+
 ###Summary
 
 `hg grsummary`

guestrepo/__init__.py

 
 from guestrepo import pull, push, sync, \
                       freeze, summary, state
-from convert import convert
+import convert
+
+import lockedui
 
 
 # reused options
    "grstate": (state,
               [],
               "hg grstate"),
-   "grconvert": (convert,
+   "grconvert": (convert.convert,
          [('b', 'branch', False,
            "write the current branch of each subrepo instead of the changeset")
          ],
          "hg grconvert [--branch]")
    }
+
+# hook run at mercurial startup just after ui object is created
+def uisetup(ui):
+    # from hgext/color.py
+    # make ui's class be lockedui a subclass of the current ui class
+    # think of it as inserting at the head of a linked list...
+    # TODO: do this on dispatch so we can only use it for gr* commands
+    if not issubclass(ui.__class__, lockedui.lockedui):
+        lockedui.lockedui.__bases__ = (ui.__class__,)
+        ui.__class__ = lockedui.lockedui

guestrepo/convert.py

 
 from guestrepo import GR_CONFIG, GR_MAPPING
 
+#python 2.4 ...
+def any(l):
+    for x in l:
+        if x:
+            return True
+
 def convert(ui, repo, **opts):
     '''convert subrepos to guestrepos
 
     repo.wwrite(GR_CONFIG, hgguestrepostr, '')
     repo.wwrite(GR_MAPPING, hggrmappingstr, '')
 
+
     if any(map(lambda conf: conf not in repo[None], [GR_CONFIG, GR_MAPPING])):
         ui.warn("Do not forget to add %s and %s to the repository\n" %
                 (GR_CONFIG, GR_MAPPING))

guestrepo/guestrepo.py

                       error, node, scmutil, bookmarks, localrepo
 
 from workerpool import workerpool
-from lockedui import lockedui
 
 GR_CONFIG = '.hgguestrepo'
 GR_MAPPING = '.hggrmapping'
     if mapping:
         resolvemapping(guests, getmapping(repo, mapping == 'local'))
     for guest in guests:
-        newui = lockedui(ui, guest)
+        newui = ui.copy()
+        newui.associate_guest(guest)
         if guest.isrepo():
             def work(guest=guest,newui=newui):
                 checkcycles(guest.uri, bannedlocs)
                 newrepo = localrepo.localrepository(newui, guest.root, False)
 
                 # be certain password prompts go to the right place
-                assert(type(newui) == lockedui and newui.guest == guest)
+                assert(newui.guest == guest)
 
                 func(newui, newrepo, guest)
                 newguests = getguestsmissing(newrepo, [])
         elif create:
             def work(guest=guest,newui=newui):
                 checkcycles(guest.uri, bannedlocs)
-                newui.status('cloning %s\n' % guest.canonpath)
+                ui.status('cloning %s\n' % guest.canonpath)
                 if not os.path.exists(guest.root):
                     os.makedirs(guest.root)
 
                 # be certain password prompts go to the right place
-                assert(type(newui) == lockedui and newui.guest == guest)
+                assert(newui.guest == guest)
 
                 srcrepo, newrepo = hg.clone(newui,
                                             {},

guestrepo/lockedui.py

 
 import mercurial.ui
 
-class lockedui(object):
+uilock = threading.RLock()
+
+class lockedui(mercurial.ui.ui):
     ''' A very quick and dirty way of keeping the user's terminal in a sane
         state
 
-        Make a proxy object for the ui with a mutex for everything that touches
+        Make a derived ui class with a mutex for everything that touches
         the terminal.
 
         Currently the lock keeps mercurial from going crazy and asking for more
         a username prompt will be a password prompt from the same repo, as many
         threads may be contending for the lock.
     '''
-
-    lockedfuncs = ['write', 'write_err', 'prompt', 'getpass',
-                   'promptchoice', 'status', 'warn', 'note', 'debug',
-                   'edit', 'traceback', 'log', 'label']
-
-    uilock = threading.RLock()
+    def __init__(self, src=None):
+        super(lockedui, self).__init__(src)
+        if src and hasattr(src, 'guest'):
+            self.guest = src.guest
+        else:
+            self.guest = None
 
     # Each lockedui object MUST be associated with the correct guest to avoid
     # sending passwords to the wrong place.
-    # It is an error to initialize a lockedui from an unlocked ui without
-    # specifying a guest to associate it with
-    def __init__(self, src, guest=None):
-        if type(src) == lockedui:
-            if guest is None:
-                self.guest = src.guest
-            else:
-                self.guest = guest
-            self.ui = mercurial.ui.ui(src.ui)
-        else:
-            assert(type(src) == mercurial.ui.ui)
-            assert(guest)
-            self.guest = guest
-            self.ui = src.copy()
-
-    def copy(self):
-        new = lockedui(mercurial.ui.ui(self.ui), self.guest)
-        return new
+    def associate_guest(self, guest):
+        self.guest = guest
 
     # When multiple threads are used the prompts for usernames and passwords
     # may become unordered.
     # We can't stop that, but we can make the prompts unambiguous whenever they
     # are presented to the user.
 
-    # We are depending on mercurial to propagate the lockedui to anything that
-    # might ask for a password
-
     # prefix username and password with local repository path
     # and remote location
     def prompt(self, msg, *args, **opts):
-        self.uilock.acquire()
+        uilock.acquire()
         try:
-            return self.ui.prompt("%s::%s: %s" % (self.guest.configpath,
-                                                  self.guest.uri, msg),
-                                   *args, **opts)
+            if self.guest:
+                return super(lockedui, self).prompt(
+                                        "%s::%s: %s" % (self.guest.configpath,
+                                                        self.guest.uri, msg),
+                                         *args, **opts)
+            else:
+                return super(lockedui, self).prompt(msg, *args, **opts)
         finally:
-            self.uilock.release()
+            uilock.release()
 
     def getpass(self, *args, **opts):
-        prompt = opts.get('prompt', None)
-        if prompt is None:
-            prompt = "password: "
-        opts['prompt'] = "%s::%s: %s" % (self.guest.configpath,
-                                         self.guest.uri, prompt)
-        self.uilock.acquire()
+        uilock.acquire()
+        if self.guest:
+                super(lockedui, self).write("%s::%s:\n" %
+                                                        (self.guest.configpath, 
+                                                         self.guest.uri))
         try:
-            return ui.getpass(self.ui, *args, **opts)
+            return super(lockedui, self).getpass(*args, **opts)
         finally:
-            self.uilock.release()
+            uilock.release()
 
-    def promptchoice(self, *args, **opts):
-        self.uilock.acquire()
+    # promptchoice is internally a 'prompt', so no need to implement it here
+
+    def write(self, *args, **opts):
+        uilock.acquire()
         try:
-            self.ui.write("%s::%s:\n" % (self.guest.configpath, self.guest.uri))
-            self.ui.promptchoice(*args, **opts)
+            return super(lockedui, self).write(*args, **opts)
         finally:
-            self.uilock.release()
+            uilock.release()
 
-    def __getattr__(self, name):
-        uiattr = getattr(self.ui, name)
-
-        if name in self.lockedfuncs:
-            lockwrapper = self
-            def lockit(*args, **opts):
-                lockwrapper.uilock.acquire()
-                lockwrapper.ui.flush()
-                try:
-                    ret = uiattr(*args, **opts)
-                finally:
-                    lockwrapper.uilock.release()
-                    lockwrapper.ui.flush()
-                return ret
-            return lockit
-        else:
-            return uiattr
+    def write_err(self, *args, **opts):
+        uilock.acquire()
+        try:
+            super(lockedui, self).write_err(*args, **opts)
+        finally:
+            uilock.release()

tests/interactive/choicetest.py

 
 import threading
 
-import mercurial.ui
 from guestrepo import lockedui
 from guestrepo import guestrepo
 
                                "canonpath", "URI", "CSID", "root")
            for n in range(5)]
 
-ui = mercurial.ui.ui()
-
-locks = [lockedui.lockedui(ui, guest) for guest in guests]
+locks = [lockedui.lockedui() for guest in guests]
+for ui, guest in zip(locks, guests):
+    ui.associate_guest(guest)
 
 def gettask(lock=None):
     def prompt():

tests/interactive/testlockedui.py

+# Just a sanity check for the lockedui class
+# Doesn't test anything related to threading
+
+from mercurial import extensions
+import mercurial.ui
+
+ui = mercurial.ui.ui()
+extensions.loadall(ui)
+guestrepo = extensions.find('guestrepo')
+
+lockedui = guestrepo.lockedui.lockedui
+
+lui = lockedui()
+
+def test():
+   a =  lui.prompt("Say 'yes': ")
+   print "answer: %s" % repr(a)
+   assert a == 'yes'  
+
+   a = lui.promptchoice("Type (y) and not (n): ", ['&yes', '&no'], default=1)
+   print "answer: %s" % repr(a)
+   assert a == 0
+
+   a = lui.getpass("type 'password': ")
+   print "answer: %s" % repr(a)
+   assert a == 'password'
+
+test()
+
+guest = guestrepo.guestrepo.guestrepo('guest', 'config/path', 'cannon/path',
+                                      'URI://location/', 'default', 'root')
+
+lui.associate_guest(guest)
+
+test()