test-batching.py
258 lines
| 6.3 KiB
| text/x-python
|
PythonLexer
/ tests / test-batching.py
Peter Arrenbrecht
|
r14621 | # test-batching.py - tests for transparent command batching | ||
# | ||||
# Copyright 2011 Peter Arrenbrecht <peter@arrenbrecht.ch> | ||||
# | ||||
# This software may be used and distributed according to the terms of the | ||||
# GNU General Public License version 2 or any later version. | ||||
Robert Stanca
|
r28732 | from __future__ import absolute_import, print_function | ||
Yuya Nishihara
|
r28800 | |||
Gregory Szorc
|
r37651 | import contextlib | ||
Yuya Nishihara
|
r28800 | from mercurial import ( | ||
Gregory Szorc
|
r37633 | localrepo, | ||
Augie Fackler
|
r41366 | pycompat, | ||
Gregory Szorc
|
r37632 | wireprotov1peer, | ||
Augie Fackler
|
r41366 | ) | ||
Gregory Szorc
|
r37633 | |||
Augie Fackler
|
r43346 | |||
Augie Fackler
|
r41366 | def bprint(*bs): | ||
print(*[pycompat.sysstr(b) for b in bs]) | ||||
Peter Arrenbrecht
|
r14621 | |||
Augie Fackler
|
r43346 | |||
Peter Arrenbrecht
|
r14621 | # equivalent of repo.repository | ||
class thing(object): | ||||
def hello(self): | ||||
Augie Fackler
|
r41366 | return b"Ready." | ||
Peter Arrenbrecht
|
r14621 | |||
Augie Fackler
|
r43346 | |||
Peter Arrenbrecht
|
r14621 | # equivalent of localrepo.localrepository | ||
class localthing(thing): | ||||
def foo(self, one, two=None): | ||||
if one: | ||||
Augie Fackler
|
r46554 | return b"%s and %s" % ( | ||
one, | ||||
two, | ||||
) | ||||
Augie Fackler
|
r41366 | return b"Nope" | ||
Augie Fackler
|
r43346 | |||
Peter Arrenbrecht
|
r14621 | def bar(self, b, a): | ||
Augie Fackler
|
r46554 | return b"%s und %s" % ( | ||
b, | ||||
a, | ||||
) | ||||
Augie Fackler
|
r43346 | |||
Peter Arrenbrecht
|
r14621 | def greet(self, name=None): | ||
Augie Fackler
|
r41366 | return b"Hello, %s" % name | ||
Gregory Szorc
|
r37651 | |||
@contextlib.contextmanager | ||||
def commandexecutor(self): | ||||
e = localrepo.localcommandexecutor(self) | ||||
try: | ||||
yield e | ||||
finally: | ||||
e.close() | ||||
Peter Arrenbrecht
|
r14621 | |||
Augie Fackler
|
r43346 | |||
Peter Arrenbrecht
|
r14621 | # usage of "thing" interface | ||
def use(it): | ||||
# Direct call to base method shared between client and server. | ||||
Augie Fackler
|
r41366 | bprint(it.hello()) | ||
Peter Arrenbrecht
|
r14621 | |||
# Direct calls to proxied methods. They cause individual roundtrips. | ||||
Augie Fackler
|
r41366 | bprint(it.foo(b"Un", two=b"Deux")) | ||
bprint(it.bar(b"Eins", b"Zwei")) | ||||
Peter Arrenbrecht
|
r14621 | |||
Gregory Szorc
|
r33761 | # Batched call to a couple of proxied methods. | ||
Gregory Szorc
|
r37651 | with it.commandexecutor() as e: | ||
Augie Fackler
|
r41366 | ffoo = e.callcommand(b'foo', {b'one': b'One', b'two': b'Two'}) | ||
fbar = e.callcommand(b'bar', {b'b': b'Eins', b'a': b'Zwei'}) | ||||
fbar2 = e.callcommand(b'bar', {b'b': b'Uno', b'a': b'Due'}) | ||||
Peter Arrenbrecht
|
r14621 | |||
Augie Fackler
|
r41366 | bprint(ffoo.result()) | ||
bprint(fbar.result()) | ||||
bprint(fbar2.result()) | ||||
Gregory Szorc
|
r33761 | |||
Augie Fackler
|
r43346 | |||
Peter Arrenbrecht
|
r14621 | # local usage | ||
mylocal = localthing() | ||||
Robert Stanca
|
r28732 | print() | ||
Augie Fackler
|
r41366 | bprint(b"== Local") | ||
Peter Arrenbrecht
|
r14621 | use(mylocal) | ||
# demo remoting; mimicks what wireproto and HTTP/SSH do | ||||
# shared | ||||
Augie Fackler
|
r43346 | |||
Peter Arrenbrecht
|
r14621 | def escapearg(plain): | ||
Augie Fackler
|
r43346 | return ( | ||
plain.replace(b':', b'::') | ||||
.replace(b',', b':,') | ||||
.replace(b';', b':;') | ||||
.replace(b'=', b':=') | ||||
) | ||||
Peter Arrenbrecht
|
r14621 | def unescapearg(escaped): | ||
Augie Fackler
|
r43346 | return ( | ||
escaped.replace(b':=', b'=') | ||||
.replace(b':;', b';') | ||||
.replace(b':,', b',') | ||||
.replace(b'::', b':') | ||||
) | ||||
Peter Arrenbrecht
|
r14621 | |||
# server side | ||||
# equivalent of wireproto's global functions | ||||
Thomas Arendsen Hein
|
r14764 | class server(object): | ||
Peter Arrenbrecht
|
r14621 | def __init__(self, local): | ||
self.local = local | ||||
Augie Fackler
|
r43346 | |||
Peter Arrenbrecht
|
r14621 | def _call(self, name, args): | ||
Augie Fackler
|
r41366 | args = dict(arg.split(b'=', 1) for arg in args) | ||
Peter Arrenbrecht
|
r14621 | return getattr(self, name)(**args) | ||
Augie Fackler
|
r43346 | |||
Peter Arrenbrecht
|
r14621 | def perform(self, req): | ||
Augie Fackler
|
r41366 | bprint(b"REQ:", req) | ||
name, args = req.split(b'?', 1) | ||||
args = args.split(b'&') | ||||
vals = dict(arg.split(b'=', 1) for arg in args) | ||||
res = getattr(self, pycompat.sysstr(name))(**pycompat.strkwargs(vals)) | ||||
bprint(b" ->", res) | ||||
Peter Arrenbrecht
|
r14621 | return res | ||
Augie Fackler
|
r43346 | |||
Peter Arrenbrecht
|
r14621 | def batch(self, cmds): | ||
res = [] | ||||
Augie Fackler
|
r41366 | for pair in cmds.split(b';'): | ||
name, args = pair.split(b':', 1) | ||||
Peter Arrenbrecht
|
r14621 | vals = {} | ||
Augie Fackler
|
r41366 | for a in args.split(b','): | ||
Peter Arrenbrecht
|
r14621 | if a: | ||
Augie Fackler
|
r41366 | n, v = a.split(b'=') | ||
Peter Arrenbrecht
|
r14621 | vals[n] = unescapearg(v) | ||
Augie Fackler
|
r43346 | res.append( | ||
escapearg( | ||||
getattr(self, pycompat.sysstr(name))( | ||||
**pycompat.strkwargs(vals) | ||||
) | ||||
) | ||||
) | ||||
Augie Fackler
|
r41366 | return b';'.join(res) | ||
Augie Fackler
|
r43346 | |||
Peter Arrenbrecht
|
r14621 | def foo(self, one, two): | ||
return mangle(self.local.foo(unmangle(one), unmangle(two))) | ||||
Augie Fackler
|
r43346 | |||
Peter Arrenbrecht
|
r14621 | def bar(self, b, a): | ||
return mangle(self.local.bar(unmangle(b), unmangle(a))) | ||||
Augie Fackler
|
r43346 | |||
Peter Arrenbrecht
|
r14621 | def greet(self, name): | ||
return mangle(self.local.greet(unmangle(name))) | ||||
Augie Fackler
|
r43346 | |||
Peter Arrenbrecht
|
r14621 | myserver = server(mylocal) | ||
# local side | ||||
# equivalent of wireproto.encode/decodelist, that is, type-specific marshalling | ||||
# here we just transform the strings a bit to check we're properly en-/decoding | ||||
def mangle(s): | ||||
Augie Fackler
|
r41366 | return b''.join(pycompat.bytechr(ord(c) + 1) for c in pycompat.bytestr(s)) | ||
Augie Fackler
|
r43346 | |||
Peter Arrenbrecht
|
r14621 | def unmangle(s): | ||
Augie Fackler
|
r41366 | return b''.join(pycompat.bytechr(ord(c) - 1) for c in pycompat.bytestr(s)) | ||
Peter Arrenbrecht
|
r14621 | |||
Augie Fackler
|
r43346 | |||
Peter Arrenbrecht
|
r14621 | # equivalent of wireproto.wirerepository and something like http's wire format | ||
class remotething(thing): | ||||
def __init__(self, server): | ||||
self.server = server | ||||
Augie Fackler
|
r43346 | |||
Peter Arrenbrecht
|
r14621 | def _submitone(self, name, args): | ||
Augie Fackler
|
r41366 | req = name + b'?' + b'&'.join([b'%s=%s' % (n, v) for n, v in args]) | ||
Peter Arrenbrecht
|
r14621 | return self.server.perform(req) | ||
Augie Fackler
|
r43346 | |||
Peter Arrenbrecht
|
r14621 | def _submitbatch(self, cmds): | ||
req = [] | ||||
for name, args in cmds: | ||||
Augie Fackler
|
r41366 | args = b','.join(n + b'=' + escapearg(v) for n, v in args) | ||
req.append(name + b':' + args) | ||||
req = b';'.join(req) | ||||
Augie Fackler
|
r46554 | res = self._submitone( | ||
b'batch', | ||||
[ | ||||
( | ||||
b'cmds', | ||||
req, | ||||
) | ||||
], | ||||
) | ||||
Augie Fackler
|
r41366 | for r in res.split(b';'): | ||
Gregory Szorc
|
r33761 | yield r | ||
Peter Arrenbrecht
|
r14621 | |||
Gregory Szorc
|
r37651 | @contextlib.contextmanager | ||
def commandexecutor(self): | ||||
e = wireprotov1peer.peerexecutor(self) | ||||
try: | ||||
yield e | ||||
finally: | ||||
e.close() | ||||
Peter Arrenbrecht
|
r14621 | |||
Gregory Szorc
|
r37633 | @wireprotov1peer.batchable | ||
Peter Arrenbrecht
|
r14621 | def foo(self, one, two=None): | ||
Martin von Zweigbergk
|
r47209 | encoded_args = [ | ||
Augie Fackler
|
r46554 | ( | ||
b'one', | ||||
mangle(one), | ||||
), | ||||
( | ||||
b'two', | ||||
mangle(two), | ||||
), | ||||
] | ||||
Martin von Zweigbergk
|
r47209 | encoded_res_future = wireprotov1peer.future() | ||
yield encoded_args, encoded_res_future | ||||
yield unmangle(encoded_res_future.value) | ||||
Peter Arrenbrecht
|
r14621 | |||
Gregory Szorc
|
r37633 | @wireprotov1peer.batchable | ||
Peter Arrenbrecht
|
r14621 | def bar(self, b, a): | ||
Gregory Szorc
|
r37633 | encresref = wireprotov1peer.future() | ||
Augie Fackler
|
r46554 | yield [ | ||
( | ||||
b'b', | ||||
mangle(b), | ||||
), | ||||
( | ||||
b'a', | ||||
mangle(a), | ||||
), | ||||
], encresref | ||||
Peter Arrenbrecht
|
r14621 | yield unmangle(encresref.value) | ||
# greet is coded directly. It therefore does not support batching. If it | ||||
# does appear in a batch, the batch is split around greet, and the call to | ||||
# greet is done in its own roundtrip. | ||||
def greet(self, name=None): | ||||
Augie Fackler
|
r46554 | return unmangle( | ||
self._submitone( | ||||
b'greet', | ||||
[ | ||||
( | ||||
b'name', | ||||
mangle(name), | ||||
) | ||||
], | ||||
) | ||||
) | ||||
Peter Arrenbrecht
|
r14621 | |||
Augie Fackler
|
r43346 | |||
Peter Arrenbrecht
|
r14621 | # demo remote usage | ||
myproxy = remotething(myserver) | ||||
Robert Stanca
|
r28732 | print() | ||
Augie Fackler
|
r41366 | bprint(b"== Remote") | ||
Peter Arrenbrecht
|
r14621 | use(myproxy) | ||