[PATCH] sshrepo: add support for paramiko transport engine

Steve Borho steve at borho.org
Thu Apr 1 00:47:11 CDT 2010


# HG changeset patch
# User Steve Borho <steve at borho.org>
# Date 1270100782 18000
# Node ID 692bd14317b35db4e856d222269d3c23320fced6
# Parent  fa7a14277cefdfda5b331fa9f9cddbfab8dd8eca
sshrepo: add support for paramiko transport engine

This is incomplete, looking for comment and/or aid.  It could at least use some
cleanup and improved error handling.  I'm not at all sure readerr() won't get
stuck if there's actual stderr output.  But this at least passes simple sniff
tests.

To use, install paramiko and pycrypto Python modules, then set ui.ssh to
paramiko.

diff -r fa7a14277cef -r 692bd14317b3 mercurial/sshrepo.py
--- a/mercurial/sshrepo.py	Thu Apr 01 00:15:29 2010 +0200
+++ b/mercurial/sshrepo.py	Thu Apr 01 00:46:22 2010 -0500
@@ -22,6 +22,7 @@
 
 class sshrepository(repo.repository):
     def __init__(self, ui, path, create=0):
+        self._pclient = None
         self._url = path
         self.ui = ui
 
@@ -40,16 +41,42 @@
         args = util.sshargs(sshcmd, self.host, self.user, self.port)
 
         if create:
-            cmd = '%s %s "%s init %s"'
-            cmd = cmd % (sshcmd, args, remotecmd, self.path)
-
-            ui.note(_('running %s\n') % cmd)
-            res = util.system(cmd)
+            if sshcmd == 'paramiko':
+                cmd = '%s init %s'
+                cmd = cmd % (remotecmd, self.path)
+                ui.note(_('running %s\n') % cmd)
+                client = self.pconnect()
+                client.exec_command(cmd)
+                client.close()
+                res = 0
+            else:
+                cmd = '%s %s "%s init %s"'
+                cmd = cmd % (sshcmd, args, remotecmd, self.path)
+                ui.note(_('running %s\n') % cmd)
+                res = util.system(cmd)
             if res != 0:
                 self.abort(error.RepoError(_("could not create remote repo")))
 
         self.validate_repo(ui, sshcmd, args, remotecmd)
 
+    def pconnect(self):
+        import demandimport
+        demandimport.disable()
+        try:
+            # http://www.lag.net/paramiko/docs/
+            from paramiko import SSHClient, AutoAddPolicy
+        except ImportError:
+            self.abort(error.Abort(_("paramiko not found")))
+        demandimport.enable()
+        client = SSHClient()
+        client.load_system_host_keys()
+        client.connect(self.host, int(self.port or '22'), self.user)
+        client.get_transport().use_compression(True)
+        # client.get_transport().set_keepalive(True)
+        # client.set_missing_host_key_policy(AutoAddPolicy())
+        # client.SecurityOptions.*
+        return client
+
     def url(self):
         return self._url
 
@@ -57,12 +84,18 @@
         # cleanup up previous run
         self.cleanup()
 
-        cmd = '%s %s "%s -R %s serve --stdio"'
-        cmd = cmd % (sshcmd, args, remotecmd, self.path)
-
-        cmd = util.quotecommand(cmd)
-        ui.note(_('running %s\n') % cmd)
-        self.pipeo, self.pipei, self.pipee = util.popen3(cmd)
+        if sshcmd == 'paramiko':
+            cmd = '%s -R %s serve --stdio'
+            cmd = cmd % (remotecmd, self.path)
+            client = self.pconnect()
+            self.pipeo, self.pipei, self.pipee = client.exec_command(cmd)
+            self._pclient = client
+        else:
+            cmd = '%s %s "%s -R %s serve --stdio"'
+            cmd = cmd % (sshcmd, args, remotecmd, self.path)
+            cmd = util.quotecommand(cmd)
+            ui.note(_('running %s\n') % cmd)
+            self.pipeo, self.pipei, self.pipee = util.popen3(cmd)
 
         # skip any noise generated by remote shell
         self.do_cmd("hello")
@@ -89,9 +122,13 @@
 
     def readerr(self):
         while 1:
-            size = util.fstat(self.pipee).st_size
-            if size == 0:
-                break
+            if self._pclient:
+                if not self.pipee.channel.recv_stderr_ready():
+                    break
+            else:
+                size = util.fstat(self.pipee).st_size
+                if size == 0:
+                    break
             l = self.pipee.readline()
             if not l:
                 break
@@ -103,6 +140,8 @@
 
     def cleanup(self):
         try:
+            if self._pclient:
+                self._pclient.close()
             self.pipeo.close()
             self.pipei.close()
             # read the error descriptor until EOF


More information about the Mercurial-devel mailing list