175 for k, v in argsdict.iteritems()) |
178 for k, v in argsdict.iteritems()) |
176 cmds.append('%s %s' % (op, args)) |
179 cmds.append('%s %s' % (op, args)) |
177 |
180 |
178 return ';'.join(cmds) |
181 return ';'.join(cmds) |
179 |
182 |
|
183 @zi.implementer(repository.ipeercommandexecutor) |
|
184 class peerexecutor(object): |
|
185 def __init__(self, peer): |
|
186 self._peer = peer |
|
187 self._sent = False |
|
188 self._closed = False |
|
189 self._calls = [] |
|
190 |
|
191 def __enter__(self): |
|
192 return self |
|
193 |
|
194 def __exit__(self, exctype, excvalee, exctb): |
|
195 self.close() |
|
196 |
|
197 def callcommand(self, command, args): |
|
198 if self._sent: |
|
199 raise error.ProgrammingError('callcommand() cannot be used ' |
|
200 'after commands are sent') |
|
201 |
|
202 if self._closed: |
|
203 raise error.ProgrammingError('callcommand() cannot be used ' |
|
204 'after close()') |
|
205 |
|
206 # Commands are dispatched through methods on the peer. |
|
207 fn = getattr(self._peer, pycompat.sysstr(command), None) |
|
208 |
|
209 if not fn: |
|
210 raise error.ProgrammingError( |
|
211 'cannot call command %s: method of same name not available ' |
|
212 'on peer' % command) |
|
213 |
|
214 # Commands are either batchable or they aren't. If a command |
|
215 # isn't batchable, we send it immediately because the executor |
|
216 # can no longer accept new commands after a non-batchable command. |
|
217 # If a command is batchable, we queue it for later. |
|
218 |
|
219 if getattr(fn, 'batchable', False): |
|
220 pass |
|
221 else: |
|
222 if self._calls: |
|
223 raise error.ProgrammingError( |
|
224 '%s is not batchable and cannot be called on a command ' |
|
225 'executor along with other commands' % command) |
|
226 |
|
227 # We don't support batching yet. So resolve it immediately. |
|
228 f = pycompat.futures.Future() |
|
229 self._calls.append((command, args, fn, f)) |
|
230 self.sendcommands() |
|
231 return f |
|
232 |
|
233 def sendcommands(self): |
|
234 if self._sent: |
|
235 return |
|
236 |
|
237 if not self._calls: |
|
238 return |
|
239 |
|
240 self._sent = True |
|
241 |
|
242 calls = self._calls |
|
243 # Mainly to destroy references to futures. |
|
244 self._calls = None |
|
245 |
|
246 if len(calls) == 1: |
|
247 command, args, fn, f = calls[0] |
|
248 |
|
249 # Future was cancelled. Ignore it. |
|
250 if not f.set_running_or_notify_cancel(): |
|
251 return |
|
252 |
|
253 try: |
|
254 result = fn(**pycompat.strkwargs(args)) |
|
255 except Exception: |
|
256 f.set_exception_info(*sys.exc_info()[1:]) |
|
257 else: |
|
258 f.set_result(result) |
|
259 |
|
260 return |
|
261 |
|
262 raise error.ProgrammingError('support for multiple commands not ' |
|
263 'yet implemented') |
|
264 |
|
265 def close(self): |
|
266 self.sendcommands() |
|
267 |
|
268 self._closed = True |
|
269 |
180 class wirepeer(repository.legacypeer): |
270 class wirepeer(repository.legacypeer): |
181 """Client-side interface for communicating with a peer repository. |
271 """Client-side interface for communicating with a peer repository. |
182 |
272 |
183 Methods commonly call wire protocol commands of the same name. |
273 Methods commonly call wire protocol commands of the same name. |
184 |
274 |
185 See also httppeer.py and sshpeer.py for protocol-specific |
275 See also httppeer.py and sshpeer.py for protocol-specific |
186 implementations of this interface. |
276 implementations of this interface. |
187 """ |
277 """ |
|
278 def commandexecutor(self): |
|
279 return peerexecutor(self) |
|
280 |
188 # Begin of ipeercommands interface. |
281 # Begin of ipeercommands interface. |
189 |
282 |
190 def iterbatch(self): |
283 def iterbatch(self): |
191 return remoteiterbatcher(self) |
284 return remoteiterbatcher(self) |
192 |
285 |