##// END OF EJS Templates
test-batching: stop direct symbol import of mercurial modules...
Yuya Nishihara -
r28800:544908ae default
parent child Browse files
Show More
@@ -1,183 +1,180 b''
1 # test-batching.py - tests for transparent command batching
1 # test-batching.py - tests for transparent command batching
2 #
2 #
3 # Copyright 2011 Peter Arrenbrecht <peter@arrenbrecht.ch>
3 # Copyright 2011 Peter Arrenbrecht <peter@arrenbrecht.ch>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import, print_function
8 from __future__ import absolute_import, print_function
9 from mercurial.peer import (
9
10 localbatch,
10 from mercurial import (
11 batchable,
11 peer,
12 future,
12 wireproto,
13 )
14 from mercurial.wireproto import (
15 remotebatch,
16 )
13 )
17
14
18 # equivalent of repo.repository
15 # equivalent of repo.repository
19 class thing(object):
16 class thing(object):
20 def hello(self):
17 def hello(self):
21 return "Ready."
18 return "Ready."
22
19
23 # equivalent of localrepo.localrepository
20 # equivalent of localrepo.localrepository
24 class localthing(thing):
21 class localthing(thing):
25 def foo(self, one, two=None):
22 def foo(self, one, two=None):
26 if one:
23 if one:
27 return "%s and %s" % (one, two,)
24 return "%s and %s" % (one, two,)
28 return "Nope"
25 return "Nope"
29 def bar(self, b, a):
26 def bar(self, b, a):
30 return "%s und %s" % (b, a,)
27 return "%s und %s" % (b, a,)
31 def greet(self, name=None):
28 def greet(self, name=None):
32 return "Hello, %s" % name
29 return "Hello, %s" % name
33 def batch(self):
30 def batch(self):
34 '''Support for local batching.'''
31 '''Support for local batching.'''
35 return localbatch(self)
32 return peer.localbatch(self)
36
33
37 # usage of "thing" interface
34 # usage of "thing" interface
38 def use(it):
35 def use(it):
39
36
40 # Direct call to base method shared between client and server.
37 # Direct call to base method shared between client and server.
41 print(it.hello())
38 print(it.hello())
42
39
43 # Direct calls to proxied methods. They cause individual roundtrips.
40 # Direct calls to proxied methods. They cause individual roundtrips.
44 print(it.foo("Un", two="Deux"))
41 print(it.foo("Un", two="Deux"))
45 print(it.bar("Eins", "Zwei"))
42 print(it.bar("Eins", "Zwei"))
46
43
47 # Batched call to a couple of (possibly proxied) methods.
44 # Batched call to a couple of (possibly proxied) methods.
48 batch = it.batch()
45 batch = it.batch()
49 # The calls return futures to eventually hold results.
46 # The calls return futures to eventually hold results.
50 foo = batch.foo(one="One", two="Two")
47 foo = batch.foo(one="One", two="Two")
51 foo2 = batch.foo(None)
48 foo2 = batch.foo(None)
52 bar = batch.bar("Eins", "Zwei")
49 bar = batch.bar("Eins", "Zwei")
53 # We can call non-batchable proxy methods, but the break the current batch
50 # We can call non-batchable proxy methods, but the break the current batch
54 # request and cause additional roundtrips.
51 # request and cause additional roundtrips.
55 greet = batch.greet(name="John Smith")
52 greet = batch.greet(name="John Smith")
56 # We can also add local methods into the mix, but they break the batch too.
53 # We can also add local methods into the mix, but they break the batch too.
57 hello = batch.hello()
54 hello = batch.hello()
58 bar2 = batch.bar(b="Uno", a="Due")
55 bar2 = batch.bar(b="Uno", a="Due")
59 # Only now are all the calls executed in sequence, with as few roundtrips
56 # Only now are all the calls executed in sequence, with as few roundtrips
60 # as possible.
57 # as possible.
61 batch.submit()
58 batch.submit()
62 # After the call to submit, the futures actually contain values.
59 # After the call to submit, the futures actually contain values.
63 print(foo.value)
60 print(foo.value)
64 print(foo2.value)
61 print(foo2.value)
65 print(bar.value)
62 print(bar.value)
66 print(greet.value)
63 print(greet.value)
67 print(hello.value)
64 print(hello.value)
68 print(bar2.value)
65 print(bar2.value)
69
66
70 # local usage
67 # local usage
71 mylocal = localthing()
68 mylocal = localthing()
72 print()
69 print()
73 print("== Local")
70 print("== Local")
74 use(mylocal)
71 use(mylocal)
75
72
76 # demo remoting; mimicks what wireproto and HTTP/SSH do
73 # demo remoting; mimicks what wireproto and HTTP/SSH do
77
74
78 # shared
75 # shared
79
76
80 def escapearg(plain):
77 def escapearg(plain):
81 return (plain
78 return (plain
82 .replace(':', '::')
79 .replace(':', '::')
83 .replace(',', ':,')
80 .replace(',', ':,')
84 .replace(';', ':;')
81 .replace(';', ':;')
85 .replace('=', ':='))
82 .replace('=', ':='))
86 def unescapearg(escaped):
83 def unescapearg(escaped):
87 return (escaped
84 return (escaped
88 .replace(':=', '=')
85 .replace(':=', '=')
89 .replace(':;', ';')
86 .replace(':;', ';')
90 .replace(':,', ',')
87 .replace(':,', ',')
91 .replace('::', ':'))
88 .replace('::', ':'))
92
89
93 # server side
90 # server side
94
91
95 # equivalent of wireproto's global functions
92 # equivalent of wireproto's global functions
96 class server(object):
93 class server(object):
97 def __init__(self, local):
94 def __init__(self, local):
98 self.local = local
95 self.local = local
99 def _call(self, name, args):
96 def _call(self, name, args):
100 args = dict(arg.split('=', 1) for arg in args)
97 args = dict(arg.split('=', 1) for arg in args)
101 return getattr(self, name)(**args)
98 return getattr(self, name)(**args)
102 def perform(self, req):
99 def perform(self, req):
103 print("REQ:", req)
100 print("REQ:", req)
104 name, args = req.split('?', 1)
101 name, args = req.split('?', 1)
105 args = args.split('&')
102 args = args.split('&')
106 vals = dict(arg.split('=', 1) for arg in args)
103 vals = dict(arg.split('=', 1) for arg in args)
107 res = getattr(self, name)(**vals)
104 res = getattr(self, name)(**vals)
108 print(" ->", res)
105 print(" ->", res)
109 return res
106 return res
110 def batch(self, cmds):
107 def batch(self, cmds):
111 res = []
108 res = []
112 for pair in cmds.split(';'):
109 for pair in cmds.split(';'):
113 name, args = pair.split(':', 1)
110 name, args = pair.split(':', 1)
114 vals = {}
111 vals = {}
115 for a in args.split(','):
112 for a in args.split(','):
116 if a:
113 if a:
117 n, v = a.split('=')
114 n, v = a.split('=')
118 vals[n] = unescapearg(v)
115 vals[n] = unescapearg(v)
119 res.append(escapearg(getattr(self, name)(**vals)))
116 res.append(escapearg(getattr(self, name)(**vals)))
120 return ';'.join(res)
117 return ';'.join(res)
121 def foo(self, one, two):
118 def foo(self, one, two):
122 return mangle(self.local.foo(unmangle(one), unmangle(two)))
119 return mangle(self.local.foo(unmangle(one), unmangle(two)))
123 def bar(self, b, a):
120 def bar(self, b, a):
124 return mangle(self.local.bar(unmangle(b), unmangle(a)))
121 return mangle(self.local.bar(unmangle(b), unmangle(a)))
125 def greet(self, name):
122 def greet(self, name):
126 return mangle(self.local.greet(unmangle(name)))
123 return mangle(self.local.greet(unmangle(name)))
127 myserver = server(mylocal)
124 myserver = server(mylocal)
128
125
129 # local side
126 # local side
130
127
131 # equivalent of wireproto.encode/decodelist, that is, type-specific marshalling
128 # equivalent of wireproto.encode/decodelist, that is, type-specific marshalling
132 # here we just transform the strings a bit to check we're properly en-/decoding
129 # here we just transform the strings a bit to check we're properly en-/decoding
133 def mangle(s):
130 def mangle(s):
134 return ''.join(chr(ord(c) + 1) for c in s)
131 return ''.join(chr(ord(c) + 1) for c in s)
135 def unmangle(s):
132 def unmangle(s):
136 return ''.join(chr(ord(c) - 1) for c in s)
133 return ''.join(chr(ord(c) - 1) for c in s)
137
134
138 # equivalent of wireproto.wirerepository and something like http's wire format
135 # equivalent of wireproto.wirerepository and something like http's wire format
139 class remotething(thing):
136 class remotething(thing):
140 def __init__(self, server):
137 def __init__(self, server):
141 self.server = server
138 self.server = server
142 def _submitone(self, name, args):
139 def _submitone(self, name, args):
143 req = name + '?' + '&'.join(['%s=%s' % (n, v) for n, v in args])
140 req = name + '?' + '&'.join(['%s=%s' % (n, v) for n, v in args])
144 return self.server.perform(req)
141 return self.server.perform(req)
145 def _submitbatch(self, cmds):
142 def _submitbatch(self, cmds):
146 req = []
143 req = []
147 for name, args in cmds:
144 for name, args in cmds:
148 args = ','.join(n + '=' + escapearg(v) for n, v in args)
145 args = ','.join(n + '=' + escapearg(v) for n, v in args)
149 req.append(name + ':' + args)
146 req.append(name + ':' + args)
150 req = ';'.join(req)
147 req = ';'.join(req)
151 res = self._submitone('batch', [('cmds', req,)])
148 res = self._submitone('batch', [('cmds', req,)])
152 return res.split(';')
149 return res.split(';')
153
150
154 def batch(self):
151 def batch(self):
155 return remotebatch(self)
152 return wireproto.remotebatch(self)
156
153
157 @batchable
154 @peer.batchable
158 def foo(self, one, two=None):
155 def foo(self, one, two=None):
159 if not one:
156 if not one:
160 yield "Nope", None
157 yield "Nope", None
161 encargs = [('one', mangle(one),), ('two', mangle(two),)]
158 encargs = [('one', mangle(one),), ('two', mangle(two),)]
162 encresref = future()
159 encresref = peer.future()
163 yield encargs, encresref
160 yield encargs, encresref
164 yield unmangle(encresref.value)
161 yield unmangle(encresref.value)
165
162
166 @batchable
163 @peer.batchable
167 def bar(self, b, a):
164 def bar(self, b, a):
168 encresref = future()
165 encresref = peer.future()
169 yield [('b', mangle(b),), ('a', mangle(a),)], encresref
166 yield [('b', mangle(b),), ('a', mangle(a),)], encresref
170 yield unmangle(encresref.value)
167 yield unmangle(encresref.value)
171
168
172 # greet is coded directly. It therefore does not support batching. If it
169 # greet is coded directly. It therefore does not support batching. If it
173 # does appear in a batch, the batch is split around greet, and the call to
170 # does appear in a batch, the batch is split around greet, and the call to
174 # greet is done in its own roundtrip.
171 # greet is done in its own roundtrip.
175 def greet(self, name=None):
172 def greet(self, name=None):
176 return unmangle(self._submitone('greet', [('name', mangle(name),)]))
173 return unmangle(self._submitone('greet', [('name', mangle(name),)]))
177
174
178 # demo remote usage
175 # demo remote usage
179
176
180 myproxy = remotething(myserver)
177 myproxy = remotething(myserver)
181 print()
178 print()
182 print("== Remote")
179 print("== Remote")
183 use(myproxy)
180 use(myproxy)
General Comments 0
You need to be logged in to leave comments. Login now