wireprotov1peer: simplify the way batchable rpcs are defined
authorValentin Gatien-Baron <valentin.gatienbaron@gmail.com>
Tue, 24 Aug 2021 17:27:16 +0200
changeset 47872 cdad6560e832
parent 47861 1c797757f5bb
child 47873 c424ff4807e6
wireprotov1peer: simplify the way batchable rpcs are defined The scheme with futures/generator is confusing due to the way communication is done by side effects, especially with two different "future" objects. Just returning a request and a function to read the response is easier to understand. There are tests failures with the largefiles extension due to it aliasing one rpc to another one, which gets fixed in the next commit. Differential Revision: https://phab.mercurial-scm.org/D11211
mercurial/wireprotov1peer.py
--- a/mercurial/wireprotov1peer.py	Fri Aug 20 11:23:41 2021 +0200
+++ b/mercurial/wireprotov1peer.py	Tue Aug 24 17:27:16 2021 +0200
@@ -35,7 +35,7 @@
 urlreq = util.urlreq
 
 
-def batchable(f):
+def batchable_new_style(f):
     """annotation for batchable methods
 
     Such methods must implement a coroutine as follows:
@@ -44,13 +44,9 @@
     def sample(self, one, two=None):
         # Build list of encoded arguments suitable for your wire protocol:
         encoded_args = [('one', encode(one),), ('two', encode(two),)]
-        # Create future for injection of encoded result:
-        encoded_res_future = future()
-        # Return encoded arguments and future:
-        yield encoded_args, encoded_res_future
-        # Assuming the future to be filled with the result from the batched
-        # request now. Decode it:
-        yield decode(encoded_res_future.value)
+        # Return it, along with a function that will receive the result
+        # from the batched request.
+        return encoded_args, decode
 
     The decorator returns a function which wraps this coroutine as a plain
     method, but adds the original method as an attribute called "batchable",
@@ -59,20 +55,37 @@
     """
 
     def plain(*args, **opts):
-        batchable = f(*args, **opts)
-        encoded_args_or_res, encoded_res_future = next(batchable)
-        if not encoded_res_future:
+        encoded_args_or_res, decode = f(*args, **opts)
+        if not decode:
             return encoded_args_or_res  # a local result in this case
         self = args[0]
         cmd = pycompat.bytesurl(f.__name__)  # ensure cmd is ascii bytestr
-        encoded_res_future.set(self._submitone(cmd, encoded_args_or_res))
-        return next(batchable)
+        encoded_res = self._submitone(cmd, encoded_args_or_res)
+        return decode(encoded_res)
 
     setattr(plain, 'batchable', f)
     setattr(plain, '__name__', f.__name__)
     return plain
 
 
+def batchable(f):
+    def upgraded(*args, **opts):
+        batchable = f(*args, **opts)
+        encoded_args_or_res, encoded_res_future = next(batchable)
+        if not encoded_res_future:
+            decode = None
+        else:
+
+            def decode(d):
+                encoded_res_future.set(d)
+                return next(batchable)
+
+        return encoded_args_or_res, decode
+
+    setattr(upgraded, '__name__', f.__name__)
+    return batchable_new_style(upgraded)
+
+
 class future(object):
     '''placeholder for a value to be set later'''
 
@@ -248,25 +261,18 @@
                 continue
 
             try:
-                batchable = fn.batchable(
+                encoded_args_or_res, decode = fn.batchable(
                     fn.__self__, **pycompat.strkwargs(args)
                 )
             except Exception:
                 pycompat.future_set_exception_info(f, sys.exc_info()[1:])
                 return
 
-            # Encoded arguments and future holding remote result.
-            try:
-                encoded_args_or_res, fremote = next(batchable)
-            except Exception:
-                pycompat.future_set_exception_info(f, sys.exc_info()[1:])
-                return
-
-            if not fremote:
+            if not decode:
                 f.set_result(encoded_args_or_res)
             else:
                 requests.append((command, encoded_args_or_res))
-                states.append((command, f, batchable, fremote))
+                states.append((command, f, batchable, decode))
 
         if not requests:
             return
@@ -319,7 +325,7 @@
     def _readbatchresponse(self, states, wireresults):
         # Executes in a thread to read data off the wire.
 
-        for command, f, batchable, fremote in states:
+        for command, f, batchable, decode in states:
             # Grab raw result off the wire and teach the internal future
             # about it.
             try:
@@ -334,11 +340,8 @@
                     )
                 )
             else:
-                fremote.set(remoteresult)
-
-                # And ask the coroutine to decode that value.
                 try:
-                    result = next(batchable)
+                    result = decode(remoteresult)
                 except Exception:
                     pycompat.future_set_exception_info(f, sys.exc_info()[1:])
                 else: