[PATCH 2 of 5 RFC] hglib: a wrapper around hg's cmdserver, written in Python

Idan Kamara idankk86 at gmail.com
Fri Jun 3 15:04:41 CDT 2011


# HG changeset patch
# User Idan Kamara <idankk86 at gmail.com>
# Date 1307111464 -10800
# Node ID 15c24865581d773d00dec51ebd0cbe222feec6e0
# Parent  5b9901ab0f4c60450ecd5c112fd937493a043667
hglib: a wrapper around hg's cmdserver, written in Python

diff -r 5b9901ab0f4c -r 15c24865581d contrib/hglib/hglib.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/hglib/hglib.py	Fri Jun 03 17:31:04 2011 +0300
@@ -0,0 +1,100 @@
+# hglib.py
+#
+#  Copyright Matt Mackall <mpm at selenic.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 subprocess
+import struct
+import cStringIO
+
+HGPATH = 'hg'
+
+def connect(path, out, err, extraconfigs=None):
+    return hgclient(path, out, err, extraconfigs)
+
+class CommandFailed(Exception):
+    pass
+
+class hgclient(object):
+    inputfmt = '>I'
+    outputfmt = '>cI'
+    outputfmtsize = struct.calcsize(outputfmt)
+    retfmt = '>i'
+
+    def __init__(self, path, out, err, extraconfigs):
+        self.channels = {'o' : out, 'e' : err}
+        cmdline = [HGPATH, '-R', path, 'serve', '--cmdserver', 'pipe']
+
+        if extraconfigs:
+            cmdline += ['--config'] + extraconfigs
+
+        self.proc = subprocess.Popen(cmdline,
+                                     stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+
+    def terminated(self):
+        if self.proc.poll() is not None:
+            return self.proc.returncode
+        else:
+            return None
+
+    def rawcommand(self, args):
+        if not self.proc or self.terminated():
+            raise ValueError("server not connected")
+
+        args = '\0'.join(args)
+        self.proc.stdin.write(struct.pack(hgclient.inputfmt, len(args)))
+        self.proc.stdin.write(args)
+
+        while True:
+            data = self.proc.stdout.read(hgclient.outputfmtsize)
+            (channel, length) = struct.unpack(hgclient.outputfmt, data)
+            data = self.proc.stdout.read(length)
+            if data[0] == '\0':
+                # end of output, this is the return code
+                break
+            self.channels[channel].write(data)
+
+        ret = struct.unpack(hgclient.retfmt, data[1:])[0]
+        return ret
+
+    def outputrawcommand(self, args):
+        self.redirect()
+        ret = self.rawcommand(args)
+        out, err = self.restore()
+
+        return (ret, out, err)
+
+    def status(self):
+        ret, out, err = self.outputrawcommand(['status', '-0'])
+
+        if ret:
+            raise CommandFailed(ret, out, err)
+
+        d = dict((c, []) for c in 'MARC!?I ')
+
+        for entry in out.split('\0'):
+            if entry:
+                t, f = entry.split(' ', 1)
+                d[t].append(f)
+
+        return d
+
+    def close(self):
+        self.proc.stdin.close()
+        self.proc.wait()
+        ret = self.proc.returncode
+        self.proc = None
+
+        return ret
+
+    def redirect(self):
+        self.old = self.channels['o'], self.channels['e']
+        self.channels['o'], self.channels['e'] = \
+            cStringIO.StringIO(), cStringIO.StringIO()
+
+    def restore(self):
+        out, err = self.channels['o'].getvalue(), self.channels['e'].getvalue()
+        self.channels['o'], self.channels['e'] = self.old
+        return out, err


More information about the Mercurial-devel mailing list