peer.py
155 lines
| 4.7 KiB
| text/x-python
|
PythonLexer
/ mercurial / peer.py
Peter Arrenbrecht
|
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. | ||||
Gregory Szorc
|
r25965 | from __future__ import absolute_import | ||
from .i18n import _ | ||||
from . import ( | ||||
error, | ||||
util, | ||||
) | ||||
Augie Fackler
|
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() | ||||
self.calls.append((name, args, opts, resref,)) | ||||
return resref | ||||
return call | ||||
def submit(self): | ||||
Augie Fackler
|
r28434 | raise NotImplementedError() | ||
Augie Fackler
|
r25912 | |||
Augie Fackler
|
r28436 | class iterbatcher(batcher): | ||
def submit(self): | ||||
raise NotImplementedError() | ||||
def results(self): | ||||
raise NotImplementedError() | ||||
Augie Fackler
|
r25912 | class localbatch(batcher): | ||
'''performs the queued calls directly''' | ||||
def __init__(self, local): | ||||
batcher.__init__(self) | ||||
self.local = local | ||||
def submit(self): | ||||
for name, args, opts, resref in self.calls: | ||||
resref.set(getattr(self.local, name)(*args, **opts)) | ||||
Augie Fackler
|
r28436 | 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: | ||||
yield getattr(self.local, name)(*args, **opts) | ||||
Augie Fackler
|
r25912 | def batchable(f): | ||
'''annotation for batchable methods | ||||
Such methods must implement a coroutine as follows: | ||||
@batchable | ||||
def sample(self, one, two=None): | ||||
# Handle locally computable results first: | ||||
if not one: | ||||
yield "a local result", 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) | ||||
timeless
|
r29216 | encargsorres, encresref = next(batchable) | ||
Augie Fackler
|
r25912 | if not encresref: | ||
return encargsorres # a local result in this case | ||||
self = args[0] | ||||
encresref.set(self._submitone(f.func_name, encargsorres)) | ||||
timeless
|
r29216 | return next(batchable) | ||
Augie Fackler
|
r25912 | setattr(plain, 'batchable', f) | ||
return plain | ||||
Peter Arrenbrecht
|
r17192 | |||
class peerrepository(object): | ||||
Augie Fackler
|
r25912 | def batch(self): | ||
return localbatch(self) | ||||
Augie Fackler
|
r28436 | def iterbatch(self): | ||
"""Batch requests but allow iterating over the results. | ||||
This is to allow interleaving responses with things like | ||||
progress updates for clients. | ||||
""" | ||||
return localiterbatcher(self) | ||||
Peter Arrenbrecht
|
r17192 | def capable(self, name): | ||
'''tell whether repo supports named capability. | ||||
return False if not supported. | ||||
if boolean capability, return True. | ||||
if string capability, return string.''' | ||||
caps = self._capabilities() | ||||
if name in caps: | ||||
return True | ||||
name_eq = name + '=' | ||||
for cap in caps: | ||||
if cap.startswith(name_eq): | ||||
return cap[len(name_eq):] | ||||
return False | ||||
def requirecap(self, name, purpose): | ||||
'''raise an exception if the given capability is not present''' | ||||
if not self.capable(name): | ||||
raise error.CapabilityError( | ||||
_('cannot %s; remote repository does not ' | ||||
'support the %r capability') % (purpose, name)) | ||||
def local(self): | ||||
'''return peer as a localrepo, or None''' | ||||
return None | ||||
def peer(self): | ||||
return self | ||||
Sune Foldager
|
r17193 | def canpush(self): | ||
return True | ||||
Peter Arrenbrecht
|
r17192 | def close(self): | ||
pass | ||||