diff mercurial/debugcommands.py @ 36530:bde0bd50f368

debugcommands: allow sending of simple commands with debugwireproto Previously, we only had support for low-level "raw" operations. A goal of `hg debugwireproto` is to allow easily performing higher-level primitives, such as sending a wire protocol command and reading its response. We implement a "command" action that does just this. Currently, we only support simple commands (those without payloads). We have basic support for sending command arguments. We don't yet support sending dictionary arguments. This will be implemented later. To prove it works, we add tests to test-ssh-proto.t that send some "listkeys" commands. Note: we don't observe/report os.read() events because these may not be deterministic. We instead observe/report the read() and readline() operations on the bufferedinputpipe. These *should* be deterministic. Differential Revision: https://phab.mercurial-scm.org/D2406
author Gregory Szorc <gregory.szorc@gmail.com>
date Thu, 01 Mar 2018 08:27:30 -0800
parents 72e487851a53
children 097ad1079192
line wrap: on
line diff
--- a/mercurial/debugcommands.py	Fri Feb 23 09:40:12 2018 -0800
+++ b/mercurial/debugcommands.py	Thu Mar 01 08:27:30 2018 -0800
@@ -2614,6 +2614,21 @@
 
     Behaves like ``raw`` except flushes output afterwards.
 
+    command <X>
+    -----------
+
+    Send a request to run a named command, whose name follows the ``command``
+    string.
+
+    Arguments to the command are defined as lines in this block. The format of
+    each line is ``<key> <value>``. e.g.::
+
+       command listkeys
+           namespace bookmarks
+
+    Values are interpreted as Python b'' literals. This allows encoding
+    special byte sequences via backslash escaping.
+
     close
     -----
 
@@ -2713,6 +2728,29 @@
                 stdin.flush()
         elif action == 'flush':
             stdin.flush()
+        elif action.startswith('command'):
+            if not peer:
+                raise error.Abort(_('cannot send commands unless peer instance '
+                                    'is available'))
+
+            command = action.split(' ', 1)[1]
+
+            args = {}
+            for line in lines:
+                # We need to allow empty values.
+                fields = line.lstrip().split(' ', 1)
+                if len(fields) == 1:
+                    key = fields[0]
+                    value = ''
+                else:
+                    key, value = fields
+
+                args[key] = util.unescapestr(value)
+
+            ui.status(_('sending %s command\n') % command)
+            res = peer._call(command, **args)
+            ui.status(_('response: %s\n') % util.escapedata(res))
+
         elif action == 'close':
             peer.close()
         elif action == 'readavailable':