diff mercurial/commandserver.py @ 40589:054d0fcba2c4

commandserver: add experimental option to use separate message channel This is loosely based on the idea of the TortoiseHg's pipeui extension, which attaches ui.label to message text so the command-server client can capture prompt text, for example. https://bitbucket.org/tortoisehg/thg/src/4.7.2/tortoisehg/util/pipeui.py I was thinking that this functionality could be generalized to templating, but changed mind as doing template stuff would be unnecessarily complex. It's merely a status message, a simple serialization option should suffice. Since this slightly changes the command-server protocol, it's gated by a config knob. If the config is enabled, and if it's supported by the server, "message-encoding: <name>" is advertised so the client can stop parsing 'o'/'e' channel data and read encoded messages from the 'm' channel. As we might add new message encodings in future releases, client can specify a list of encoding names in preferred order. This patch includes 'cbor' encoding as example. Perhaps, 'json' should be supported as well.
author Yuya Nishihara <yuya@tcha.org>
date Sun, 18 Jan 2015 18:49:59 +0900
parents 9683dfb6f13a
children 83dd8c63a0c6
line wrap: on
line diff
--- a/mercurial/commandserver.py	Wed Nov 07 22:37:51 2018 +0900
+++ b/mercurial/commandserver.py	Sun Jan 18 18:49:59 2015 +0900
@@ -26,9 +26,11 @@
 from . import (
     encoding,
     error,
+    pycompat,
     util,
 )
 from .utils import (
+    cborutil,
     procutil,
 )
 
@@ -70,6 +72,30 @@
             raise AttributeError(attr)
         return getattr(self.out, attr)
 
+class channeledmessage(object):
+    """
+    Write encoded message and metadata to out in the following format:
+
+    data length (unsigned int),
+    encoded message and metadata, as a flat key-value dict.
+    """
+
+    # teach ui that write() can take **opts
+    structured = True
+
+    def __init__(self, out, channel, encodename, encodefn):
+        self._cout = channeledoutput(out, channel)
+        self.encoding = encodename
+        self._encodefn = encodefn
+
+    def write(self, data, **opts):
+        opts = pycompat.byteskwargs(opts)
+        opts[b'data'] = data
+        self._cout.write(self._encodefn(opts))
+
+    def __getattr__(self, attr):
+        return getattr(self._cout, attr)
+
 class channeledinput(object):
     """
     Read data from in_.
@@ -156,6 +182,20 @@
             raise AttributeError(attr)
         return getattr(self.in_, attr)
 
+_messageencoders = {
+    b'cbor': lambda v: b''.join(cborutil.streamencode(v)),
+}
+
+def _selectmessageencoder(ui):
+    # experimental config: cmdserver.message-encodings
+    encnames = ui.configlist(b'cmdserver', b'message-encodings')
+    for n in encnames:
+        f = _messageencoders.get(n)
+        if f:
+            return n, f
+    raise error.Abort(b'no supported message encodings: %s'
+                      % b' '.join(encnames))
+
 class server(object):
     """
     Listens for commands on fin, runs them and writes the output on a channel
@@ -189,6 +229,14 @@
         self.cin = channeledinput(fin, fout, 'I')
         self.cresult = channeledoutput(fout, 'r')
 
+        # TODO: add this to help/config.txt when stabilized
+        # ``channel``
+        #   Use separate channel for structured output. (Command-server only)
+        self.cmsg = None
+        if ui.config(b'ui', b'message-output') == b'channel':
+            encname, encfn = _selectmessageencoder(ui)
+            self.cmsg = channeledmessage(fout, b'm', encname, encfn)
+
         self.client = fin
 
     def cleanup(self):
@@ -254,7 +302,7 @@
                 ui.setconfig('ui', 'nontty', 'true', 'commandserver')
 
         req = dispatch.request(args[:], copiedui, self.repo, self.cin,
-                               self.cout, self.cerr)
+                               self.cout, self.cerr, self.cmsg)
 
         try:
             ret = dispatch.dispatch(req) & 255
@@ -289,6 +337,8 @@
         hellomsg += '\n'
         hellomsg += 'encoding: ' + encoding.encoding
         hellomsg += '\n'
+        if self.cmsg:
+            hellomsg += 'message-encoding: %s\n' % self.cmsg.encoding
         hellomsg += 'pid: %d' % procutil.getpid()
         if util.safehasattr(os, 'getpgid'):
             hellomsg += '\n'