tests/test-batching.py
changeset 41360 b81ca9a3f4e4
parent 37633 33a6eee08db2
child 43076 2372284d9457
equal deleted inserted replaced
41359:5361f9ed8a30 41360:b81ca9a3f4e4
     9 
     9 
    10 import contextlib
    10 import contextlib
    11 
    11 
    12 from mercurial import (
    12 from mercurial import (
    13     localrepo,
    13     localrepo,
       
    14     pycompat,
    14     wireprotov1peer,
    15     wireprotov1peer,
       
    16 )
    15 
    17 
    16 )
    18 def bprint(*bs):
       
    19     print(*[pycompat.sysstr(b) for b in bs])
    17 
    20 
    18 # equivalent of repo.repository
    21 # equivalent of repo.repository
    19 class thing(object):
    22 class thing(object):
    20     def hello(self):
    23     def hello(self):
    21         return "Ready."
    24         return b"Ready."
    22 
    25 
    23 # equivalent of localrepo.localrepository
    26 # equivalent of localrepo.localrepository
    24 class localthing(thing):
    27 class localthing(thing):
    25     def foo(self, one, two=None):
    28     def foo(self, one, two=None):
    26         if one:
    29         if one:
    27             return "%s and %s" % (one, two,)
    30             return b"%s and %s" % (one, two,)
    28         return "Nope"
    31         return b"Nope"
    29     def bar(self, b, a):
    32     def bar(self, b, a):
    30         return "%s und %s" % (b, a,)
    33         return b"%s und %s" % (b, a,)
    31     def greet(self, name=None):
    34     def greet(self, name=None):
    32         return "Hello, %s" % name
    35         return b"Hello, %s" % name
    33 
    36 
    34     @contextlib.contextmanager
    37     @contextlib.contextmanager
    35     def commandexecutor(self):
    38     def commandexecutor(self):
    36         e = localrepo.localcommandexecutor(self)
    39         e = localrepo.localcommandexecutor(self)
    37         try:
    40         try:
    41 
    44 
    42 # usage of "thing" interface
    45 # usage of "thing" interface
    43 def use(it):
    46 def use(it):
    44 
    47 
    45     # Direct call to base method shared between client and server.
    48     # Direct call to base method shared between client and server.
    46     print(it.hello())
    49     bprint(it.hello())
    47 
    50 
    48     # Direct calls to proxied methods. They cause individual roundtrips.
    51     # Direct calls to proxied methods. They cause individual roundtrips.
    49     print(it.foo("Un", two="Deux"))
    52     bprint(it.foo(b"Un", two=b"Deux"))
    50     print(it.bar("Eins", "Zwei"))
    53     bprint(it.bar(b"Eins", b"Zwei"))
    51 
    54 
    52     # Batched call to a couple of proxied methods.
    55     # Batched call to a couple of proxied methods.
    53 
    56 
    54     with it.commandexecutor() as e:
    57     with it.commandexecutor() as e:
    55         ffoo = e.callcommand('foo', {'one': 'One', 'two': 'Two'})
    58         ffoo = e.callcommand(b'foo', {b'one': b'One', b'two': b'Two'})
    56         fbar = e.callcommand('bar', {'b': 'Eins', 'a': 'Zwei'})
    59         fbar = e.callcommand(b'bar', {b'b': b'Eins', b'a': b'Zwei'})
    57         fbar2 = e.callcommand('bar', {'b': 'Uno', 'a': 'Due'})
    60         fbar2 = e.callcommand(b'bar', {b'b': b'Uno', b'a': b'Due'})
    58 
    61 
    59     print(ffoo.result())
    62     bprint(ffoo.result())
    60     print(fbar.result())
    63     bprint(fbar.result())
    61     print(fbar2.result())
    64     bprint(fbar2.result())
    62 
    65 
    63 # local usage
    66 # local usage
    64 mylocal = localthing()
    67 mylocal = localthing()
    65 print()
    68 print()
    66 print("== Local")
    69 bprint(b"== Local")
    67 use(mylocal)
    70 use(mylocal)
    68 
    71 
    69 # demo remoting; mimicks what wireproto and HTTP/SSH do
    72 # demo remoting; mimicks what wireproto and HTTP/SSH do
    70 
    73 
    71 # shared
    74 # shared
    72 
    75 
    73 def escapearg(plain):
    76 def escapearg(plain):
    74     return (plain
    77     return (plain
    75             .replace(':', '::')
    78             .replace(b':', b'::')
    76             .replace(',', ':,')
    79             .replace(b',', b':,')
    77             .replace(';', ':;')
    80             .replace(b';', b':;')
    78             .replace('=', ':='))
    81             .replace(b'=', b':='))
    79 def unescapearg(escaped):
    82 def unescapearg(escaped):
    80     return (escaped
    83     return (escaped
    81             .replace(':=', '=')
    84             .replace(b':=', b'=')
    82             .replace(':;', ';')
    85             .replace(b':;', b';')
    83             .replace(':,', ',')
    86             .replace(b':,', b',')
    84             .replace('::', ':'))
    87             .replace(b'::', b':'))
    85 
    88 
    86 # server side
    89 # server side
    87 
    90 
    88 # equivalent of wireproto's global functions
    91 # equivalent of wireproto's global functions
    89 class server(object):
    92 class server(object):
    90     def __init__(self, local):
    93     def __init__(self, local):
    91         self.local = local
    94         self.local = local
    92     def _call(self, name, args):
    95     def _call(self, name, args):
    93         args = dict(arg.split('=', 1) for arg in args)
    96         args = dict(arg.split(b'=', 1) for arg in args)
    94         return getattr(self, name)(**args)
    97         return getattr(self, name)(**args)
    95     def perform(self, req):
    98     def perform(self, req):
    96         print("REQ:", req)
    99         bprint(b"REQ:", req)
    97         name, args = req.split('?', 1)
   100         name, args = req.split(b'?', 1)
    98         args = args.split('&')
   101         args = args.split(b'&')
    99         vals = dict(arg.split('=', 1) for arg in args)
   102         vals = dict(arg.split(b'=', 1) for arg in args)
   100         res = getattr(self, name)(**vals)
   103         res = getattr(self, pycompat.sysstr(name))(**pycompat.strkwargs(vals))
   101         print("  ->", res)
   104         bprint(b"  ->", res)
   102         return res
   105         return res
   103     def batch(self, cmds):
   106     def batch(self, cmds):
   104         res = []
   107         res = []
   105         for pair in cmds.split(';'):
   108         for pair in cmds.split(b';'):
   106             name, args = pair.split(':', 1)
   109             name, args = pair.split(b':', 1)
   107             vals = {}
   110             vals = {}
   108             for a in args.split(','):
   111             for a in args.split(b','):
   109                 if a:
   112                 if a:
   110                     n, v = a.split('=')
   113                     n, v = a.split(b'=')
   111                     vals[n] = unescapearg(v)
   114                     vals[n] = unescapearg(v)
   112             res.append(escapearg(getattr(self, name)(**vals)))
   115             res.append(escapearg(getattr(self, pycompat.sysstr(name))(
   113         return ';'.join(res)
   116                 **pycompat.strkwargs(vals))))
       
   117         return b';'.join(res)
   114     def foo(self, one, two):
   118     def foo(self, one, two):
   115         return mangle(self.local.foo(unmangle(one), unmangle(two)))
   119         return mangle(self.local.foo(unmangle(one), unmangle(two)))
   116     def bar(self, b, a):
   120     def bar(self, b, a):
   117         return mangle(self.local.bar(unmangle(b), unmangle(a)))
   121         return mangle(self.local.bar(unmangle(b), unmangle(a)))
   118     def greet(self, name):
   122     def greet(self, name):
   122 # local side
   126 # local side
   123 
   127 
   124 # equivalent of wireproto.encode/decodelist, that is, type-specific marshalling
   128 # equivalent of wireproto.encode/decodelist, that is, type-specific marshalling
   125 # here we just transform the strings a bit to check we're properly en-/decoding
   129 # here we just transform the strings a bit to check we're properly en-/decoding
   126 def mangle(s):
   130 def mangle(s):
   127     return ''.join(chr(ord(c) + 1) for c in s)
   131     return b''.join(pycompat.bytechr(ord(c) + 1) for c in pycompat.bytestr(s))
   128 def unmangle(s):
   132 def unmangle(s):
   129     return ''.join(chr(ord(c) - 1) for c in s)
   133     return b''.join(pycompat.bytechr(ord(c) - 1) for c in pycompat.bytestr(s))
   130 
   134 
   131 # equivalent of wireproto.wirerepository and something like http's wire format
   135 # equivalent of wireproto.wirerepository and something like http's wire format
   132 class remotething(thing):
   136 class remotething(thing):
   133     def __init__(self, server):
   137     def __init__(self, server):
   134         self.server = server
   138         self.server = server
   135     def _submitone(self, name, args):
   139     def _submitone(self, name, args):
   136         req = name + '?' + '&'.join(['%s=%s' % (n, v) for n, v in args])
   140         req = name + b'?' + b'&'.join([b'%s=%s' % (n, v) for n, v in args])
   137         return self.server.perform(req)
   141         return self.server.perform(req)
   138     def _submitbatch(self, cmds):
   142     def _submitbatch(self, cmds):
   139         req = []
   143         req = []
   140         for name, args in cmds:
   144         for name, args in cmds:
   141             args = ','.join(n + '=' + escapearg(v) for n, v in args)
   145             args = b','.join(n + b'=' + escapearg(v) for n, v in args)
   142             req.append(name + ':' + args)
   146             req.append(name + b':' + args)
   143         req = ';'.join(req)
   147         req = b';'.join(req)
   144         res = self._submitone('batch', [('cmds', req,)])
   148         res = self._submitone(b'batch', [(b'cmds', req,)])
   145         for r in res.split(';'):
   149         for r in res.split(b';'):
   146             yield r
   150             yield r
   147 
   151 
   148     @contextlib.contextmanager
   152     @contextlib.contextmanager
   149     def commandexecutor(self):
   153     def commandexecutor(self):
   150         e = wireprotov1peer.peerexecutor(self)
   154         e = wireprotov1peer.peerexecutor(self)
   153         finally:
   157         finally:
   154             e.close()
   158             e.close()
   155 
   159 
   156     @wireprotov1peer.batchable
   160     @wireprotov1peer.batchable
   157     def foo(self, one, two=None):
   161     def foo(self, one, two=None):
   158         encargs = [('one', mangle(one),), ('two', mangle(two),)]
   162         encargs = [(b'one', mangle(one),), (b'two', mangle(two),)]
   159         encresref = wireprotov1peer.future()
   163         encresref = wireprotov1peer.future()
   160         yield encargs, encresref
   164         yield encargs, encresref
   161         yield unmangle(encresref.value)
   165         yield unmangle(encresref.value)
   162 
   166 
   163     @wireprotov1peer.batchable
   167     @wireprotov1peer.batchable
   164     def bar(self, b, a):
   168     def bar(self, b, a):
   165         encresref = wireprotov1peer.future()
   169         encresref = wireprotov1peer.future()
   166         yield [('b', mangle(b),), ('a', mangle(a),)], encresref
   170         yield [(b'b', mangle(b),), (b'a', mangle(a),)], encresref
   167         yield unmangle(encresref.value)
   171         yield unmangle(encresref.value)
   168 
   172 
   169     # greet is coded directly. It therefore does not support batching. If it
   173     # greet is coded directly. It therefore does not support batching. If it
   170     # does appear in a batch, the batch is split around greet, and the call to
   174     # does appear in a batch, the batch is split around greet, and the call to
   171     # greet is done in its own roundtrip.
   175     # greet is done in its own roundtrip.
   172     def greet(self, name=None):
   176     def greet(self, name=None):
   173         return unmangle(self._submitone('greet', [('name', mangle(name),)]))
   177         return unmangle(self._submitone(b'greet', [(b'name', mangle(name),)]))
   174 
   178 
   175 # demo remote usage
   179 # demo remote usage
   176 
   180 
   177 myproxy = remotething(myserver)
   181 myproxy = remotething(myserver)
   178 print()
   182 print()
   179 print("== Remote")
   183 bprint(b"== Remote")
   180 use(myproxy)
   184 use(myproxy)