diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -67,7 +67,6 @@ txnutil, util, vfs as vfsmod, - wireprotov1peer, ) from .utils import ( procutil, @@ -155,20 +154,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): @@ -334,9 +319,6 @@ finally: executor.close() - def iterbatch(self): - return localiterbatcher(self) - # End of peer interface. class locallegacypeer(repository.legacypeer, localpeer): diff --git a/mercurial/repository.py b/mercurial/repository.py --- a/mercurial/repository.py +++ b/mercurial/repository.py @@ -274,29 +274,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/wireprotov1peer.py b/mercurial/wireprotov1peer.py --- a/mercurial/wireprotov1peer.py +++ b/mercurial/wireprotov1peer.py @@ -74,97 +74,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 @@ -364,9 +273,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/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/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-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, =;: