wireproto: add batching support to wirerepository
Adds the plumbing and wire call for batched execution, but does not
batch-enable any methods yet.
--- a/mercurial/wireproto.py Tue Jun 14 22:51:26 2011 +0200
+++ b/mercurial/wireproto.py Tue Jun 14 22:52:58 2011 +0200
@@ -126,9 +126,41 @@
def encodelist(l, sep=' '):
return sep.join(map(hex, l))
+# batched call argument encoding
+
+def escapearg(plain):
+ return (plain
+ .replace(':', '::')
+ .replace(',', ':,')
+ .replace(';', ':;')
+ .replace('=', ':='))
+
+def unescapearg(escaped):
+ return (escaped
+ .replace(':=', '=')
+ .replace(':;', ';')
+ .replace(':,', ',')
+ .replace('::', ':'))
+
# client side
+def todict(**args):
+ return args
+
class wirerepository(repo.repository):
+
+ def batch(self):
+ return remotebatch(self)
+ def _submitbatch(self, req):
+ cmds = []
+ for op, argsdict in req:
+ args = ','.join('%s=%s' % p for p in argsdict.iteritems())
+ cmds.append('%s %s' % (op, args))
+ rsp = self._call("batch", cmds=';'.join(cmds))
+ return rsp.split(';')
+ def _submitone(self, op, args):
+ return self._call(op, **args)
+
def lookup(self, key):
self.requirecap('lookup', _('look up remote revision'))
d = self._call("lookup", key=encoding.fromlocal(key))
@@ -302,6 +334,34 @@
% (cmd, ",".join(others)))
return opts
+def batch(repo, proto, cmds, others):
+ res = []
+ for pair in cmds.split(';'):
+ op, args = pair.split(' ', 1)
+ vals = {}
+ for a in args.split(','):
+ if a:
+ n, v = a.split('=')
+ vals[n] = unescapearg(v)
+ func, spec = commands[op]
+ if spec:
+ keys = spec.split()
+ data = {}
+ for k in keys:
+ if k == '*':
+ star = {}
+ for key in vals.keys():
+ if key not in keys:
+ star[key] = vals[key]
+ data['*'] = star
+ else:
+ data[k] = vals[k]
+ result = func(repo, proto, *[data[k] for k in keys])
+ else:
+ result = func(repo, proto)
+ res.append(escapearg(result))
+ return ';'.join(res)
+
def between(repo, proto, pairs):
pairs = [decodelist(p, '-') for p in pairs.split(" ")]
r = []
@@ -327,7 +387,7 @@
def capabilities(repo, proto):
caps = ('lookup changegroupsubset branchmap pushkey known getbundle '
- 'unbundlehash').split()
+ 'unbundlehash batch').split()
if _allowstream(repo.ui):
requiredformats = repo.requirements & repo.supportedformats
# if our local revlogs are just revlogv1, add 'stream' cap
@@ -506,6 +566,7 @@
os.unlink(tempname)
commands = {
+ 'batch': (batch, 'cmds *'),
'between': (between, 'pairs'),
'branchmap': (branchmap, ''),
'branches': (branches, 'nodes'),
--- a/tests/test-hgweb-commands.t Tue Jun 14 22:51:26 2011 +0200
+++ b/tests/test-hgweb-commands.t Tue Jun 14 22:52:58 2011 +0200
@@ -981,7 +981,7 @@
$ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=capabilities'; echo
200 Script output follows
- lookup changegroupsubset branchmap pushkey known getbundle unbundlehash unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024
+ lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024
heads
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-wireprotocol.py Tue Jun 14 22:52:58 2011 +0200
@@ -0,0 +1,45 @@
+from mercurial import wireproto
+
+class proto():
+ def __init__(self, args):
+ self.args = args
+ def getargs(self, spec):
+ args = self.args
+ args.setdefault('*', {})
+ names = spec.split()
+ return [args[n] for n in names]
+
+class clientrepo(wireproto.wirerepository):
+ def __init__(self, serverrepo):
+ self.serverrepo = serverrepo
+ def _call(self, cmd, **args):
+ return wireproto.dispatch(self.serverrepo, proto(args), cmd)
+
+ @wireproto.batchable
+ def greet(self, name):
+ f = wireproto.future()
+ yield wireproto.todict(name=mangle(name)), f
+ yield unmangle(f.value)
+
+class serverrepo():
+ def greet(self, name):
+ return "Hello, " + name
+
+def mangle(s):
+ return ''.join(chr(ord(c) + 1) for c in s)
+def unmangle(s):
+ return ''.join(chr(ord(c) - 1) for c in s)
+
+def greet(repo, proto, name):
+ return mangle(repo.greet(unmangle(name)))
+
+wireproto.commands['greet'] = (greet, 'name',)
+
+srv = serverrepo()
+clt = clientrepo(srv)
+
+print clt.greet("Foobar")
+b = clt.batch()
+fs = [b.greet(s) for s in ["Fo, =;o", "Bar"]]
+b.submit()
+print [f.value for f in fs]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-wireprotocol.py.out Tue Jun 14 22:52:58 2011 +0200
@@ -0,0 +1,2 @@
+Hello, Foobar
+['Hello, Fo, =;o', 'Hello, Bar']