# HG changeset patch # User Peter Arrenbrecht # Date 2011-06-14 20:52:58 # Node ID bd88561afb4b41c61d323bfff5ccb9d4a71791b8 # Parent 84094c0d27245b6e0522c9cc29a71bbc25b857b0 wireproto: add batching support to wirerepository Adds the plumbing and wire call for batched execution, but does not batch-enable any methods yet. diff --git a/mercurial/wireproto.py b/mercurial/wireproto.py --- a/mercurial/wireproto.py +++ b/mercurial/wireproto.py @@ -126,9 +126,41 @@ def decodelist(l, sep=' '): 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 @@ def options(cmd, keys, others): % (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 branches(repo, proto, nodes): 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 @@ def unbundle(repo, proto, heads): os.unlink(tempname) commands = { + 'batch': (batch, 'cmds *'), 'between': (between, 'pairs'), 'branchmap': (branchmap, ''), 'branches': (branches, 'nodes'), diff --git a/tests/test-hgweb-commands.t b/tests/test-hgweb-commands.t --- a/tests/test-hgweb-commands.t +++ b/tests/test-hgweb-commands.t @@ -981,7 +981,7 @@ capabilities $ "$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 diff --git a/tests/test-wireprotocol.py b/tests/test-wireprotocol.py new file mode 100644 --- /dev/null +++ b/tests/test-wireprotocol.py @@ -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] diff --git a/tests/test-wireprotocol.py.out b/tests/test-wireprotocol.py.out new file mode 100644 --- /dev/null +++ b/tests/test-wireprotocol.py.out @@ -0,0 +1,2 @@ +Hello, Foobar +['Hello, Fo, =;o', 'Hello, Bar']