D3271: wireproto: remove iterbatch() from peer interface (API)

indygreg (Gregory Szorc) phabricator at mercurial-scm.org
Fri Apr 13 15:15:29 EDT 2018


indygreg updated this revision to Diff 8124.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D3271?vs=8037&id=8124

REVISION DETAIL
  https://phab.mercurial-scm.org/D3271

AFFECTED FILES
  mercurial/localrepo.py
  mercurial/repository.py
  mercurial/wireprotov1peer.py
  tests/test-batching.py
  tests/test-batching.py.out
  tests/test-wireproto.py

CHANGE DETAILS

diff --git a/tests/test-wireproto.py b/tests/test-wireproto.py
--- a/tests/test-wireproto.py
+++ b/tests/test-wireproto.py
@@ -93,7 +93,9 @@
 clt = clientpeer(srv, uimod.ui())
 
 print(clt.greet(b"Foobar"))
-b = clt.iterbatch()
-list(map(b.greet, (b'Fo, =;:<o', b'Bar')))
-b.submit()
-print([r for r in b.results()])
+
+with clt.commandexecutor() as e:
+    fgreet1 = e.callcommand(b'greet', {b'name': b'Fo, =;:<o'})
+    fgreet2 = e.callcommand(b'greet', {b'name': b'Bar'})
+
+print([f.result() for f in (fgreet1, fgreet2)])
diff --git a/tests/test-batching.py.out b/tests/test-batching.py.out
--- a/tests/test-batching.py.out
+++ b/tests/test-batching.py.out
@@ -6,7 +6,6 @@
 One and Two
 Eins und Zwei
 Uno und Due
-proper end of results generator
 
 == Remote
 Ready.
@@ -21,6 +20,3 @@
 One and Two
 Eins und Zwei
 Uno und Due
-proper end of results generator
-Attempted to batch a non-batchable call to 'greet'
-Attempted to batch a non-batchable call to 'hello'
diff --git a/tests/test-batching.py b/tests/test-batching.py
--- a/tests/test-batching.py
+++ b/tests/test-batching.py
@@ -7,10 +7,10 @@
 
 from __future__ import absolute_import, print_function
 
+import contextlib
+
 from mercurial import (
-    error,
     localrepo,
-    util,
     wireprotov1peer,
 
 )
@@ -30,9 +30,14 @@
         return "%s und %s" % (b, a,)
     def greet(self, name=None):
         return "Hello, %s" % name
-    def batchiter(self):
-        '''Support for local batching.'''
-        return localrepo.localiterbatcher(self)
+
+    @contextlib.contextmanager
+    def commandexecutor(self):
+        e = localrepo.localcommandexecutor(self)
+        try:
+            yield e
+        finally:
+            e.close()
 
 # usage of "thing" interface
 def use(it):
@@ -45,52 +50,15 @@
     print(it.bar("Eins", "Zwei"))
 
     # Batched call to a couple of proxied methods.
-    batch = it.batchiter()
-    # The calls return futures to eventually hold results.
-    foo = batch.foo(one="One", two="Two")
-    bar = batch.bar("Eins", "Zwei")
-    bar2 = batch.bar(b="Uno", a="Due")
-
-    # Future shouldn't be set until we submit().
-    assert isinstance(foo, wireprotov1peer.future)
-    assert not util.safehasattr(foo, 'value')
-    assert not util.safehasattr(bar, 'value')
-    batch.submit()
-    # Call results() to obtain results as a generator.
-    results = batch.results()
 
-    # Future results shouldn't be set until we consume a value.
-    assert not util.safehasattr(foo, 'value')
-    foovalue = next(results)
-    assert util.safehasattr(foo, 'value')
-    assert foovalue == foo.value
-    print(foo.value)
-    next(results)
-    print(bar.value)
-    next(results)
-    print(bar2.value)
+    with it.commandexecutor() as e:
+        ffoo = e.callcommand('foo', {'one': 'One', 'two': 'Two'})
+        fbar = e.callcommand('bar', {'b': 'Eins', 'a': 'Zwei'})
+        fbar2 = e.callcommand('bar', {'b': 'Uno', 'a': 'Due'})
 
-    # We should be at the end of the results generator.
-    try:
-        next(results)
-    except StopIteration:
-        print('proper end of results generator')
-    else:
-        print('extra emitted element!')
-
-    # Attempting to call a non-batchable method inside a batch fails.
-    batch = it.batchiter()
-    try:
-        batch.greet(name='John Smith')
-    except error.ProgrammingError as e:
-        print(e)
-
-    # Attempting to call a local method inside a batch fails.
-    batch = it.batchiter()
-    try:
-        batch.hello()
-    except error.ProgrammingError as e:
-        print(e)
+    print(ffoo.result())
+    print(fbar.result())
+    print(fbar2.result())
 
 # local usage
 mylocal = localthing()
@@ -177,8 +145,13 @@
         for r in res.split(';'):
             yield r
 
-    def batchiter(self):
-        return wireprotov1peer.remoteiterbatcher(self)
+    @contextlib.contextmanager
+    def commandexecutor(self):
+        e = wireprotov1peer.peerexecutor(self)
+        try:
+            yield e
+        finally:
+            e.close()
 
     @wireprotov1peer.batchable
     def foo(self, one, two=None):
diff --git a/mercurial/wireprotov1peer.py b/mercurial/wireprotov1peer.py
--- a/mercurial/wireprotov1peer.py
+++ b/mercurial/wireprotov1peer.py
@@ -73,97 +73,6 @@
             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
-
-    def __getattr__(self, name):
-        # Validate this method is batchable, since submit() only supports
-        # batchable methods.
-        fn = getattr(self._remote, name)
-        if not getattr(fn, 'batchable', None):
-            raise error.ProgrammingError('Attempted to batch a non-batchable '
-                                         'call to %r' % name)
-
-        return super(remoteiterbatcher, self).__getattr__(name)
-
-    def submit(self):
-        """Break the batch request into many patch calls and pipeline them.
-
-        This is mostly valuable over http where request sizes can be
-        limited, but can be used in other places as well.
-        """
-        # 2-tuple of (command, arguments) that represents what will be
-        # sent over the wire.
-        requests = []
-
-        # 4-tuple of (command, final future, @batchable generator, remote
-        # future).
-        results = []
-
-        for command, args, opts, finalfuture in self.calls:
-            mtd = getattr(self._remote, command)
-            batchable = mtd.batchable(mtd.__self__, *args, **opts)
-
-            commandargs, fremote = next(batchable)
-            assert fremote
-            requests.append((command, commandargs))
-            results.append((command, finalfuture, batchable, fremote))
-
-        if requests:
-            self._resultiter = self._remote._submitbatch(requests)
-
-        self._results = results
-
-    def results(self):
-        for command, finalfuture, batchable, remotefuture in self._results:
-            # Get the raw result, set it in the remote future, feed it
-            # back into the @batchable generator so it can be decoded, and
-            # set the result on the final future to this value.
-            remoteresult = next(self._resultiter)
-            remotefuture.set(remoteresult)
-            finalfuture.set(next(batchable))
-
-            # Verify our @batchable generators only emit 2 values.
-            try:
-                next(batchable)
-            except StopIteration:
-                pass
-            else:
-                raise error.ProgrammingError('%s @batchable generator emitted '
-                                             'unexpected value count' % command)
-
-            yield finalfuture.value
-
 def encodebatchcmds(req):
     """Return a ``cmds`` argument value for the ``batch`` command."""
     escapearg = wireprototypes.escapebatcharg
@@ -412,9 +321,6 @@
 
     # Begin of ipeercommands interface.
 
-    def iterbatch(self):
-        return remoteiterbatcher(self)
-
     @batchable
     def lookup(self, key):
         self.requirecap('lookup', _('look up remote revision'))
diff --git a/mercurial/repository.py b/mercurial/repository.py
--- a/mercurial/repository.py
+++ b/mercurial/repository.py
@@ -284,29 +284,6 @@
 
     All peer instances must conform to this interface.
     """
-    def iterbatch():
-        """Obtain an object to be used for multiple method calls.
-
-        Various operations call several methods on peer instances. If each
-        method call were performed immediately and serially, this would
-        require round trips to remote peers and/or would slow down execution.
-
-        Some peers have the ability to "batch" method calls to avoid costly
-        round trips or to facilitate concurrent execution.
-
-        This method returns an object that can be used to indicate intent to
-        perform batched method calls.
-
-        The returned object is a proxy of this peer. It intercepts calls to
-        batchable methods and queues them instead of performing them
-        immediately. This proxy object has a ``submit`` method that will
-        perform all queued batchable method calls. A ``results()`` method
-        exposes the results of queued/batched method calls. It is a generator
-        of results in the order they were called.
-
-        Not all peers or wire protocol implementations may actually batch method
-        calls. However, they must all support this API.
-        """
 
 class ipeerbaselegacycommands(ipeerbase, ipeerlegacycommands):
     """Unified peer interface that supports legacy commands."""
diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -66,7 +66,6 @@
     txnutil,
     util,
     vfs as vfsmod,
-    wireprotov1peer,
 )
 from .utils import (
     procutil,
@@ -154,20 +153,6 @@
               'unbundle'}
 legacycaps = moderncaps.union({'changegroupsubset'})
 
-class localiterbatcher(wireprotov1peer.iterbatcher):
-    def __init__(self, local):
-        super(localiterbatcher, 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:
-            resref.set(getattr(self.local, name)(*args, **opts))
-            yield resref.value
-
 @zi.implementer(repository.ipeercommandexecutor)
 class localcommandexecutor(object):
     def __init__(self, peer):
@@ -333,9 +318,6 @@
     def commandexecutor(self):
         return localcommandexecutor(self)
 
-    def iterbatch(self):
-        return localiterbatcher(self)
-
     # End of peer interface.
 
 class locallegacypeer(repository.legacypeer, localpeer):



To: indygreg, #hg-reviewers
Cc: mercurial-devel


More information about the Mercurial-devel mailing list