9 |
9 |
10 import contextlib |
10 import contextlib |
11 |
11 |
12 from mercurial import ( |
12 from mercurial import ( |
13 localrepo, |
13 localrepo, |
|
14 pycompat, |
14 wireprotov1peer, |
15 wireprotov1peer, |
|
16 ) |
15 |
17 |
16 ) |
18 def bprint(*bs): |
|
19 print(*[pycompat.sysstr(b) for b in bs]) |
17 |
20 |
18 # equivalent of repo.repository |
21 # equivalent of repo.repository |
19 class thing(object): |
22 class thing(object): |
20 def hello(self): |
23 def hello(self): |
21 return "Ready." |
24 return b"Ready." |
22 |
25 |
23 # equivalent of localrepo.localrepository |
26 # equivalent of localrepo.localrepository |
24 class localthing(thing): |
27 class localthing(thing): |
25 def foo(self, one, two=None): |
28 def foo(self, one, two=None): |
26 if one: |
29 if one: |
27 return "%s and %s" % (one, two,) |
30 return b"%s and %s" % (one, two,) |
28 return "Nope" |
31 return b"Nope" |
29 def bar(self, b, a): |
32 def bar(self, b, a): |
30 return "%s und %s" % (b, a,) |
33 return b"%s und %s" % (b, a,) |
31 def greet(self, name=None): |
34 def greet(self, name=None): |
32 return "Hello, %s" % name |
35 return b"Hello, %s" % name |
33 |
36 |
34 @contextlib.contextmanager |
37 @contextlib.contextmanager |
35 def commandexecutor(self): |
38 def commandexecutor(self): |
36 e = localrepo.localcommandexecutor(self) |
39 e = localrepo.localcommandexecutor(self) |
37 try: |
40 try: |
41 |
44 |
42 # usage of "thing" interface |
45 # usage of "thing" interface |
43 def use(it): |
46 def use(it): |
44 |
47 |
45 # Direct call to base method shared between client and server. |
48 # Direct call to base method shared between client and server. |
46 print(it.hello()) |
49 bprint(it.hello()) |
47 |
50 |
48 # Direct calls to proxied methods. They cause individual roundtrips. |
51 # Direct calls to proxied methods. They cause individual roundtrips. |
49 print(it.foo("Un", two="Deux")) |
52 bprint(it.foo(b"Un", two=b"Deux")) |
50 print(it.bar("Eins", "Zwei")) |
53 bprint(it.bar(b"Eins", b"Zwei")) |
51 |
54 |
52 # Batched call to a couple of proxied methods. |
55 # Batched call to a couple of proxied methods. |
53 |
56 |
54 with it.commandexecutor() as e: |
57 with it.commandexecutor() as e: |
55 ffoo = e.callcommand('foo', {'one': 'One', 'two': 'Two'}) |
58 ffoo = e.callcommand(b'foo', {b'one': b'One', b'two': b'Two'}) |
56 fbar = e.callcommand('bar', {'b': 'Eins', 'a': 'Zwei'}) |
59 fbar = e.callcommand(b'bar', {b'b': b'Eins', b'a': b'Zwei'}) |
57 fbar2 = e.callcommand('bar', {'b': 'Uno', 'a': 'Due'}) |
60 fbar2 = e.callcommand(b'bar', {b'b': b'Uno', b'a': b'Due'}) |
58 |
61 |
59 print(ffoo.result()) |
62 bprint(ffoo.result()) |
60 print(fbar.result()) |
63 bprint(fbar.result()) |
61 print(fbar2.result()) |
64 bprint(fbar2.result()) |
62 |
65 |
63 # local usage |
66 # local usage |
64 mylocal = localthing() |
67 mylocal = localthing() |
65 print() |
68 print() |
66 print("== Local") |
69 bprint(b"== Local") |
67 use(mylocal) |
70 use(mylocal) |
68 |
71 |
69 # demo remoting; mimicks what wireproto and HTTP/SSH do |
72 # demo remoting; mimicks what wireproto and HTTP/SSH do |
70 |
73 |
71 # shared |
74 # shared |
72 |
75 |
73 def escapearg(plain): |
76 def escapearg(plain): |
74 return (plain |
77 return (plain |
75 .replace(':', '::') |
78 .replace(b':', b'::') |
76 .replace(',', ':,') |
79 .replace(b',', b':,') |
77 .replace(';', ':;') |
80 .replace(b';', b':;') |
78 .replace('=', ':=')) |
81 .replace(b'=', b':=')) |
79 def unescapearg(escaped): |
82 def unescapearg(escaped): |
80 return (escaped |
83 return (escaped |
81 .replace(':=', '=') |
84 .replace(b':=', b'=') |
82 .replace(':;', ';') |
85 .replace(b':;', b';') |
83 .replace(':,', ',') |
86 .replace(b':,', b',') |
84 .replace('::', ':')) |
87 .replace(b'::', b':')) |
85 |
88 |
86 # server side |
89 # server side |
87 |
90 |
88 # equivalent of wireproto's global functions |
91 # equivalent of wireproto's global functions |
89 class server(object): |
92 class server(object): |
90 def __init__(self, local): |
93 def __init__(self, local): |
91 self.local = local |
94 self.local = local |
92 def _call(self, name, args): |
95 def _call(self, name, args): |
93 args = dict(arg.split('=', 1) for arg in args) |
96 args = dict(arg.split(b'=', 1) for arg in args) |
94 return getattr(self, name)(**args) |
97 return getattr(self, name)(**args) |
95 def perform(self, req): |
98 def perform(self, req): |
96 print("REQ:", req) |
99 bprint(b"REQ:", req) |
97 name, args = req.split('?', 1) |
100 name, args = req.split(b'?', 1) |
98 args = args.split('&') |
101 args = args.split(b'&') |
99 vals = dict(arg.split('=', 1) for arg in args) |
102 vals = dict(arg.split(b'=', 1) for arg in args) |
100 res = getattr(self, name)(**vals) |
103 res = getattr(self, pycompat.sysstr(name))(**pycompat.strkwargs(vals)) |
101 print(" ->", res) |
104 bprint(b" ->", res) |
102 return res |
105 return res |
103 def batch(self, cmds): |
106 def batch(self, cmds): |
104 res = [] |
107 res = [] |
105 for pair in cmds.split(';'): |
108 for pair in cmds.split(b';'): |
106 name, args = pair.split(':', 1) |
109 name, args = pair.split(b':', 1) |
107 vals = {} |
110 vals = {} |
108 for a in args.split(','): |
111 for a in args.split(b','): |
109 if a: |
112 if a: |
110 n, v = a.split('=') |
113 n, v = a.split(b'=') |
111 vals[n] = unescapearg(v) |
114 vals[n] = unescapearg(v) |
112 res.append(escapearg(getattr(self, name)(**vals))) |
115 res.append(escapearg(getattr(self, pycompat.sysstr(name))( |
113 return ';'.join(res) |
116 **pycompat.strkwargs(vals)))) |
|
117 return b';'.join(res) |
114 def foo(self, one, two): |
118 def foo(self, one, two): |
115 return mangle(self.local.foo(unmangle(one), unmangle(two))) |
119 return mangle(self.local.foo(unmangle(one), unmangle(two))) |
116 def bar(self, b, a): |
120 def bar(self, b, a): |
117 return mangle(self.local.bar(unmangle(b), unmangle(a))) |
121 return mangle(self.local.bar(unmangle(b), unmangle(a))) |
118 def greet(self, name): |
122 def greet(self, name): |
122 # local side |
126 # local side |
123 |
127 |
124 # equivalent of wireproto.encode/decodelist, that is, type-specific marshalling |
128 # equivalent of wireproto.encode/decodelist, that is, type-specific marshalling |
125 # 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 |
126 def mangle(s): |
130 def mangle(s): |
127 return ''.join(chr(ord(c) + 1) for c in s) |
131 return b''.join(pycompat.bytechr(ord(c) + 1) for c in pycompat.bytestr(s)) |
128 def unmangle(s): |
132 def unmangle(s): |
129 return ''.join(chr(ord(c) - 1) for c in s) |
133 return b''.join(pycompat.bytechr(ord(c) - 1) for c in pycompat.bytestr(s)) |
130 |
134 |
131 # equivalent of wireproto.wirerepository and something like http's wire format |
135 # equivalent of wireproto.wirerepository and something like http's wire format |
132 class remotething(thing): |
136 class remotething(thing): |
133 def __init__(self, server): |
137 def __init__(self, server): |
134 self.server = server |
138 self.server = server |
135 def _submitone(self, name, args): |
139 def _submitone(self, name, args): |
136 req = name + '?' + '&'.join(['%s=%s' % (n, v) for n, v in args]) |
140 req = name + b'?' + b'&'.join([b'%s=%s' % (n, v) for n, v in args]) |
137 return self.server.perform(req) |
141 return self.server.perform(req) |
138 def _submitbatch(self, cmds): |
142 def _submitbatch(self, cmds): |
139 req = [] |
143 req = [] |
140 for name, args in cmds: |
144 for name, args in cmds: |
141 args = ','.join(n + '=' + escapearg(v) for n, v in args) |
145 args = b','.join(n + b'=' + escapearg(v) for n, v in args) |
142 req.append(name + ':' + args) |
146 req.append(name + b':' + args) |
143 req = ';'.join(req) |
147 req = b';'.join(req) |
144 res = self._submitone('batch', [('cmds', req,)]) |
148 res = self._submitone(b'batch', [(b'cmds', req,)]) |
145 for r in res.split(';'): |
149 for r in res.split(b';'): |
146 yield r |
150 yield r |
147 |
151 |
148 @contextlib.contextmanager |
152 @contextlib.contextmanager |
149 def commandexecutor(self): |
153 def commandexecutor(self): |
150 e = wireprotov1peer.peerexecutor(self) |
154 e = wireprotov1peer.peerexecutor(self) |
153 finally: |
157 finally: |
154 e.close() |
158 e.close() |
155 |
159 |
156 @wireprotov1peer.batchable |
160 @wireprotov1peer.batchable |
157 def foo(self, one, two=None): |
161 def foo(self, one, two=None): |
158 encargs = [('one', mangle(one),), ('two', mangle(two),)] |
162 encargs = [(b'one', mangle(one),), (b'two', mangle(two),)] |
159 encresref = wireprotov1peer.future() |
163 encresref = wireprotov1peer.future() |
160 yield encargs, encresref |
164 yield encargs, encresref |
161 yield unmangle(encresref.value) |
165 yield unmangle(encresref.value) |
162 |
166 |
163 @wireprotov1peer.batchable |
167 @wireprotov1peer.batchable |
164 def bar(self, b, a): |
168 def bar(self, b, a): |
165 encresref = wireprotov1peer.future() |
169 encresref = wireprotov1peer.future() |
166 yield [('b', mangle(b),), ('a', mangle(a),)], encresref |
170 yield [(b'b', mangle(b),), (b'a', mangle(a),)], encresref |
167 yield unmangle(encresref.value) |
171 yield unmangle(encresref.value) |
168 |
172 |
169 # greet is coded directly. It therefore does not support batching. If it |
173 # greet is coded directly. It therefore does not support batching. If it |
170 # does appear in a batch, the batch is split around greet, and the call to |
174 # does appear in a batch, the batch is split around greet, and the call to |
171 # greet is done in its own roundtrip. |
175 # greet is done in its own roundtrip. |
172 def greet(self, name=None): |
176 def greet(self, name=None): |
173 return unmangle(self._submitone('greet', [('name', mangle(name),)])) |
177 return unmangle(self._submitone(b'greet', [(b'name', mangle(name),)])) |
174 |
178 |
175 # demo remote usage |
179 # demo remote usage |
176 |
180 |
177 myproxy = remotething(myserver) |
181 myproxy = remotething(myserver) |
178 print() |
182 print() |
179 print("== Remote") |
183 bprint(b"== Remote") |
180 use(myproxy) |
184 use(myproxy) |