peer.py
98 lines
| 3.1 KiB
| text/x-python
|
PythonLexer
/ mercurial / peer.py
|
r17192 | # peer.py - repository base classes for mercurial | ||
# | ||||
# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com> | ||||
# Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com> | ||||
# | ||||
# This software may be used and distributed according to the terms of the | ||||
# GNU General Public License version 2 or any later version. | ||||
|
r25965 | from __future__ import absolute_import | ||
from . import ( | ||||
error, | ||||
util, | ||||
) | ||||
|
r25912 | |||
# abstract batching support | ||||
class future(object): | ||||
'''placeholder for a value to be set later''' | ||||
def set(self, value): | ||||
if util.safehasattr(self, 'value'): | ||||
raise error.RepoError("future is already set") | ||||
self.value = value | ||||
class batcher(object): | ||||
'''base class for batches of commands submittable in a single request | ||||
All methods invoked on instances of this class are simply queued and | ||||
return a a future for the result. Once you call submit(), all the queued | ||||
calls are performed and the results set in their respective futures. | ||||
''' | ||||
def __init__(self): | ||||
self.calls = [] | ||||
def __getattr__(self, name): | ||||
def call(*args, **opts): | ||||
resref = future() | ||||
|
r34728 | # Please don't invent non-ascii method names, or you will | ||
# give core hg a very sad time. | ||||
self.calls.append((name.encode('ascii'), args, opts, resref,)) | ||||
|
r25912 | return resref | ||
return call | ||||
def submit(self): | ||||
|
r28434 | raise NotImplementedError() | ||
|
r25912 | |||
|
r28436 | class iterbatcher(batcher): | ||
def submit(self): | ||||
raise NotImplementedError() | ||||
def results(self): | ||||
raise NotImplementedError() | ||||
class localiterbatcher(iterbatcher): | ||||
def __init__(self, local): | ||||
super(iterbatcher, self).__init__() | ||||
self.local = local | ||||
def submit(self): | ||||
# submit for a local iter batcher is a noop | ||||
pass | ||||
def results(self): | ||||
for name, args, opts, resref in self.calls: | ||||
|
r33761 | resref.set(getattr(self.local, name)(*args, **opts)) | ||
yield resref.value | ||||
|
r28436 | |||
|
r25912 | def batchable(f): | ||
'''annotation for batchable methods | ||||
Such methods must implement a coroutine as follows: | ||||
@batchable | ||||
def sample(self, one, two=None): | ||||
# Build list of encoded arguments suitable for your wire protocol: | ||||
encargs = [('one', encode(one),), ('two', encode(two),)] | ||||
# Create future for injection of encoded result: | ||||
encresref = future() | ||||
# Return encoded arguments and future: | ||||
yield encargs, encresref | ||||
# Assuming the future to be filled with the result from the batched | ||||
# request now. Decode it: | ||||
yield decode(encresref.value) | ||||
The decorator returns a function which wraps this coroutine as a plain | ||||
method, but adds the original method as an attribute called "batchable", | ||||
which is used by remotebatch to split the call into separate encoding and | ||||
decoding phases. | ||||
''' | ||||
def plain(*args, **opts): | ||||
batchable = f(*args, **opts) | ||||
|
r29216 | encargsorres, encresref = next(batchable) | ||
|
r25912 | if not encresref: | ||
return encargsorres # a local result in this case | ||||
self = args[0] | ||||
|
r34694 | encresref.set(self._submitone(f.__name__, encargsorres)) | ||
|
r29216 | return next(batchable) | ||
|
r25912 | setattr(plain, 'batchable', f) | ||
return plain | ||||