mercurial/peer.py
branchstable
changeset 37788 ed5448edcbfa
parent 37287 fb92df8b634c
parent 37787 92213f6745ed
child 37789 bfd32db06952
equal deleted inserted replaced
37287:fb92df8b634c 37788:ed5448edcbfa
     1 # peer.py - repository base classes for mercurial
       
     2 #
       
     3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
       
     4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
       
     5 #
       
     6 # This software may be used and distributed according to the terms of the
       
     7 # GNU General Public License version 2 or any later version.
       
     8 
       
     9 from __future__ import absolute_import
       
    10 
       
    11 from . import (
       
    12     error,
       
    13     pycompat,
       
    14     util,
       
    15 )
       
    16 
       
    17 # abstract batching support
       
    18 
       
    19 class future(object):
       
    20     '''placeholder for a value to be set later'''
       
    21     def set(self, value):
       
    22         if util.safehasattr(self, 'value'):
       
    23             raise error.RepoError("future is already set")
       
    24         self.value = value
       
    25 
       
    26 class batcher(object):
       
    27     '''base class for batches of commands submittable in a single request
       
    28 
       
    29     All methods invoked on instances of this class are simply queued and
       
    30     return a a future for the result. Once you call submit(), all the queued
       
    31     calls are performed and the results set in their respective futures.
       
    32     '''
       
    33     def __init__(self):
       
    34         self.calls = []
       
    35     def __getattr__(self, name):
       
    36         def call(*args, **opts):
       
    37             resref = future()
       
    38             # Please don't invent non-ascii method names, or you will
       
    39             # give core hg a very sad time.
       
    40             self.calls.append((name.encode('ascii'), args, opts, resref,))
       
    41             return resref
       
    42         return call
       
    43     def submit(self):
       
    44         raise NotImplementedError()
       
    45 
       
    46 class iterbatcher(batcher):
       
    47 
       
    48     def submit(self):
       
    49         raise NotImplementedError()
       
    50 
       
    51     def results(self):
       
    52         raise NotImplementedError()
       
    53 
       
    54 class localiterbatcher(iterbatcher):
       
    55     def __init__(self, local):
       
    56         super(iterbatcher, self).__init__()
       
    57         self.local = local
       
    58 
       
    59     def submit(self):
       
    60         # submit for a local iter batcher is a noop
       
    61         pass
       
    62 
       
    63     def results(self):
       
    64         for name, args, opts, resref in self.calls:
       
    65             resref.set(getattr(self.local, name)(*args, **opts))
       
    66             yield resref.value
       
    67 
       
    68 def batchable(f):
       
    69     '''annotation for batchable methods
       
    70 
       
    71     Such methods must implement a coroutine as follows:
       
    72 
       
    73     @batchable
       
    74     def sample(self, one, two=None):
       
    75         # Build list of encoded arguments suitable for your wire protocol:
       
    76         encargs = [('one', encode(one),), ('two', encode(two),)]
       
    77         # Create future for injection of encoded result:
       
    78         encresref = future()
       
    79         # Return encoded arguments and future:
       
    80         yield encargs, encresref
       
    81         # Assuming the future to be filled with the result from the batched
       
    82         # request now. Decode it:
       
    83         yield decode(encresref.value)
       
    84 
       
    85     The decorator returns a function which wraps this coroutine as a plain
       
    86     method, but adds the original method as an attribute called "batchable",
       
    87     which is used by remotebatch to split the call into separate encoding and
       
    88     decoding phases.
       
    89     '''
       
    90     def plain(*args, **opts):
       
    91         batchable = f(*args, **opts)
       
    92         encargsorres, encresref = next(batchable)
       
    93         if not encresref:
       
    94             return encargsorres # a local result in this case
       
    95         self = args[0]
       
    96         cmd = pycompat.bytesurl(f.__name__)  # ensure cmd is ascii bytestr
       
    97         encresref.set(self._submitone(cmd, encargsorres))
       
    98         return next(batchable)
       
    99     setattr(plain, 'batchable', f)
       
   100     return plain