mercurial/wireprotov1peer.py
changeset 37615 f3dc8239e3a9
parent 37614 a81d02ea65db
child 37630 e1b32dc4646c
--- a/mercurial/wireprotov1peer.py	Wed Apr 11 12:49:08 2018 -0700
+++ b/mercurial/wireprotov1peer.py	Wed Apr 11 12:51:09 2018 -0700
@@ -19,7 +19,6 @@
     changegroup as changegroupmod,
     encoding,
     error,
-    peer,
     pushkey as pushkeymod,
     pycompat,
     repository,
@@ -29,7 +28,76 @@
 
 urlreq = util.urlreq
 
-class remoteiterbatcher(peer.iterbatcher):
+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)
+        encargsorres, encresref = next(batchable)
+        if not encresref:
+            return encargsorres # a local result in this case
+        self = args[0]
+        cmd = pycompat.bytesurl(f.__name__)  # ensure cmd is ascii bytestr
+        encresref.set(self._submitone(cmd, encargsorres))
+        return next(batchable)
+    setattr(plain, 'batchable', f)
+    return plain
+
+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()
+            # 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,))
+            return resref
+        return call
+    def submit(self):
+        raise NotImplementedError()
+
+class iterbatcher(batcher):
+
+    def submit(self):
+        raise NotImplementedError()
+
+    def results(self):
+        raise NotImplementedError()
+
+class remoteiterbatcher(iterbatcher):
     def __init__(self, remote):
         super(remoteiterbatcher, self).__init__()
         self._remote = remote
@@ -92,11 +160,6 @@
 
             yield finalfuture.value
 
-# Forward a couple of names from peer to make wireproto interactions
-# slightly more sensible.
-batchable = peer.batchable
-future = peer.future
-
 def encodebatchcmds(req):
     """Return a ``cmds`` argument value for the ``batch`` command."""
     escapearg = wireprototypes.escapebatcharg