[PATCH 1 of 4 hglib] turn hglib into a module and expose open (previously connect) in its __init__.py
Idan Kamara
idankk86 at gmail.com
Fri Aug 5 10:42:38 CDT 2011
# HG changeset patch
# User Idan Kamara <idankk86 at gmail.com>
# Date 1311450936 -10800
# Node ID 3ab9a103cc2711bd3dd74c2b0a878e0148474018
# Parent 0358762171c05ccb8337cb735a2469f4dc0ab479
turn hglib into a module and expose open (previously connect) in its __init__.py
so common usage will now be:
import hglib
hglib.open(...)
also rename hglib.py to client.py
diff -r 0358762171c0 -r 3ab9a103cc27 hglib/__init__.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hglib/__init__.py Sat Jul 23 22:55:36 2011 +0300
@@ -0,0 +1,9 @@
+from client import hgclient
+
+HGPATH = 'hg'
+
+def open(path=None, encoding=None, configs=None):
+ ''' starts a cmdserver for the given path (or for a repository found in the
+ cwd). HGENCODING is set to the given encoding. configs is a list of key, value,
+ similar to those passed to hg --config. '''
+ return hgclient(path, encoding, configs)
diff -r 0358762171c0 -r 3ab9a103cc27 hglib/client.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hglib/client.py Sat Jul 23 22:55:36 2011 +0300
@@ -0,0 +1,299 @@
+import subprocess, os, struct, cStringIO, collections
+import hglib, error, util, templates
+
+class hgclient(object):
+ inputfmt = '>I'
+ outputfmt = '>cI'
+ outputfmtsize = struct.calcsize(outputfmt)
+ retfmt = '>i'
+
+ revision = collections.namedtuple('revision', 'rev, node, tags, '
+ 'branch, author, desc')
+
+ def __init__(self, path, encoding, configs):
+ args = [hglib.HGPATH, 'serve', '--cmdserver', 'pipe']
+ if path:
+ args += ['-R', path]
+ if configs:
+ args += ['--config'] + configs
+ env = dict(os.environ)
+ if encoding:
+ env['HGENCODING'] = encoding
+
+ self.server = subprocess.Popen(args, stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE, env=env)
+
+ self._readhello()
+ self._config = {}
+
+ def _readhello(self):
+ """ read the hello message the server sends when started """
+ ch, msg = self._readchannel()
+ assert ch == 'o'
+
+ msg = msg.split('\n')
+
+ self.capabilities = msg[0][len('capabilities: '):]
+ if not self.capabilities:
+ raise error.ResponseError("bad hello message: expected 'capabilities: '"
+ ", got %r" % msg[0])
+
+ self.capabilities = set(self.capabilities.split())
+
+ # at the very least the server should be able to run commands
+ assert 'runcommand' in self.capabilities
+
+ self._encoding = msg[1][len('encoding: '):]
+ if not self._encoding:
+ raise error.ResponseError("bad hello message: expected 'encoding: '"
+ ", got %r" % msg[1])
+
+ def _readchannel(self):
+ data = self.server.stdout.read(hgclient.outputfmtsize)
+ if not data:
+ raise error.ServerError()
+ channel, length = struct.unpack(hgclient.outputfmt, data)
+ if channel in 'IL':
+ return channel, length
+ else:
+ return channel, self.server.stdout.read(length)
+
+ def _parserevs(self, splitted):
+ ''' splitted is a list of fields according to our rev.style, where each 6
+ fields compose one revision. '''
+ return [self.revision._make(rev) for rev in util.grouper(6, splitted)]
+
+ def _eatlines(self, s, n):
+ idx = 0
+ for i in xrange(n):
+ idx = s.find('\n', idx) + 1
+
+ return s[idx:]
+
+ def runcommand(self, args, inchannels, outchannels):
+ def writeblock(data):
+ self.server.stdin.write(struct.pack(self.inputfmt, len(data)))
+ self.server.stdin.write(data)
+ self.server.stdin.flush()
+
+ if not self.server:
+ raise ValueError("server not connected")
+
+ self.server.stdin.write('runcommand\n')
+ writeblock('\0'.join(args))
+
+ while True:
+ channel, data = self._readchannel()
+
+ # input channels
+ if channel in inchannels:
+ writeblock(inchannels[channel](data))
+ # output channels
+ elif channel in outchannels:
+ outchannels[channel](data)
+ # result channel, command finished
+ elif channel == 'r':
+ return struct.unpack(hgclient.retfmt, data)[0]
+ # a channel that we don't know and can't ignore
+ elif channel.isupper():
+ raise error.ResponseError("unexpected data on required channel '%s'"
+ % channel)
+ # optional channel
+ else:
+ pass
+
+ def outputruncommand(self, args, inchannels = {}, raiseonerror=True):
+ ''' run the command specified by args, returning (ret, output, error) '''
+ out, err = cStringIO.StringIO(), cStringIO.StringIO()
+ outchannels = {'o' : out.write, 'e' : err.write}
+ ret = self.runcommand(args, inchannels, outchannels)
+ if ret and raiseonerror:
+ raise error.CommandError(args, ret, out.getvalue(), err.getvalue())
+ return ret, out.getvalue(), err.getvalue()
+
+ def close(self):
+ self.server.stdin.close()
+ self.server.wait()
+ ret = self.server.returncode
+ self.server = None
+ return ret
+
+ @property
+ def encoding(self):
+ """ get the servers encoding """
+ if not 'getencoding' in self.capabilities:
+ raise CapabilityError('getencoding')
+
+ if not self._encoding:
+ self.server.stdin.write('getencoding\n')
+ self._encoding = self._readfromchannel('r')
+
+ return self._encoding
+
+ def config(self, refresh=False):
+ if not self._config or refresh:
+ self._config.clear()
+
+ ret, out, err = self.outputruncommand(['showconfig'])
+ if ret:
+ raise error.CommandError(['showconfig'], ret, out, err)
+
+ for entry in cStringIO.StringIO(out):
+ k, v = entry.rstrip().split('=', 1)
+ section, name = k.split('.', 1)
+ self._config.setdefault(section, {})[name] = v
+
+ return self._config
+
+ def status(self):
+ ret, out = self.outputruncommand(['status', '-0'])
+
+ 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 log(self, revrange=None):
+ args = ['log'] + templates.changeset
+ if revrange:
+ args.append('-r')
+ args += revrange
+
+ out = self.outputruncommand(args)[1]
+ out = out.split('\0')[:-1]
+
+ return self._parserevs(out)
+
+ def incoming(self, revrange=None, path=None):
+ args = ['incoming'] + templates.changeset
+ if revrange:
+ args.append('-r')
+ args += revrange
+
+ if path:
+ args += [path]
+
+ ret, out, err = self.outputruncommand(args, raiseonerror=False)
+ if not ret:
+ out = self._eatlines(out, 2).split('\0')[:-1]
+ return self._parserevs(out)
+ elif ret == 1:
+ return []
+ else:
+ raise error.CommandError(args, ret, out, err)
+
+ def outgoing(self, revrange=None, path=None):
+ args = ['outgoing'] + templates.changeset
+ if revrange:
+ args.append('-r')
+ args += revrange
+
+ if path:
+ args += [path]
+
+ ret, out, err = self.outputruncommand(args, raiseonerror=False)
+ if not ret:
+ out = self._eatlines(out, 2).split('\0')[:-1]
+ return self._parserevs(out)
+ elif ret == 1:
+ return []
+ else:
+ raise error.CommandError(args, ret, out, err)
+
+ def commit(self, message, addremove=False):
+ # --debug will print the committed cset
+ args = ['commit', '--debug', '-m', message]
+
+ if addremove:
+ args += ['-A']
+
+ out = self.outputruncommand(args)[1]
+ rev = out.split(':')[1]
+ return self.log(revrange=[rev])[0]
+
+ def import_(self, patch):
+ if isinstance(patch, str):
+ fp = open(patch)
+ else:
+ assert hasattr(patch, 'read')
+ assert hasattr(patch, 'readline')
+
+ fp = patch
+
+ try:
+ inchannels = {'I' : fp.read, 'L' : fp.readline}
+ self.outputruncommand(['import', '-'], inchannels)
+ finally:
+ if fp != patch:
+ fp.close()
+
+ def root(self):
+ return self.outputruncommand(['root'])[1].rstrip()
+
+ def clone(self, source='.', dest=None, branch=None, updaterev=None,
+ revrange=None):
+ args = ['clone']
+
+ if branch:
+ args += ['-b', branch]
+ if updaterev:
+ args += ['-u', updaterev]
+ if revrange:
+ args.append('-r')
+ args += revrange
+ args.append(source)
+
+ if dest:
+ args.append(dest)
+
+ self.outputruncommand(args)
+
+ def tip(self):
+ out = self.outputruncommand(['tip'] + templates.changeset)[1]
+ out = out.split('\0')
+
+ return self._parserevs(out)[0]
+
+ def branch(self, name=None):
+ if not name:
+ return self.outputruncommand(['branch'])[1].rstrip()
+
+ def branches(self):
+ out = self.outputruncommand(['branches'])[1]
+ branches = {}
+ for line in out.rstrip().split('\n'):
+ branch, revnode = line.split()
+ branches[branch] = self.log(revrange=[revnode.split(':')[0]])[0]
+
+ return branches
+
+ def paths(self, name=None):
+ if not name:
+ out = self.outputruncommand(['paths'])[1]
+ if not out:
+ return {}
+
+ return dict([s.split(' = ') for s in out.rstrip().split('\n')])
+ else:
+ args = ['paths', name]
+ ret, out, err = self.outputruncommand(args, raiseonerror=False)
+ if ret:
+ raise error.CommandError(args, ret, out, err)
+ return out.rstrip()
+
+ def cat(self, files, rev=None, output=None):
+ args = ['cat']
+ if rev:
+ args += ['-r', rev]
+ if output:
+ args += ['-o', output]
+
+ args += files
+ ret, out, err = self.outputruncommand(args)
+
+ if not output:
+ return out
diff -r 0358762171c0 -r 3ab9a103cc27 hglib/hglib.py
--- a/hglib/hglib.py Sat Jul 23 22:55:39 2011 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,307 +0,0 @@
-import subprocess, os, struct, cStringIO, collections
-import error, util, templates
-
-HGPATH = 'hg'
-
-def connect(path=None, encoding=None, configs=None):
- ''' starts a cmdserver for the given path (or for a repository found in the
- cwd). HGENCODING is set to the given encoding. configs is a list of key, value,
- similar to those passed to hg --config. '''
- return hgclient(path, encoding, configs)
-
-class hgclient(object):
- inputfmt = '>I'
- outputfmt = '>cI'
- outputfmtsize = struct.calcsize(outputfmt)
- retfmt = '>i'
-
- revision = collections.namedtuple('revision', 'rev, node, tags, '
- 'branch, author, desc')
-
- def __init__(self, path, encoding, configs):
- args = [HGPATH, 'serve', '--cmdserver', 'pipe']
- if path:
- args += ['-R', path]
- if configs:
- args += ['--config'] + configs
- env = dict(os.environ)
- if encoding:
- env['HGENCODING'] = encoding
-
- self.server = subprocess.Popen(args, stdin=subprocess.PIPE,
- stdout=subprocess.PIPE, env=env)
-
- self._readhello()
- self._config = {}
-
- def _readhello(self):
- """ read the hello message the server sends when started """
- ch, msg = self._readchannel()
- assert ch == 'o'
-
- msg = msg.split('\n')
-
- self.capabilities = msg[0][len('capabilities: '):]
- if not self.capabilities:
- raise error.ResponseError("bad hello message: expected 'capabilities: '"
- ", got %r" % msg[0])
-
- self.capabilities = set(self.capabilities.split())
-
- # at the very least the server should be able to run commands
- assert 'runcommand' in self.capabilities
-
- self._encoding = msg[1][len('encoding: '):]
- if not self._encoding:
- raise error.ResponseError("bad hello message: expected 'encoding: '"
- ", got %r" % msg[1])
-
- def _readchannel(self):
- data = self.server.stdout.read(hgclient.outputfmtsize)
- if not data:
- raise error.ServerError()
- channel, length = struct.unpack(hgclient.outputfmt, data)
- if channel in 'IL':
- return channel, length
- else:
- return channel, self.server.stdout.read(length)
-
- def _parserevs(self, splitted):
- ''' splitted is a list of fields according to our rev.style, where each 6
- fields compose one revision. '''
- return [self.revision._make(rev) for rev in util.grouper(6, splitted)]
-
- def _eatlines(self, s, n):
- idx = 0
- for i in xrange(n):
- idx = s.find('\n', idx) + 1
-
- return s[idx:]
-
- def runcommand(self, args, inchannels, outchannels):
- def writeblock(data):
- self.server.stdin.write(struct.pack(self.inputfmt, len(data)))
- self.server.stdin.write(data)
- self.server.stdin.flush()
-
- if not self.server:
- raise ValueError("server not connected")
-
- self.server.stdin.write('runcommand\n')
- writeblock('\0'.join(args))
-
- while True:
- channel, data = self._readchannel()
-
- # input channels
- if channel in inchannels:
- writeblock(inchannels[channel](data))
- # output channels
- elif channel in outchannels:
- outchannels[channel](data)
- # result channel, command finished
- elif channel == 'r':
- return struct.unpack(hgclient.retfmt, data)[0]
- # a channel that we don't know and can't ignore
- elif channel.isupper():
- raise error.ResponseError("unexpected data on required channel '%s'"
- % channel)
- # optional channel
- else:
- pass
-
- def outputruncommand(self, args, inchannels = {}, raiseonerror=True):
- ''' run the command specified by args, returning (ret, output, error) '''
- out, err = cStringIO.StringIO(), cStringIO.StringIO()
- outchannels = {'o' : out.write, 'e' : err.write}
- ret = self.runcommand(args, inchannels, outchannels)
- if ret and raiseonerror:
- raise error.CommandError(args, ret, out.getvalue(), err.getvalue())
- return ret, out.getvalue(), err.getvalue()
-
- def close(self):
- self.server.stdin.close()
- self.server.wait()
- ret = self.server.returncode
- self.server = None
- return ret
-
- @property
- def encoding(self):
- """ get the servers encoding """
- if not 'getencoding' in self.capabilities:
- raise CapabilityError('getencoding')
-
- if not self._encoding:
- self.server.stdin.write('getencoding\n')
- self._encoding = self._readfromchannel('r')
-
- return self._encoding
-
- def config(self, refresh=False):
- if not self._config or refresh:
- self._config.clear()
-
- ret, out, err = self.outputruncommand(['showconfig'])
- if ret:
- raise error.CommandError(['showconfig'], ret, out, err)
-
- for entry in cStringIO.StringIO(out):
- k, v = entry.rstrip().split('=', 1)
- section, name = k.split('.', 1)
- self._config.setdefault(section, {})[name] = v
-
- return self._config
-
- def status(self):
- ret, out = self.outputruncommand(['status', '-0'])
-
- 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 log(self, revrange=None):
- args = ['log'] + templates.changeset
- if revrange:
- args.append('-r')
- args += revrange
-
- out = self.outputruncommand(args)[1]
- out = out.split('\0')[:-1]
-
- return self._parserevs(out)
-
- def incoming(self, revrange=None, path=None):
- args = ['incoming'] + templates.changeset
- if revrange:
- args.append('-r')
- args += revrange
-
- if path:
- args += [path]
-
- ret, out, err = self.outputruncommand(args, raiseonerror=False)
- if not ret:
- out = self._eatlines(out, 2).split('\0')[:-1]
- return self._parserevs(out)
- elif ret == 1:
- return []
- else:
- raise error.CommandError(args, ret, out, err)
-
- def outgoing(self, revrange=None, path=None):
- args = ['outgoing'] + templates.changeset
- if revrange:
- args.append('-r')
- args += revrange
-
- if path:
- args += [path]
-
- ret, out, err = self.outputruncommand(args, raiseonerror=False)
- if not ret:
- out = self._eatlines(out, 2).split('\0')[:-1]
- return self._parserevs(out)
- elif ret == 1:
- return []
- else:
- raise error.CommandError(args, ret, out, err)
-
- def commit(self, message, addremove=False):
- # --debug will print the committed cset
- args = ['commit', '--debug', '-m', message]
-
- if addremove:
- args += ['-A']
-
- out = self.outputruncommand(args)[1]
- rev = out.split(':')[1]
- return self.log(revrange=[rev])[0]
-
- def import_(self, patch):
- if isinstance(patch, str):
- fp = open(patch)
- else:
- assert hasattr(patch, 'read')
- assert hasattr(patch, 'readline')
-
- fp = patch
-
- try:
- inchannels = {'I' : fp.read, 'L' : fp.readline}
- self.outputruncommand(['import', '-'], inchannels)
- finally:
- if fp != patch:
- fp.close()
-
- def root(self):
- return self.outputruncommand(['root'])[1].rstrip()
-
- def clone(self, source='.', dest=None, branch=None, updaterev=None,
- revrange=None):
- args = ['clone']
-
- if branch:
- args += ['-b', branch]
- if updaterev:
- args += ['-u', updaterev]
- if revrange:
- args.append('-r')
- args += revrange
- args.append(source)
-
- if dest:
- args.append(dest)
-
- self.outputruncommand(args)
-
- def tip(self):
- out = self.outputruncommand(['tip'] + templates.changeset)[1]
- out = out.split('\0')
-
- return self._parserevs(out)[0]
-
- def branch(self, name=None):
- if not name:
- return self.outputruncommand(['branch'])[1].rstrip()
-
- def branches(self):
- out = self.outputruncommand(['branches'])[1]
- branches = {}
- for line in out.rstrip().split('\n'):
- branch, revnode = line.split()
- branches[branch] = self.log(revrange=[revnode.split(':')[0]])[0]
-
- return branches
-
- def paths(self, name=None):
- if not name:
- out = self.outputruncommand(['paths'])[1]
- if not out:
- return {}
-
- return dict([s.split(' = ') for s in out.rstrip().split('\n')])
- else:
- args = ['paths', name]
- ret, out, err = self.outputruncommand(args, raiseonerror=False)
- if ret:
- raise error.CommandError(args, ret, out, err)
- return out.rstrip()
-
- def cat(self, files, rev=None, output=None):
- args = ['cat']
- if rev:
- args += ['-r', rev]
- if output:
- args += ['-o', output]
-
- args += files
- ret, out, err = self.outputruncommand(args)
-
- if not output:
- return out
diff -r 0358762171c0 -r 3ab9a103cc27 tests/test-hglib.py
--- a/tests/test-hglib.py Sat Jul 23 22:55:39 2011 +0300
+++ b/tests/test-hglib.py Sat Jul 23 22:55:36 2011 +0300
@@ -4,9 +4,8 @@
import sys, os, subprocess, cStringIO, shutil, tempfile
-# XXX fix this hack
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../')
-from hglib import hglib
+import hglib
class test_hglib(unittest.TestCase):
def setUp(self):
@@ -14,7 +13,7 @@
os.chdir(self._tmpdir)
# until we can run norepo commands in the cmdserver
os.system('hg init')
- self.client = hglib.connect()
+ self.client = hglib.open()
def tearDown(self):
shutil.rmtree(self._tmpdir)
@@ -46,7 +45,7 @@
self.client.commit('second')
self.client.clone(dest='bar')
- bar = hglib.connect('bar')
+ bar = hglib.open('bar')
self.assertEquals(self.client.log(), bar.log())
self.assertEquals(self.client.outgoing(path='bar'), bar.incoming())
@@ -69,14 +68,14 @@
self.assertEquals(rev, branches[rev.branch])
def test_encoding(self):
- self.client = hglib.connect(encoding='utf-8')
+ self.client = hglib.open(encoding='utf-8')
self.assertEquals(self.client.encoding, 'utf-8')
def test_paths(self):
open('.hg/hgrc', 'a').write('[paths]\nfoo = bar\n')
# hgrc isn't watched for changes yet, have to reconnect
- self.client = hglib.connect()
+ self.client = hglib.open()
paths = self.client.paths()
self.assertEquals(len(paths), 1)
self.assertEquals(paths['foo'], os.path.abspath('bar'))
More information about the Mercurial-devel
mailing list