All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/42] python: move qmp-shell into qemu.qmp package
@ 2021-06-07 20:06 John Snow
  2021-06-07 20:06 ` [PATCH 01/42] scripts/qmp-shell: apply isort rules John Snow
                   ` (42 more replies)
  0 siblings, 43 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

Based-on: <20210603003719.1321369-1-jsnow@redhat.com>
Based-on: <20210604155532.1499282-1-jsnow@redhat.com>
CI: https://gitlab.com/jsnow/qemu/-/pipelines/316425665
GitLab: https://gitlab.com/jsnow/qemu/-/commits/python-package-qmp-shell

This series follows:

1. [PATCH v3 00/19] Python: move /scripts/qmp/qom* to /python/qemu/qmp/qom*​
2. [PATCH 00/11] python: move /scripts/qmp/gemu-ga-client.py to qemu.qmp package

and finishes moving stuff in ./scripts/qmp/ into ./python/qemu/qmp/*.

The benefits of this are:

1. Improved protection against accidental regression in qmp-using
scripts as we refactor QMP to introduce Async QMP and OOB support
2. Availability of common qmp command-line tools as part of the qemu.qmp
package
3. No more sys.path hacking for qemu.qmp-using utilities
4. Newly 100% clean linting baseline to use as an aid in reviewing
future patches.

This series is largely minor refactors, linting and typing cleanups,
followed by the move into the python packaging folder at the very end of
the series.

John Snow (42):
  scripts/qmp-shell: apply isort rules
  scripts/qmp-shell: Apply flake8 rules
  scripts/qmp-shell: fix show_banner signature
  scripts/qmp-shell: fix exception handling
  scripts/qmp-shell: fix connect method signature
  scripts/qmp-shell: remove shadowed variable from _print()
  scripts/qmp-shell: use @classmethod where appropriate
  scripts/qmp-shell: Use python3-style super()
  scripts/qmp-shell: declare verbose in __init__
  scripts/qmp-shell: use triple-double-quote docstring style
  scripts/qmp-shell: ignore visit_Name name
  scripts/qmp-shell: make QMPCompleter returns explicit
  scripts/qmp-shell: rename one and two-letter variables
  scripts/qmp-shell: fix shell history exception handling
  scripts/qmp-shell: remove if-raise-else patterns
  scripts/qmp-shell: use isinstance() instead of type()
  scripts/qmp-shell: use argparse
  scripts/qmp-shell: Add pretty attribute to HMP shell
  scripts/qmp-shell: Make verbose a public attribute
  scripts/qmp-shell: move get_prompt() to prompt property
  scripts/qmp-shell: remove prompt argument from read_exec_command
  scripts/qmp-shell: move the REPL functionality into QMPShell
  scripts/qmp-shell: Fix "FuzzyJSON" parser
  scripts/qmp-shell: refactor QMPCompleter
  scripts/qmp-shell: initialize completer early
  python/qmp: add QMPObject type alias
  scripts/qmp-shell: add mypy types
  scripts/qmp-shell: Accept SocketAddrT instead of string
  scripts/qmp-shell: unprivatize 'pretty' property
  python/qmp: return generic type from context manager
  scripts/qmp-shell: Use context manager instead of atexit
  scripts/qmp-shell: use logging to show warnings
  scripts/qmp-shell: remove TODO
  scripts/qmp-shell: Fix empty-transaction invocation
  scripts/qmp-shell: Remove too-broad-exception
  scripts/qmp-shell: convert usage comment to docstring
  scripts/qmp-shell: remove double-underscores
  scripts/qmp-shell: make QMPShellError inherit QMPError
  scripts/qmp-shell: add docstrings
  scripts/qmp-shell: move to python/qemu/qmp/qmp_shell.py
  python: add qmp-shell entry point
  scripts/qmp-shell: add redirection shim

 python/qemu/qmp/__init__.py  |   8 +-
 python/qemu/qmp/qmp_shell.py | 535 +++++++++++++++++++++++++++++++++++
 python/setup.cfg             |   1 +
 scripts/qmp/qmp-shell        | 437 +---------------------------
 4 files changed, 546 insertions(+), 435 deletions(-)
 create mode 100644 python/qemu/qmp/qmp_shell.py

-- 
2.31.1




^ permalink raw reply	[flat|nested] 44+ messages in thread

* [PATCH 01/42] scripts/qmp-shell: apply isort rules
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 02/42] scripts/qmp-shell: Apply flake8 rules John Snow
                   ` (41 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index d5ae8a9b21..a00efe6fea 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -65,18 +65,20 @@
 # which will echo back the properly formatted JSON-compliant QMP that is being
 # sent to QEMU, which is useful for debugging and documentation generation.
 
-import json
 import ast
-import readline
-import sys
-import os
-import errno
 import atexit
+import errno
+import json
+import os
 import re
+import readline
+import sys
+
 
 sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
 from qemu import qmp
 
+
 class QMPCompleter(list):
     def complete(self, text, state):
         for cmd in self:
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 02/42] scripts/qmp-shell: Apply flake8 rules
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
  2021-06-07 20:06 ` [PATCH 01/42] scripts/qmp-shell: apply isort rules John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 03/42] scripts/qmp-shell: fix show_banner signature John Snow
                   ` (40 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

A lot of fiddling around to get us below 80 columns.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell | 64 +++++++++++++++++++++++++++++--------------
 1 file changed, 43 insertions(+), 21 deletions(-)

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index a00efe6fea..62a6377e06 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -88,6 +88,7 @@ class QMPCompleter(list):
                 else:
                     state -= 1
 
+
 class QMPShellError(Exception):
     pass
 
@@ -105,6 +106,7 @@ class FuzzyJSON(ast.NodeTransformer):
             node.id = 'None'
         return node
 
+
 # TODO: QMPShell's interface is a bit ugly (eg. _fill_completion() and
 #       _execute_cmd()). Let's design a better one.
 class QMPShell(qmp.QEMUMonitorProtocol):
@@ -131,8 +133,8 @@ class QMPShell(qmp.QEMUMonitorProtocol):
         readline.set_history_length(1024)
         readline.set_completer(self._completer.complete)
         readline.parse_and_bind("tab: complete")
-        # XXX: default delimiters conflict with some command names (eg. query-),
-        # clearing everything as it doesn't seem to matter
+        # NB: default delimiters conflict with some command names
+        # (eg. query-), clearing everything as it doesn't seem to matter
         readline.set_completer_delims('')
         try:
             readline.read_history_file(self._histfile)
@@ -180,7 +182,9 @@ class QMPShell(qmp.QEMUMonitorProtocol):
         for arg in tokens:
             (key, sep, val) = arg.partition('=')
             if sep != '=':
-                raise QMPShellError("Expected a key=value pair, got '%s'" % arg)
+                raise QMPShellError(
+                    f"Expected a key=value pair, got '{arg!s}'"
+                )
 
             value = self.__parse_value(val)
             optpath = key.split('.')
@@ -189,14 +193,16 @@ class QMPShell(qmp.QEMUMonitorProtocol):
                 curpath.append(p)
                 d = parent.get(p, {})
                 if type(d) is not dict:
-                    raise QMPShellError('Cannot use "%s" as both leaf and non-leaf key' % '.'.join(curpath))
+                    msg = 'Cannot use "{:s}" as both leaf and non-leaf key'
+                    raise QMPShellError(msg.format('.'.join(curpath)))
                 parent[p] = d
                 parent = d
             if optpath[-1] in parent:
                 if type(parent[optpath[-1]]) is dict:
-                    raise QMPShellError('Cannot use "%s" as both leaf and non-leaf key' % '.'.join(curpath))
+                    msg = 'Cannot use "{:s}" as both leaf and non-leaf key'
+                    raise QMPShellError(msg.format('.'.join(curpath)))
                 else:
-                    raise QMPShellError('Cannot set "%s" multiple times' % key)
+                    raise QMPShellError(f'Cannot set "{key}" multiple times')
             parent[optpath[-1]] = value
 
     def __build_cmd(self, cmdline):
@@ -206,7 +212,8 @@ class QMPShell(qmp.QEMUMonitorProtocol):
 
             < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ]
         """
-        cmdargs = re.findall(r'''(?:[^\s"']|"(?:\\.|[^"])*"|'(?:\\.|[^'])*')+''', cmdline)
+        argument_regex = r'''(?:[^\s"']|"(?:\\.|[^"])*"|'(?:\\.|[^'])*')+'''
+        cmdargs = re.findall(argument_regex, cmdline)
 
         # Transactional CLI entry/exit:
         if cmdargs[0] == 'transaction(':
@@ -215,9 +222,12 @@ class QMPShell(qmp.QEMUMonitorProtocol):
         elif cmdargs[0] == ')' and self._transmode:
             self._transmode = False
             if len(cmdargs) > 1:
-                raise QMPShellError("Unexpected input after close of Transaction sub-shell")
-            qmpcmd = { 'execute': 'transaction',
-                       'arguments': { 'actions': self._actions } }
+                msg = 'Unexpected input after close of Transaction sub-shell'
+                raise QMPShellError(msg)
+            qmpcmd = {
+                'execute': 'transaction',
+                'arguments': {'actions': self._actions}
+            }
             self._actions = list()
             return qmpcmd
 
@@ -228,7 +238,7 @@ class QMPShell(qmp.QEMUMonitorProtocol):
         # Parse and then cache this Transactional Action
         if self._transmode:
             finalize = False
-            action = { 'type': cmdargs[0], 'data': {} }
+            action = {'type': cmdargs[0], 'data': {}}
             if cmdargs[-1] == ')':
                 cmdargs.pop(-1)
                 finalize = True
@@ -237,7 +247,7 @@ class QMPShell(qmp.QEMUMonitorProtocol):
             return self.__build_cmd(')') if finalize else None
 
         # Standard command: parse and return it to be executed.
-        qmpcmd = { 'execute': cmdargs[0], 'arguments': {} }
+        qmpcmd = {'execute': cmdargs[0], 'arguments': {}}
         self.__cli_expr(cmdargs[1:], qmpcmd['arguments'])
         return qmpcmd
 
@@ -278,7 +288,7 @@ class QMPShell(qmp.QEMUMonitorProtocol):
             print('Connected')
             return
         version = self._greeting['QMP']['version']['qemu']
-        print('Connected to QEMU %d.%d.%d\n' % (version['major'],version['minor'],version['micro']))
+        print("Connected to QEMU {major}.{minor}.{micro}\n".format(**version))
 
     def get_prompt(self):
         if self._transmode:
@@ -307,6 +317,7 @@ class QMPShell(qmp.QEMUMonitorProtocol):
     def set_verbosity(self, verbose):
         self._verbose = verbose
 
+
 class HMPShell(QMPShell):
     def __init__(self, address):
         QMPShell.__init__(self, address)
@@ -315,7 +326,7 @@ class HMPShell(QMPShell):
     def __cmd_completion(self):
         for cmd in self.__cmd_passthrough('help')['return'].split('\r\n'):
             if cmd and cmd[0] != '[' and cmd[0] != '\t':
-                name = cmd.split()[0] # drop help text
+                name = cmd.split()[0]  # drop help text
                 if name == 'info':
                     continue
                 if name.find('|') != -1:
@@ -327,7 +338,7 @@ class HMPShell(QMPShell):
                     else:
                         name = opt[0]
                 self._completer.append(name)
-                self._completer.append('help ' + name) # help completion
+                self._completer.append('help ' + name)  # help completion
 
     def __info_completion(self):
         for cmd in self.__cmd_passthrough('info')['return'].split('\r\n'):
@@ -343,17 +354,21 @@ class HMPShell(QMPShell):
         self.__info_completion()
         self.__other_completion()
 
-    def __cmd_passthrough(self, cmdline, cpu_index = 0):
-        return self.cmd_obj({ 'execute': 'human-monitor-command', 'arguments':
-                              { 'command-line': cmdline,
-                                'cpu-index': cpu_index } })
+    def __cmd_passthrough(self, cmdline, cpu_index=0):
+        return self.cmd_obj({
+            'execute': 'human-monitor-command',
+            'arguments': {
+                'command-line': cmdline,
+                'cpu-index': cpu_index
+            }
+        })
 
     def _execute_cmd(self, cmdline):
         if cmdline.split()[0] == "cpu":
             # trap the cpu command, it requires special setting
             try:
                 idx = int(cmdline.split()[1])
-                if not 'return' in self.__cmd_passthrough('info version', idx):
+                if 'return' not in self.__cmd_passthrough('info version', idx):
                     print('bad CPU index')
                     return True
                 self.__cpu_index = idx
@@ -377,20 +392,26 @@ class HMPShell(QMPShell):
     def show_banner(self):
         QMPShell.show_banner(self, msg='Welcome to the HMP shell!')
 
+
 def die(msg):
     sys.stderr.write('ERROR: %s\n' % msg)
     sys.exit(1)
 
+
 def fail_cmdline(option=None):
     if option:
         sys.stderr.write('ERROR: bad command-line option \'%s\'\n' % option)
-    sys.stderr.write('qmp-shell [ -v ] [ -p ] [ -H ] [ -N ] < UNIX socket path> | < TCP address:port >\n')
+    sys.stderr.write(
+        'qmp-shell [ -v ] [ -p ] [ -H ] [ -N ] '
+        '< UNIX socket path> | < TCP address:port >\n'
+    )
     sys.stderr.write('    -v     Verbose (echo command sent and received)\n')
     sys.stderr.write('    -p     Pretty-print JSON\n')
     sys.stderr.write('    -H     Use HMP interface\n')
     sys.stderr.write('    -N     Skip negotiate (for qemu-ga)\n')
     sys.exit(1)
 
+
 def main():
     addr = ''
     qemu = None
@@ -440,5 +461,6 @@ def main():
         pass
     qemu.close()
 
+
 if __name__ == '__main__':
     main()
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 03/42] scripts/qmp-shell: fix show_banner signature
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
  2021-06-07 20:06 ` [PATCH 01/42] scripts/qmp-shell: apply isort rules John Snow
  2021-06-07 20:06 ` [PATCH 02/42] scripts/qmp-shell: Apply flake8 rules John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 04/42] scripts/qmp-shell: fix exception handling John Snow
                   ` (39 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

The signatures need to match.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index 62a6377e06..18bf49bb26 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -389,8 +389,8 @@ class HMPShell(QMPShell):
             print('%s: %s' % (resp['error']['class'], resp['error']['desc']))
         return True
 
-    def show_banner(self):
-        QMPShell.show_banner(self, msg='Welcome to the HMP shell!')
+    def show_banner(self, msg='Welcome to the HMP shell!'):
+        QMPShell.show_banner(self, msg)
 
 
 def die(msg):
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 04/42] scripts/qmp-shell: fix exception handling
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (2 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 03/42] scripts/qmp-shell: fix show_banner signature John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 05/42] scripts/qmp-shell: fix connect method signature John Snow
                   ` (38 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

Fixes: 50d189c

Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index 18bf49bb26..413dd4d2de 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -452,7 +452,7 @@ def main():
         die('Didn\'t get QMP greeting message')
     except qmp.QMPCapabilitiesError:
         die('Could not negotiate capabilities')
-    except qemu.error:
+    except OSError:
         die('Could not connect to %s' % addr)
 
     qemu.show_banner()
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 05/42] scripts/qmp-shell: fix connect method signature
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (3 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 04/42] scripts/qmp-shell: fix exception handling John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 06/42] scripts/qmp-shell: remove shadowed variable from _print() John Snow
                   ` (37 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

It needs to match the parent's signature -- the negotiate parameter must
be optional.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index 413dd4d2de..04ca6a25ae 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -278,7 +278,7 @@ class QMPShell(qmp.QEMUMonitorProtocol):
         self._print(resp)
         return True
 
-    def connect(self, negotiate):
+    def connect(self, negotiate: bool = True):
         self._greeting = super(QMPShell, self).connect(negotiate)
         self.__completer_setup()
 
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 06/42] scripts/qmp-shell: remove shadowed variable from _print()
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (4 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 05/42] scripts/qmp-shell: fix connect method signature John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 07/42] scripts/qmp-shell: use @classmethod where appropriate John Snow
                   ` (36 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

Don't use 'qmp' here, which shadows the qmp module.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index 04ca6a25ae..ae3f04534a 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -251,11 +251,11 @@ class QMPShell(qmp.QEMUMonitorProtocol):
         self.__cli_expr(cmdargs[1:], qmpcmd['arguments'])
         return qmpcmd
 
-    def _print(self, qmp):
+    def _print(self, qmp_message):
         indent = None
         if self._pretty:
             indent = 4
-        jsobj = json.dumps(qmp, indent=indent, sort_keys=self._pretty)
+        jsobj = json.dumps(qmp_message, indent=indent, sort_keys=self._pretty)
         print(str(jsobj))
 
     def _execute_cmd(self, cmdline):
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 07/42] scripts/qmp-shell: use @classmethod where appropriate
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (5 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 06/42] scripts/qmp-shell: remove shadowed variable from _print() John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 08/42] scripts/qmp-shell: Use python3-style super() John Snow
                   ` (35 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

Methods with no self-use should belong to the class.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index ae3f04534a..f354549bf2 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -97,7 +97,8 @@ class FuzzyJSON(ast.NodeTransformer):
     '''This extension of ast.NodeTransformer filters literal "true/false/null"
     values in an AST and replaces them by proper "True/False/None" values that
     Python can properly evaluate.'''
-    def visit_Name(self, node):
+    @classmethod
+    def visit_Name(cls, node):
         if node.id == 'true':
             node.id = 'True'
         if node.id == 'false':
@@ -152,7 +153,8 @@ class QMPShell(qmp.QEMUMonitorProtocol):
         except Exception as e:
             print("Failed to save history file '%s'; %s" % (self._histfile, e))
 
-    def __parse_value(self, val):
+    @classmethod
+    def __parse_value(cls, val):
         try:
             return int(val)
         except ValueError:
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 08/42] scripts/qmp-shell: Use python3-style super()
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (6 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 07/42] scripts/qmp-shell: use @classmethod where appropriate John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 09/42] scripts/qmp-shell: declare verbose in __init__ John Snow
                   ` (34 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index f354549bf2..3066e37ae5 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -112,7 +112,7 @@ class FuzzyJSON(ast.NodeTransformer):
 #       _execute_cmd()). Let's design a better one.
 class QMPShell(qmp.QEMUMonitorProtocol):
     def __init__(self, address, pretty=False):
-        super(QMPShell, self).__init__(self.parse_address(address))
+        super().__init__(self.parse_address(address))
         self._greeting = None
         self._completer = None
         self._pretty = pretty
@@ -281,7 +281,7 @@ class QMPShell(qmp.QEMUMonitorProtocol):
         return True
 
     def connect(self, negotiate: bool = True):
-        self._greeting = super(QMPShell, self).connect(negotiate)
+        self._greeting = super().connect(negotiate)
         self.__completer_setup()
 
     def show_banner(self, msg='Welcome to the QMP low-level shell!'):
@@ -322,7 +322,7 @@ class QMPShell(qmp.QEMUMonitorProtocol):
 
 class HMPShell(QMPShell):
     def __init__(self, address):
-        QMPShell.__init__(self, address)
+        super().__init__(address)
         self.__cpu_index = 0
 
     def __cmd_completion(self):
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 09/42] scripts/qmp-shell: declare verbose in __init__
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (7 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 08/42] scripts/qmp-shell: Use python3-style super() John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 10/42] scripts/qmp-shell: use triple-double-quote docstring style John Snow
                   ` (33 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

Linters get angry when we don't define state at init time.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell | 1 +
 1 file changed, 1 insertion(+)

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index 3066e37ae5..4027454324 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -120,6 +120,7 @@ class QMPShell(qmp.QEMUMonitorProtocol):
         self._actions = list()
         self._histfile = os.path.join(os.path.expanduser('~'),
                                       '.qmp-shell_history')
+        self._verbose = False
 
     def _fill_completion(self):
         cmds = self.cmd('query-commands')
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 10/42] scripts/qmp-shell: use triple-double-quote docstring style
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (8 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 09/42] scripts/qmp-shell: declare verbose in __init__ John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 11/42] scripts/qmp-shell: ignore visit_Name name John Snow
                   ` (32 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

(2014 me had never written python before.)

Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index 4027454324..c46f4f516b 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -94,9 +94,12 @@ class QMPShellError(Exception):
 
 
 class FuzzyJSON(ast.NodeTransformer):
-    '''This extension of ast.NodeTransformer filters literal "true/false/null"
+    """
+    This extension of ast.NodeTransformer filters literal "true/false/null"
     values in an AST and replaces them by proper "True/False/None" values that
-    Python can properly evaluate.'''
+    Python can properly evaluate.
+    """
+
     @classmethod
     def visit_Name(cls, node):
         if node.id == 'true':
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 11/42] scripts/qmp-shell: ignore visit_Name name
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (9 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 10/42] scripts/qmp-shell: use triple-double-quote docstring style John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 12/42] scripts/qmp-shell: make QMPCompleter returns explicit John Snow
                   ` (31 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

Not something I control, sorry, pylint.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index c46f4f516b..ea6a87e0b3 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -101,7 +101,7 @@ class FuzzyJSON(ast.NodeTransformer):
     """
 
     @classmethod
-    def visit_Name(cls, node):
+    def visit_Name(cls, node):  # pylint: disable=invalid-name
         if node.id == 'true':
             node.id = 'True'
         if node.id == 'false':
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 12/42] scripts/qmp-shell: make QMPCompleter returns explicit
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (10 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 11/42] scripts/qmp-shell: ignore visit_Name name John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 13/42] scripts/qmp-shell: rename one and two-letter variables John Snow
                   ` (30 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

This function returns None when it doesn't find a match; do that
explicitly.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index ea6a87e0b3..8d84467b53 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -83,10 +83,10 @@ class QMPCompleter(list):
     def complete(self, text, state):
         for cmd in self:
             if cmd.startswith(text):
-                if not state:
+                if state == 0:
                     return cmd
-                else:
-                    state -= 1
+                state -= 1
+        return None
 
 
 class QMPShellError(Exception):
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 13/42] scripts/qmp-shell: rename one and two-letter variables
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (11 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 12/42] scripts/qmp-shell: make QMPCompleter returns explicit John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 14/42] scripts/qmp-shell: fix shell history exception handling John Snow
                   ` (29 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

A bit of churn and housekeeping for pylint, flake8 et al.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell | 24 ++++++++++++------------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index 8d84467b53..afb4b0c544 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -176,8 +176,8 @@ class QMPShell(qmp.QEMUMonitorProtocol):
                 pass
             # Try once again as FuzzyJSON:
             try:
-                st = ast.parse(val, mode='eval')
-                return ast.literal_eval(FuzzyJSON().visit(st))
+                tree = ast.parse(val, mode='eval')
+                return ast.literal_eval(FuzzyJSON().visit(tree))
             except SyntaxError:
                 pass
             except ValueError:
@@ -195,14 +195,14 @@ class QMPShell(qmp.QEMUMonitorProtocol):
             value = self.__parse_value(val)
             optpath = key.split('.')
             curpath = []
-            for p in optpath[:-1]:
-                curpath.append(p)
-                d = parent.get(p, {})
-                if type(d) is not dict:
+            for path in optpath[:-1]:
+                curpath.append(path)
+                obj = parent.get(path, {})
+                if type(obj) is not dict:
                     msg = 'Cannot use "{:s}" as both leaf and non-leaf key'
                     raise QMPShellError(msg.format('.'.join(curpath)))
-                parent[p] = d
-                parent = d
+                parent[path] = obj
+                parent = obj
             if optpath[-1] in parent:
                 if type(parent[optpath[-1]]) is dict:
                     msg = 'Cannot use "{:s}" as both leaf and non-leaf key'
@@ -267,8 +267,8 @@ class QMPShell(qmp.QEMUMonitorProtocol):
     def _execute_cmd(self, cmdline):
         try:
             qmpcmd = self.__build_cmd(cmdline)
-        except Exception as e:
-            print('Error while parsing command line: %s' % e)
+        except Exception as err:
+            print('Error while parsing command line: %s' % err)
             print('command format: <command-name> ', end=' ')
             print('[arg-name1=arg1] ... [arg-nameN=argN]')
             return True
@@ -313,8 +313,8 @@ class QMPShell(qmp.QEMUMonitorProtocol):
             print()
             return False
         if cmdline == '':
-            for ev in self.get_events():
-                print(ev)
+            for event in self.get_events():
+                print(event)
             self.clear_events()
             return True
         else:
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 14/42] scripts/qmp-shell: fix shell history exception handling
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (12 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 13/42] scripts/qmp-shell: rename one and two-letter variables John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 15/42] scripts/qmp-shell: remove if-raise-else patterns John Snow
                   ` (28 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

We want to remove exceptions that are too broad here; we only want to
catch IOErrors that get raised as a direct result of the open call.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell | 15 ++++++---------
 1 file changed, 6 insertions(+), 9 deletions(-)

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index afb4b0c544..80cd432607 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -67,7 +67,6 @@
 
 import ast
 import atexit
-import errno
 import json
 import os
 import re
@@ -143,19 +142,17 @@ class QMPShell(qmp.QEMUMonitorProtocol):
         readline.set_completer_delims('')
         try:
             readline.read_history_file(self._histfile)
-        except Exception as e:
-            if isinstance(e, IOError) and e.errno == errno.ENOENT:
-                # File not found. No problem.
-                pass
-            else:
-                print("Failed to read history '%s'; %s" % (self._histfile, e))
+        except FileNotFoundError:
+            pass
+        except IOError as err:
+            print(f"Failed to read history '{self._histfile}': {err!s}")
         atexit.register(self.__save_history)
 
     def __save_history(self):
         try:
             readline.write_history_file(self._histfile)
-        except Exception as e:
-            print("Failed to save history file '%s'; %s" % (self._histfile, e))
+        except IOError as err:
+            print(f"Failed to save history file '{self._histfile}': {err!s}")
 
     @classmethod
     def __parse_value(cls, val):
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 15/42] scripts/qmp-shell: remove if-raise-else patterns
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (13 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 14/42] scripts/qmp-shell: fix shell history exception handling John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 16/42] scripts/qmp-shell: use isinstance() instead of type() John Snow
                   ` (27 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

Shushes pylint. I don't always mind these patterns personally, but I'm
not as sure that I want to remove the warning from pylint's repertoire
entirely. Oh well.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index 80cd432607..bf7a49dfc1 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -204,8 +204,7 @@ class QMPShell(qmp.QEMUMonitorProtocol):
                 if type(parent[optpath[-1]]) is dict:
                     msg = 'Cannot use "{:s}" as both leaf and non-leaf key'
                     raise QMPShellError(msg.format('.'.join(curpath)))
-                else:
-                    raise QMPShellError(f'Cannot set "{key}" multiple times')
+                raise QMPShellError(f'Cannot set "{key}" multiple times')
             parent[optpath[-1]] = value
 
     def __build_cmd(self, cmdline):
@@ -309,13 +308,14 @@ class QMPShell(qmp.QEMUMonitorProtocol):
         except EOFError:
             print()
             return False
+
         if cmdline == '':
             for event in self.get_events():
                 print(event)
             self.clear_events()
             return True
-        else:
-            return self._execute_cmd(cmdline)
+
+        return self._execute_cmd(cmdline)
 
     def set_verbosity(self, verbose):
         self._verbose = verbose
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 16/42] scripts/qmp-shell: use isinstance() instead of type()
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (14 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 15/42] scripts/qmp-shell: remove if-raise-else patterns John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 17/42] scripts/qmp-shell: use argparse John Snow
                   ` (26 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

A bit more idiomatic, and quiets some linter warnings.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index bf7a49dfc1..970f43dd00 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -195,13 +195,13 @@ class QMPShell(qmp.QEMUMonitorProtocol):
             for path in optpath[:-1]:
                 curpath.append(path)
                 obj = parent.get(path, {})
-                if type(obj) is not dict:
+                if not isinstance(obj, dict):
                     msg = 'Cannot use "{:s}" as both leaf and non-leaf key'
                     raise QMPShellError(msg.format('.'.join(curpath)))
                 parent[path] = obj
                 parent = obj
             if optpath[-1] in parent:
-                if type(parent[optpath[-1]]) is dict:
+                if isinstance(parent[optpath[-1]], dict):
                     msg = 'Cannot use "{:s}" as both leaf and non-leaf key'
                     raise QMPShellError(msg.format('.'.join(curpath)))
                 raise QMPShellError(f'Cannot set "{key}" multiple times')
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 17/42] scripts/qmp-shell: use argparse
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (15 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 16/42] scripts/qmp-shell: use isinstance() instead of type() John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 18/42] scripts/qmp-shell: Add pretty attribute to HMP shell John Snow
                   ` (25 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

Use argparse instead of an open-coded CLI parser, for consistency with
everything else.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell | 82 +++++++++++++++++--------------------------
 1 file changed, 32 insertions(+), 50 deletions(-)

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index 970f43dd00..5317dcd516 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -64,7 +64,7 @@
 # Use the -v and -p options to activate the verbose and pretty-print options,
 # which will echo back the properly formatted JSON-compliant QMP that is being
 # sent to QEMU, which is useful for debugging and documentation generation.
-
+import argparse
 import ast
 import atexit
 import json
@@ -401,65 +401,47 @@ def die(msg):
     sys.exit(1)
 
 
-def fail_cmdline(option=None):
-    if option:
-        sys.stderr.write('ERROR: bad command-line option \'%s\'\n' % option)
-    sys.stderr.write(
-        'qmp-shell [ -v ] [ -p ] [ -H ] [ -N ] '
-        '< UNIX socket path> | < TCP address:port >\n'
-    )
-    sys.stderr.write('    -v     Verbose (echo command sent and received)\n')
-    sys.stderr.write('    -p     Pretty-print JSON\n')
-    sys.stderr.write('    -H     Use HMP interface\n')
-    sys.stderr.write('    -N     Skip negotiate (for qemu-ga)\n')
-    sys.exit(1)
-
-
 def main():
-    addr = ''
-    qemu = None
-    hmp = False
-    pretty = False
-    verbose = False
-    negotiate = True
+    parser = argparse.ArgumentParser()
+    parser.add_argument('-H', '--hmp', action='store_true',
+                        help='Use HMP interface')
+    parser.add_argument('-N', '--skip-negotiation', action='store_true',
+                        help='Skip negotiate (for qemu-ga)')
+    parser.add_argument('-v', '--verbose', action='store_true',
+                        help='Verbose (echo commands sent and received)')
+    parser.add_argument('-p', '--pretty', action='store_true',
+                        help='Pretty-print JSON')
 
+    default_server = os.environ.get('QMP_SOCKET')
+    parser.add_argument('qmp_server', action='store',
+                        default=default_server,
+                        help='< UNIX socket path | TCP address:port >')
+
+    args = parser.parse_args()
+    if args.qmp_server is None:
+        parser.error("QMP socket or TCP address must be specified")
+
+    qemu: QMPShell
     try:
-        for arg in sys.argv[1:]:
-            if arg == "-H":
-                if qemu is not None:
-                    fail_cmdline(arg)
-                hmp = True
-            elif arg == "-p":
-                pretty = True
-            elif arg == "-N":
-                negotiate = False
-            elif arg == "-v":
-                verbose = True
-            else:
-                if qemu is not None:
-                    fail_cmdline(arg)
-                if hmp:
-                    qemu = HMPShell(arg)
-                else:
-                    qemu = QMPShell(arg, pretty)
-                addr = arg
-
-        if qemu is None:
-            fail_cmdline()
+        if args.hmp:
+            qemu = HMPShell(args.qmp_server)
+        else:
+            qemu = QMPShell(args.qmp_server, args.pretty)
     except qmp.QMPBadPortError:
-        die('bad port number in command-line')
+        parser.error(f"Bad port number: {args.qmp_server}")
+        return  # pycharm doesn't know error() is noreturn
 
     try:
-        qemu.connect(negotiate)
+        qemu.connect(negotiate=not args.skip_negotiation)
     except qmp.QMPConnectError:
-        die('Didn\'t get QMP greeting message')
+        die("Didn't get QMP greeting message")
     except qmp.QMPCapabilitiesError:
-        die('Could not negotiate capabilities')
-    except OSError:
-        die('Could not connect to %s' % addr)
+        die("Couldn't negotiate capabilities")
+    except OSError as err:
+        die(f"Couldn't connect to {args.qmp_server}: {err!s}")
 
     qemu.show_banner()
-    qemu.set_verbosity(verbose)
+    qemu.set_verbosity(args.verbose)
     while qemu.read_exec_command(qemu.get_prompt()):
         pass
     qemu.close()
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 18/42] scripts/qmp-shell: Add pretty attribute to HMP shell
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (16 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 17/42] scripts/qmp-shell: use argparse John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 19/42] scripts/qmp-shell: Make verbose a public attribute John Snow
                   ` (24 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

It's less useful, but it makes the initialization methods LSP
consistent, which quiets a mypy complaint.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell | 11 ++++-------
 1 file changed, 4 insertions(+), 7 deletions(-)

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index 5317dcd516..de5fa189f0 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -322,8 +322,8 @@ class QMPShell(qmp.QEMUMonitorProtocol):
 
 
 class HMPShell(QMPShell):
-    def __init__(self, address):
-        super().__init__(address)
+    def __init__(self, address, pretty=False):
+        super().__init__(address, pretty)
         self.__cpu_index = 0
 
     def __cmd_completion(self):
@@ -421,12 +421,9 @@ def main():
     if args.qmp_server is None:
         parser.error("QMP socket or TCP address must be specified")
 
-    qemu: QMPShell
+    shell_class = HMPShell if args.hmp else QMPShell
     try:
-        if args.hmp:
-            qemu = HMPShell(args.qmp_server)
-        else:
-            qemu = QMPShell(args.qmp_server, args.pretty)
+        qemu = shell_class(args.qmp_server, args.pretty)
     except qmp.QMPBadPortError:
         parser.error(f"Bad port number: {args.qmp_server}")
         return  # pycharm doesn't know error() is noreturn
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 19/42] scripts/qmp-shell: Make verbose a public attribute
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (17 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 18/42] scripts/qmp-shell: Add pretty attribute to HMP shell John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 20/42] scripts/qmp-shell: move get_prompt() to prompt property John Snow
                   ` (23 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

No real reason to hide this behind an underscore; make it part of the
initializer and make it a regular RW attribute.
Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell | 16 ++++++----------
 1 file changed, 6 insertions(+), 10 deletions(-)

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index de5fa189f0..cfcefb95f9 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -113,7 +113,7 @@ class FuzzyJSON(ast.NodeTransformer):
 # TODO: QMPShell's interface is a bit ugly (eg. _fill_completion() and
 #       _execute_cmd()). Let's design a better one.
 class QMPShell(qmp.QEMUMonitorProtocol):
-    def __init__(self, address, pretty=False):
+    def __init__(self, address, pretty=False, verbose=False):
         super().__init__(self.parse_address(address))
         self._greeting = None
         self._completer = None
@@ -122,7 +122,7 @@ class QMPShell(qmp.QEMUMonitorProtocol):
         self._actions = list()
         self._histfile = os.path.join(os.path.expanduser('~'),
                                       '.qmp-shell_history')
-        self._verbose = False
+        self.verbose = verbose
 
     def _fill_completion(self):
         cmds = self.cmd('query-commands')
@@ -271,7 +271,7 @@ class QMPShell(qmp.QEMUMonitorProtocol):
         # For transaction mode, we may have just cached the action:
         if qmpcmd is None:
             return True
-        if self._verbose:
+        if self.verbose:
             self._print(qmpcmd)
         resp = self.cmd_obj(qmpcmd)
         if resp is None:
@@ -317,13 +317,10 @@ class QMPShell(qmp.QEMUMonitorProtocol):
 
         return self._execute_cmd(cmdline)
 
-    def set_verbosity(self, verbose):
-        self._verbose = verbose
-
 
 class HMPShell(QMPShell):
-    def __init__(self, address, pretty=False):
-        super().__init__(address, pretty)
+    def __init__(self, address, pretty=False, verbose=False):
+        super().__init__(address, pretty, verbose)
         self.__cpu_index = 0
 
     def __cmd_completion(self):
@@ -423,7 +420,7 @@ def main():
 
     shell_class = HMPShell if args.hmp else QMPShell
     try:
-        qemu = shell_class(args.qmp_server, args.pretty)
+        qemu = shell_class(args.qmp_server, args.pretty, args.verbose)
     except qmp.QMPBadPortError:
         parser.error(f"Bad port number: {args.qmp_server}")
         return  # pycharm doesn't know error() is noreturn
@@ -438,7 +435,6 @@ def main():
         die(f"Couldn't connect to {args.qmp_server}: {err!s}")
 
     qemu.show_banner()
-    qemu.set_verbosity(args.verbose)
     while qemu.read_exec_command(qemu.get_prompt()):
         pass
     qemu.close()
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 20/42] scripts/qmp-shell: move get_prompt() to prompt property
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (18 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 19/42] scripts/qmp-shell: Make verbose a public attribute John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 21/42] scripts/qmp-shell: remove prompt argument from read_exec_command John Snow
                   ` (22 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

Small tidying; treat "prompt" like an immutable property instead of
function/method/routine.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index cfcefb95f9..3b86ef7d88 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -292,10 +292,11 @@ class QMPShell(qmp.QEMUMonitorProtocol):
         version = self._greeting['QMP']['version']['qemu']
         print("Connected to QEMU {major}.{minor}.{micro}\n".format(**version))
 
-    def get_prompt(self):
+    @property
+    def prompt(self):
         if self._transmode:
-            return "TRANS> "
-        return "(QEMU) "
+            return 'TRANS> '
+        return '(QEMU) '
 
     def read_exec_command(self, prompt):
         """
@@ -435,7 +436,7 @@ def main():
         die(f"Couldn't connect to {args.qmp_server}: {err!s}")
 
     qemu.show_banner()
-    while qemu.read_exec_command(qemu.get_prompt()):
+    while qemu.read_exec_command(qemu.prompt):
         pass
     qemu.close()
 
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 21/42] scripts/qmp-shell: remove prompt argument from read_exec_command
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (19 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 20/42] scripts/qmp-shell: move get_prompt() to prompt property John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 22/42] scripts/qmp-shell: move the REPL functionality into QMPShell John Snow
                   ` (21 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

It's only ever used by one caller, we can just absorb that logic.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index 3b86ef7d88..31269859c4 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -298,14 +298,14 @@ class QMPShell(qmp.QEMUMonitorProtocol):
             return 'TRANS> '
         return '(QEMU) '
 
-    def read_exec_command(self, prompt):
+    def read_exec_command(self):
         """
         Read and execute a command.
 
         @return True if execution was ok, return False if disconnected.
         """
         try:
-            cmdline = input(prompt)
+            cmdline = input(self.prompt)
         except EOFError:
             print()
             return False
@@ -436,7 +436,7 @@ def main():
         die(f"Couldn't connect to {args.qmp_server}: {err!s}")
 
     qemu.show_banner()
-    while qemu.read_exec_command(qemu.prompt):
+    while qemu.read_exec_command():
         pass
     qemu.close()
 
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 22/42] scripts/qmp-shell: move the REPL functionality into QMPShell
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (20 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 21/42] scripts/qmp-shell: remove prompt argument from read_exec_command John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 23/42] scripts/qmp-shell: Fix "FuzzyJSON" parser John Snow
                   ` (20 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

Instead of doing this in main, move it into the class itself. (This
makes it easier to put into the qemu.qmp package later by removing as
much as we can from the main() function.)

Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index 31269859c4..aa148517a8 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -318,6 +318,12 @@ class QMPShell(qmp.QEMUMonitorProtocol):
 
         return self._execute_cmd(cmdline)
 
+    def repl(self):
+        self.show_banner()
+        while self.read_exec_command():
+            yield
+        self.close()
+
 
 class HMPShell(QMPShell):
     def __init__(self, address, pretty=False, verbose=False):
@@ -435,10 +441,8 @@ def main():
     except OSError as err:
         die(f"Couldn't connect to {args.qmp_server}: {err!s}")
 
-    qemu.show_banner()
-    while qemu.read_exec_command():
+    for _ in qemu.repl():
         pass
-    qemu.close()
 
 
 if __name__ == '__main__':
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 23/42] scripts/qmp-shell: Fix "FuzzyJSON" parser
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (21 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 22/42] scripts/qmp-shell: move the REPL functionality into QMPShell John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 24/42] scripts/qmp-shell: refactor QMPCompleter John Snow
                   ` (19 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

I'm not sure when this regressed (Or maybe if it was ever working right
to begin with?), but the Python AST requires you to change "Names" to
"Constants" in order to truly convert `false` to `False`.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index aa148517a8..847d34890f 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -95,18 +95,19 @@ class QMPShellError(Exception):
 class FuzzyJSON(ast.NodeTransformer):
     """
     This extension of ast.NodeTransformer filters literal "true/false/null"
-    values in an AST and replaces them by proper "True/False/None" values that
-    Python can properly evaluate.
+    values in a Python AST and replaces them by proper "True/False/None" values
+    that Python can properly evaluate.
     """
 
     @classmethod
-    def visit_Name(cls, node):  # pylint: disable=invalid-name
+    def visit_Name(cls,  # pylint: disable=invalid-name
+                   node: ast.Name) -> ast.AST:
         if node.id == 'true':
-            node.id = 'True'
+            return ast.Constant(value=True)
         if node.id == 'false':
-            node.id = 'False'
+            return ast.Constant(value=False)
         if node.id == 'null':
-            node.id = 'None'
+            return ast.Constant(value=None)
         return node
 
 
@@ -174,10 +175,9 @@ class QMPShell(qmp.QEMUMonitorProtocol):
             # Try once again as FuzzyJSON:
             try:
                 tree = ast.parse(val, mode='eval')
-                return ast.literal_eval(FuzzyJSON().visit(tree))
-            except SyntaxError:
-                pass
-            except ValueError:
+                transformed = FuzzyJSON().visit(tree)
+                return ast.literal_eval(transformed)
+            except (SyntaxError, ValueError):
                 pass
         return val
 
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 24/42] scripts/qmp-shell: refactor QMPCompleter
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (22 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 23/42] scripts/qmp-shell: Fix "FuzzyJSON" parser John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 25/42] scripts/qmp-shell: initialize completer early John Snow
                   ` (18 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

list is a generic type, but we expect to use strings directly. We could
subclass list[str], but pylint does not presently understand that
invocation.

Change this class to envelop a list instead of *being* a list, for
simpler mypy typing.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell | 14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index 847d34890f..73694035b2 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -78,9 +78,17 @@ sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
 from qemu import qmp
 
 
-class QMPCompleter(list):
-    def complete(self, text, state):
-        for cmd in self:
+class QMPCompleter:
+    # NB: Python 3.9+ will probably allow us to subclass list[str] directly,
+    # but pylint as of today does not know that List[str] is simply 'list'.
+    def __init__(self) -> None:
+        self._matches: List[str] = []
+
+    def append(self, value: str) -> None:
+        return self._matches.append(value)
+
+    def complete(self, text: str, state: int) -> Optional[str]:
+        for cmd in self._matches:
             if cmd.startswith(text):
                 if state == 0:
                     return cmd
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 25/42] scripts/qmp-shell: initialize completer early
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (23 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 24/42] scripts/qmp-shell: refactor QMPCompleter John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 26/42] python/qmp: add QMPObject type alias John Snow
                   ` (17 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

Add an empty completer as a more type-safe placeholder instead of
'None'.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index 73694035b2..670361322c 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -125,7 +125,7 @@ class QMPShell(qmp.QEMUMonitorProtocol):
     def __init__(self, address, pretty=False, verbose=False):
         super().__init__(self.parse_address(address))
         self._greeting = None
-        self._completer = None
+        self._completer = QMPCompleter()
         self._pretty = pretty
         self._transmode = False
         self._actions = list()
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 26/42] python/qmp: add QMPObject type alias
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (24 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 25/42] scripts/qmp-shell: initialize completer early John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 27/42] scripts/qmp-shell: add mypy types John Snow
                   ` (16 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

This is meant to represent any generic object seen in a QMPMessage, not
just the root object itself.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 python/qemu/qmp/__init__.py | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/python/qemu/qmp/__init__.py b/python/qemu/qmp/__init__.py
index a6e1a7b857..ba0d2281d6 100644
--- a/python/qemu/qmp/__init__.py
+++ b/python/qemu/qmp/__init__.py
@@ -41,6 +41,9 @@
 #: QMPReturnValue is the 'return' value of a command.
 QMPReturnValue = object
 
+#: QMPObject is any object in a QMP message.
+QMPObject = Dict[str, object]
+
 # QMPMessage can be outgoing commands or incoming events/returns.
 # QMPReturnValue is usually a dict/json object, but due to QAPI's
 # 'returns-whitelist', it can actually be anything.
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 27/42] scripts/qmp-shell: add mypy types
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (25 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 26/42] python/qmp: add QMPObject type alias John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 28/42] scripts/qmp-shell: Accept SocketAddrT instead of string John Snow
                   ` (15 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

As per my usual, this patch is annotations only. Any changes with side
effects are done elsewhere.

Note: pylint does not understand the subscripts for Collection in Python 3.6,
so use the stronger Sequence type as a workaround.
Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell | 67 ++++++++++++++++++++++++++-----------------
 1 file changed, 41 insertions(+), 26 deletions(-)

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index 670361322c..2d0e85b5f7 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -72,10 +72,18 @@ import os
 import re
 import readline
 import sys
+from typing import (
+    Iterator,
+    List,
+    NoReturn,
+    Optional,
+    Sequence,
+)
 
 
 sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
 from qemu import qmp
+from qemu.qmp import QMPMessage
 
 
 class QMPCompleter:
@@ -122,25 +130,26 @@ class FuzzyJSON(ast.NodeTransformer):
 # TODO: QMPShell's interface is a bit ugly (eg. _fill_completion() and
 #       _execute_cmd()). Let's design a better one.
 class QMPShell(qmp.QEMUMonitorProtocol):
-    def __init__(self, address, pretty=False, verbose=False):
+    def __init__(self, address: str, pretty: bool = False,
+                 verbose: bool = False):
         super().__init__(self.parse_address(address))
-        self._greeting = None
+        self._greeting: Optional[QMPMessage] = None
         self._completer = QMPCompleter()
         self._pretty = pretty
         self._transmode = False
-        self._actions = list()
+        self._actions: List[QMPMessage] = []
         self._histfile = os.path.join(os.path.expanduser('~'),
                                       '.qmp-shell_history')
         self.verbose = verbose
 
-    def _fill_completion(self):
+    def _fill_completion(self) -> None:
         cmds = self.cmd('query-commands')
         if 'error' in cmds:
             return
         for cmd in cmds['return']:
             self._completer.append(cmd['name'])
 
-    def __completer_setup(self):
+    def __completer_setup(self) -> None:
         self._completer = QMPCompleter()
         self._fill_completion()
         readline.set_history_length(1024)
@@ -157,14 +166,14 @@ class QMPShell(qmp.QEMUMonitorProtocol):
             print(f"Failed to read history '{self._histfile}': {err!s}")
         atexit.register(self.__save_history)
 
-    def __save_history(self):
+    def __save_history(self) -> None:
         try:
             readline.write_history_file(self._histfile)
         except IOError as err:
             print(f"Failed to save history file '{self._histfile}': {err!s}")
 
     @classmethod
-    def __parse_value(cls, val):
+    def __parse_value(cls, val: str) -> object:
         try:
             return int(val)
         except ValueError:
@@ -189,7 +198,9 @@ class QMPShell(qmp.QEMUMonitorProtocol):
                 pass
         return val
 
-    def __cli_expr(self, tokens, parent):
+    def __cli_expr(self,
+                   tokens: Sequence[str],
+                   parent: qmp.QMPObject) -> None:
         for arg in tokens:
             (key, sep, val) = arg.partition('=')
             if sep != '=':
@@ -215,7 +226,7 @@ class QMPShell(qmp.QEMUMonitorProtocol):
                 raise QMPShellError(f'Cannot set "{key}" multiple times')
             parent[optpath[-1]] = value
 
-    def __build_cmd(self, cmdline):
+    def __build_cmd(self, cmdline: str) -> Optional[QMPMessage]:
         """
         Build a QMP input object from a user provided command-line in the
         following format:
@@ -224,6 +235,7 @@ class QMPShell(qmp.QEMUMonitorProtocol):
         """
         argument_regex = r'''(?:[^\s"']|"(?:\\.|[^"])*"|'(?:\\.|[^'])*')+'''
         cmdargs = re.findall(argument_regex, cmdline)
+        qmpcmd: QMPMessage
 
         # Transactional CLI entry/exit:
         if cmdargs[0] == 'transaction(':
@@ -261,14 +273,14 @@ class QMPShell(qmp.QEMUMonitorProtocol):
         self.__cli_expr(cmdargs[1:], qmpcmd['arguments'])
         return qmpcmd
 
-    def _print(self, qmp_message):
+    def _print(self, qmp_message: object) -> None:
         indent = None
         if self._pretty:
             indent = 4
         jsobj = json.dumps(qmp_message, indent=indent, sort_keys=self._pretty)
         print(str(jsobj))
 
-    def _execute_cmd(self, cmdline):
+    def _execute_cmd(self, cmdline: str) -> bool:
         try:
             qmpcmd = self.__build_cmd(cmdline)
         except Exception as err:
@@ -288,11 +300,12 @@ class QMPShell(qmp.QEMUMonitorProtocol):
         self._print(resp)
         return True
 
-    def connect(self, negotiate: bool = True):
+    def connect(self, negotiate: bool = True) -> None:
         self._greeting = super().connect(negotiate)
         self.__completer_setup()
 
-    def show_banner(self, msg='Welcome to the QMP low-level shell!'):
+    def show_banner(self,
+                    msg: str = 'Welcome to the QMP low-level shell!') -> None:
         print(msg)
         if not self._greeting:
             print('Connected')
@@ -301,12 +314,12 @@ class QMPShell(qmp.QEMUMonitorProtocol):
         print("Connected to QEMU {major}.{minor}.{micro}\n".format(**version))
 
     @property
-    def prompt(self):
+    def prompt(self) -> str:
         if self._transmode:
             return 'TRANS> '
         return '(QEMU) '
 
-    def read_exec_command(self):
+    def read_exec_command(self) -> bool:
         """
         Read and execute a command.
 
@@ -326,7 +339,7 @@ class QMPShell(qmp.QEMUMonitorProtocol):
 
         return self._execute_cmd(cmdline)
 
-    def repl(self):
+    def repl(self) -> Iterator[None]:
         self.show_banner()
         while self.read_exec_command():
             yield
@@ -334,11 +347,12 @@ class QMPShell(qmp.QEMUMonitorProtocol):
 
 
 class HMPShell(QMPShell):
-    def __init__(self, address, pretty=False, verbose=False):
+    def __init__(self, address: str,
+                 pretty: bool = False, verbose: bool = False):
         super().__init__(address, pretty, verbose)
         self.__cpu_index = 0
 
-    def __cmd_completion(self):
+    def __cmd_completion(self) -> None:
         for cmd in self.__cmd_passthrough('help')['return'].split('\r\n'):
             if cmd and cmd[0] != '[' and cmd[0] != '\t':
                 name = cmd.split()[0]  # drop help text
@@ -355,21 +369,22 @@ class HMPShell(QMPShell):
                 self._completer.append(name)
                 self._completer.append('help ' + name)  # help completion
 
-    def __info_completion(self):
+    def __info_completion(self) -> None:
         for cmd in self.__cmd_passthrough('info')['return'].split('\r\n'):
             if cmd:
                 self._completer.append('info ' + cmd.split()[1])
 
-    def __other_completion(self):
+    def __other_completion(self) -> None:
         # special cases
         self._completer.append('help info')
 
-    def _fill_completion(self):
+    def _fill_completion(self) -> None:
         self.__cmd_completion()
         self.__info_completion()
         self.__other_completion()
 
-    def __cmd_passthrough(self, cmdline, cpu_index=0):
+    def __cmd_passthrough(self, cmdline: str,
+                          cpu_index: int = 0) -> QMPMessage:
         return self.cmd_obj({
             'execute': 'human-monitor-command',
             'arguments': {
@@ -378,7 +393,7 @@ class HMPShell(QMPShell):
             }
         })
 
-    def _execute_cmd(self, cmdline):
+    def _execute_cmd(self, cmdline: str) -> bool:
         if cmdline.split()[0] == "cpu":
             # trap the cpu command, it requires special setting
             try:
@@ -404,16 +419,16 @@ class HMPShell(QMPShell):
             print('%s: %s' % (resp['error']['class'], resp['error']['desc']))
         return True
 
-    def show_banner(self, msg='Welcome to the HMP shell!'):
+    def show_banner(self, msg: str = 'Welcome to the HMP shell!') -> None:
         QMPShell.show_banner(self, msg)
 
 
-def die(msg):
+def die(msg: str) -> NoReturn:
     sys.stderr.write('ERROR: %s\n' % msg)
     sys.exit(1)
 
 
-def main():
+def main() -> None:
     parser = argparse.ArgumentParser()
     parser.add_argument('-H', '--hmp', action='store_true',
                         help='Use HMP interface')
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 28/42] scripts/qmp-shell: Accept SocketAddrT instead of string
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (26 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 27/42] scripts/qmp-shell: add mypy types John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 29/42] scripts/qmp-shell: unprivatize 'pretty' property John Snow
                   ` (14 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

Don't "extend" QEMUMonitorProtocol by changing the argument types. Move
the string parsing just outside of the class instead.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index 2d0e85b5f7..b465c7f9e2 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -130,9 +130,9 @@ class FuzzyJSON(ast.NodeTransformer):
 # TODO: QMPShell's interface is a bit ugly (eg. _fill_completion() and
 #       _execute_cmd()). Let's design a better one.
 class QMPShell(qmp.QEMUMonitorProtocol):
-    def __init__(self, address: str, pretty: bool = False,
-                 verbose: bool = False):
-        super().__init__(self.parse_address(address))
+    def __init__(self, address: qmp.SocketAddrT,
+                 pretty: bool = False, verbose: bool = False):
+        super().__init__(address)
         self._greeting: Optional[QMPMessage] = None
         self._completer = QMPCompleter()
         self._pretty = pretty
@@ -347,7 +347,7 @@ class QMPShell(qmp.QEMUMonitorProtocol):
 
 
 class HMPShell(QMPShell):
-    def __init__(self, address: str,
+    def __init__(self, address: qmp.SocketAddrT,
                  pretty: bool = False, verbose: bool = False):
         super().__init__(address, pretty, verbose)
         self.__cpu_index = 0
@@ -450,11 +450,13 @@ def main() -> None:
 
     shell_class = HMPShell if args.hmp else QMPShell
     try:
-        qemu = shell_class(args.qmp_server, args.pretty, args.verbose)
+        address = shell_class.parse_address(args.qmp_server)
     except qmp.QMPBadPortError:
         parser.error(f"Bad port number: {args.qmp_server}")
         return  # pycharm doesn't know error() is noreturn
 
+    qemu = shell_class(address, args.pretty, args.verbose)
+
     try:
         qemu.connect(negotiate=not args.skip_negotiation)
     except qmp.QMPConnectError:
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 29/42] scripts/qmp-shell: unprivatize 'pretty' property
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (27 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 28/42] scripts/qmp-shell: Accept SocketAddrT instead of string John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 30/42] python/qmp: return generic type from context manager John Snow
                   ` (13 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

Similar to verbose, there's no reason this needs to be hidden.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index b465c7f9e2..f14fe211cc 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -135,11 +135,11 @@ class QMPShell(qmp.QEMUMonitorProtocol):
         super().__init__(address)
         self._greeting: Optional[QMPMessage] = None
         self._completer = QMPCompleter()
-        self._pretty = pretty
         self._transmode = False
         self._actions: List[QMPMessage] = []
         self._histfile = os.path.join(os.path.expanduser('~'),
                                       '.qmp-shell_history')
+        self.pretty = pretty
         self.verbose = verbose
 
     def _fill_completion(self) -> None:
@@ -274,10 +274,9 @@ class QMPShell(qmp.QEMUMonitorProtocol):
         return qmpcmd
 
     def _print(self, qmp_message: object) -> None:
-        indent = None
-        if self._pretty:
-            indent = 4
-        jsobj = json.dumps(qmp_message, indent=indent, sort_keys=self._pretty)
+        jsobj = json.dumps(qmp_message,
+                           indent=4 if self.pretty else None,
+                           sort_keys=self.pretty)
         print(str(jsobj))
 
     def _execute_cmd(self, cmdline: str) -> bool:
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 30/42] python/qmp: return generic type from context manager
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (28 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 29/42] scripts/qmp-shell: unprivatize 'pretty' property John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 31/42] scripts/qmp-shell: Use context manager instead of atexit John Snow
                   ` (12 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

__enter__ can be invoked from a subclass, so it needs a more flexible
type.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 python/qemu/qmp/__init__.py | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/python/qemu/qmp/__init__.py b/python/qemu/qmp/__init__.py
index ba0d2281d6..376954cb6d 100644
--- a/python/qemu/qmp/__init__.py
+++ b/python/qemu/qmp/__init__.py
@@ -30,6 +30,7 @@
     TextIO,
     Tuple,
     Type,
+    TypeVar,
     Union,
     cast,
 )
@@ -220,7 +221,9 @@ def __get_events(self, wait: Union[bool, float] = False) -> None:
             if ret is None:
                 raise QMPConnectError("Error while reading from socket")
 
-    def __enter__(self) -> 'QEMUMonitorProtocol':
+    T = TypeVar('T')
+
+    def __enter__(self: T) -> T:
         # Implement context manager enter function.
         return self
 
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 31/42] scripts/qmp-shell: Use context manager instead of atexit
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (29 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 30/42] python/qmp: return generic type from context manager John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 32/42] scripts/qmp-shell: use logging to show warnings John Snow
                   ` (11 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

We can invoke the shell history writing when we leave the QMPShell scope
instead of relying on atexit. Doing so may be preferable to avoid global
state being registered from within a class instead of from the
application logic directly.

Use QMP's context manager to hook this history saving at close time,
which gets invoked when we leave the context block.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell | 33 ++++++++++++++++++---------------
 1 file changed, 18 insertions(+), 15 deletions(-)

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index f14fe211cc..ec028d662e 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -66,7 +66,6 @@
 # sent to QEMU, which is useful for debugging and documentation generation.
 import argparse
 import ast
-import atexit
 import json
 import os
 import re
@@ -142,6 +141,11 @@ class QMPShell(qmp.QEMUMonitorProtocol):
         self.pretty = pretty
         self.verbose = verbose
 
+    def close(self) -> None:
+        # Hook into context manager of parent to save shell history.
+        self._save_history()
+        super().close()
+
     def _fill_completion(self) -> None:
         cmds = self.cmd('query-commands')
         if 'error' in cmds:
@@ -164,9 +168,8 @@ class QMPShell(qmp.QEMUMonitorProtocol):
             pass
         except IOError as err:
             print(f"Failed to read history '{self._histfile}': {err!s}")
-        atexit.register(self.__save_history)
 
-    def __save_history(self) -> None:
+    def _save_history(self) -> None:
         try:
             readline.write_history_file(self._histfile)
         except IOError as err:
@@ -448,25 +451,25 @@ def main() -> None:
         parser.error("QMP socket or TCP address must be specified")
 
     shell_class = HMPShell if args.hmp else QMPShell
+
     try:
         address = shell_class.parse_address(args.qmp_server)
     except qmp.QMPBadPortError:
         parser.error(f"Bad port number: {args.qmp_server}")
         return  # pycharm doesn't know error() is noreturn
 
-    qemu = shell_class(address, args.pretty, args.verbose)
+    with shell_class(address, args.pretty, args.verbose) as qemu:
+        try:
+            qemu.connect(negotiate=not args.skip_negotiation)
+        except qmp.QMPConnectError:
+            die("Didn't get QMP greeting message")
+        except qmp.QMPCapabilitiesError:
+            die("Couldn't negotiate capabilities")
+        except OSError as err:
+            die(f"Couldn't connect to {args.qmp_server}: {err!s}")
 
-    try:
-        qemu.connect(negotiate=not args.skip_negotiation)
-    except qmp.QMPConnectError:
-        die("Didn't get QMP greeting message")
-    except qmp.QMPCapabilitiesError:
-        die("Couldn't negotiate capabilities")
-    except OSError as err:
-        die(f"Couldn't connect to {args.qmp_server}: {err!s}")
-
-    for _ in qemu.repl():
-        pass
+        for _ in qemu.repl():
+            pass
 
 
 if __name__ == '__main__':
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 32/42] scripts/qmp-shell: use logging to show warnings
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (30 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 31/42] scripts/qmp-shell: Use context manager instead of atexit John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 33/42] scripts/qmp-shell: remove TODO John Snow
                   ` (10 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

A perfect candidate is non-fatal shell history messages.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index ec028d662e..0199a13a34 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -67,6 +67,7 @@
 import argparse
 import ast
 import json
+import logging
 import os
 import re
 import readline
@@ -85,6 +86,9 @@ from qemu import qmp
 from qemu.qmp import QMPMessage
 
 
+LOG = logging.getLogger(__name__)
+
+
 class QMPCompleter:
     # NB: Python 3.9+ will probably allow us to subclass list[str] directly,
     # but pylint as of today does not know that List[str] is simply 'list'.
@@ -167,13 +171,15 @@ class QMPShell(qmp.QEMUMonitorProtocol):
         except FileNotFoundError:
             pass
         except IOError as err:
-            print(f"Failed to read history '{self._histfile}': {err!s}")
+            msg = f"Failed to read history '{self._histfile}': {err!s}"
+            LOG.warning(msg)
 
     def _save_history(self) -> None:
         try:
             readline.write_history_file(self._histfile)
         except IOError as err:
-            print(f"Failed to save history file '{self._histfile}': {err!s}")
+            msg = f"Failed to save history file '{self._histfile}': {err!s}"
+            LOG.warning(msg)
 
     @classmethod
     def __parse_value(cls, val: str) -> object:
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 33/42] scripts/qmp-shell: remove TODO
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (31 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 32/42] scripts/qmp-shell: use logging to show warnings John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 34/42] scripts/qmp-shell: Fix empty-transaction invocation John Snow
                   ` (9 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

We still want to revamp qmp-shell again, but there's much more to the
idea than the comment now intuits. Remove it.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell | 2 --
 1 file changed, 2 deletions(-)

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index 0199a13a34..3c32b576a3 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -130,8 +130,6 @@ class FuzzyJSON(ast.NodeTransformer):
         return node
 
 
-# TODO: QMPShell's interface is a bit ugly (eg. _fill_completion() and
-#       _execute_cmd()). Let's design a better one.
 class QMPShell(qmp.QEMUMonitorProtocol):
     def __init__(self, address: qmp.SocketAddrT,
                  pretty: bool = False, verbose: bool = False):
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 34/42] scripts/qmp-shell: Fix empty-transaction invocation
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (32 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 33/42] scripts/qmp-shell: remove TODO John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 35/42] scripts/qmp-shell: Remove too-broad-exception John Snow
                   ` (8 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

calling "transaction( )" is pointless, but valid. Rework the parser to
allow this kind of invocation. This helps clean up exception handling
later by removing accidental breakages of the parser that aren't
explicitly forbidden.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index 3c32b576a3..78e4eae007 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -244,11 +244,14 @@ class QMPShell(qmp.QEMUMonitorProtocol):
         cmdargs = re.findall(argument_regex, cmdline)
         qmpcmd: QMPMessage
 
-        # Transactional CLI entry/exit:
-        if cmdargs[0] == 'transaction(':
+        # Transactional CLI entry:
+        if cmdargs and cmdargs[0] == 'transaction(':
             self._transmode = True
+            self._actions = []
             cmdargs.pop(0)
-        elif cmdargs[0] == ')' and self._transmode:
+
+        # Transactional CLI exit:
+        if cmdargs and cmdargs[0] == ')' and self._transmode:
             self._transmode = False
             if len(cmdargs) > 1:
                 msg = 'Unexpected input after close of Transaction sub-shell'
@@ -257,15 +260,14 @@ class QMPShell(qmp.QEMUMonitorProtocol):
                 'execute': 'transaction',
                 'arguments': {'actions': self._actions}
             }
-            self._actions = list()
             return qmpcmd
 
-        # Nothing to process?
+        # No args, or no args remaining
         if not cmdargs:
             return None
 
-        # Parse and then cache this Transactional Action
         if self._transmode:
+            # Parse and cache this Transactional Action
             finalize = False
             action = {'type': cmdargs[0], 'data': {}}
             if cmdargs[-1] == ')':
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 35/42] scripts/qmp-shell: Remove too-broad-exception
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (33 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 34/42] scripts/qmp-shell: Fix empty-transaction invocation John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 36/42] scripts/qmp-shell: convert usage comment to docstring John Snow
                   ` (7 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

We are only anticipating QMPShellErrors here, for syntax we weren't able
to understand. Other errors, if any, should be allowed to percolate
upwards.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index 78e4eae007..8d5845ab48 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -291,10 +291,13 @@ class QMPShell(qmp.QEMUMonitorProtocol):
     def _execute_cmd(self, cmdline: str) -> bool:
         try:
             qmpcmd = self.__build_cmd(cmdline)
-        except Exception as err:
-            print('Error while parsing command line: %s' % err)
-            print('command format: <command-name> ', end=' ')
-            print('[arg-name1=arg1] ... [arg-nameN=argN]')
+        except QMPShellError as err:
+            print(
+                f"Error while parsing command line: {err!s}\n"
+                "command format: <command-name> "
+                "[arg-name1=arg1] ... [arg-nameN=argN",
+                file=sys.stderr
+            )
             return True
         # For transaction mode, we may have just cached the action:
         if qmpcmd is None:
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 36/42] scripts/qmp-shell: convert usage comment to docstring
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (34 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 35/42] scripts/qmp-shell: Remove too-broad-exception John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 37/42] scripts/qmp-shell: remove double-underscores John Snow
                   ` (6 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

The nice usage comment should be a docstring instead of a comment, so
that it's visible from other python tooling.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell | 128 ++++++++++++++++++++++++------------------
 1 file changed, 72 insertions(+), 56 deletions(-)

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index 8d5845ab48..82fe16cff8 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -1,7 +1,5 @@
 #!/usr/bin/env python3
 #
-# Low-level QEMU shell on top of QMP.
-#
 # Copyright (C) 2009, 2010 Red Hat Inc.
 #
 # Authors:
@@ -10,60 +8,78 @@
 # This work is licensed under the terms of the GNU GPL, version 2.  See
 # the COPYING file in the top-level directory.
 #
-# Usage:
-#
-# Start QEMU with:
-#
-# # qemu [...] -qmp unix:./qmp-sock,server
-#
-# Run the shell:
-#
-# $ qmp-shell ./qmp-sock
-#
-# Commands have the following format:
-#
-#    < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ]
-#
-# For example:
-#
-# (QEMU) device_add driver=e1000 id=net1
-# {u'return': {}}
-# (QEMU)
-#
-# key=value pairs also support Python or JSON object literal subset notations,
-# without spaces. Dictionaries/objects {} are supported as are arrays [].
-#
-#    example-command arg-name1={'key':'value','obj'={'prop':"value"}}
-#
-# Both JSON and Python formatting should work, including both styles of
-# string literal quotes. Both paradigms of literal values should work,
-# including null/true/false for JSON and None/True/False for Python.
-#
-#
-# Transactions have the following multi-line format:
-#
-#    transaction(
-#    action-name1 [ arg-name1=arg1 ] ... [arg-nameN=argN ]
-#    ...
-#    action-nameN [ arg-name1=arg1 ] ... [arg-nameN=argN ]
-#    )
-#
-# One line transactions are also supported:
-#
-#    transaction( action-name1 ... )
-#
-# For example:
-#
-#     (QEMU) transaction(
-#     TRANS> block-dirty-bitmap-add node=drive0 name=bitmap1
-#     TRANS> block-dirty-bitmap-clear node=drive0 name=bitmap0
-#     TRANS> )
-#     {"return": {}}
-#     (QEMU)
-#
-# Use the -v and -p options to activate the verbose and pretty-print options,
-# which will echo back the properly formatted JSON-compliant QMP that is being
-# sent to QEMU, which is useful for debugging and documentation generation.
+
+"""
+Low-level QEMU shell on top of QMP.
+
+usage: qmp-shell [-h] [-H] [-N] [-v] [-p] qmp_server
+
+positional arguments:
+  qmp_server            < UNIX socket path | TCP address:port >
+
+optional arguments:
+  -h, --help            show this help message and exit
+  -H, --hmp             Use HMP interface
+  -N, --skip-negotiation
+                        Skip negotiate (for qemu-ga)
+  -v, --verbose         Verbose (echo commands sent and received)
+  -p, --pretty          Pretty-print JSON
+
+
+Start QEMU with:
+
+# qemu [...] -qmp unix:./qmp-sock,server
+
+Run the shell:
+
+$ qmp-shell ./qmp-sock
+
+Commands have the following format:
+
+   < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ]
+
+For example:
+
+(QEMU) device_add driver=e1000 id=net1
+{'return': {}}
+(QEMU)
+
+key=value pairs also support Python or JSON object literal subset notations,
+without spaces. Dictionaries/objects {} are supported as are arrays [].
+
+   example-command arg-name1={'key':'value','obj'={'prop':"value"}}
+
+Both JSON and Python formatting should work, including both styles of
+string literal quotes. Both paradigms of literal values should work,
+including null/true/false for JSON and None/True/False for Python.
+
+
+Transactions have the following multi-line format:
+
+   transaction(
+   action-name1 [ arg-name1=arg1 ] ... [arg-nameN=argN ]
+   ...
+   action-nameN [ arg-name1=arg1 ] ... [arg-nameN=argN ]
+   )
+
+One line transactions are also supported:
+
+   transaction( action-name1 ... )
+
+For example:
+
+    (QEMU) transaction(
+    TRANS> block-dirty-bitmap-add node=drive0 name=bitmap1
+    TRANS> block-dirty-bitmap-clear node=drive0 name=bitmap0
+    TRANS> )
+    {"return": {}}
+    (QEMU)
+
+Use the -v and -p options to activate the verbose and pretty-print options,
+which will echo back the properly formatted JSON-compliant QMP that is being
+sent to QEMU, which is useful for debugging and documentation generation.
+"""
+
 import argparse
 import ast
 import json
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 37/42] scripts/qmp-shell: remove double-underscores
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (35 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 36/42] scripts/qmp-shell: convert usage comment to docstring John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 38/42] scripts/qmp-shell: make QMPShellError inherit QMPError John Snow
                   ` (5 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

They're not needed; single underscore is enough to express intent that
these methods are "internal". double underscore is used as a weak name
mangling, but that isn't beneficial for us here.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell | 52 +++++++++++++++++++++----------------------
 1 file changed, 26 insertions(+), 26 deletions(-)

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index 82fe16cff8..40ff9e0a82 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -171,7 +171,7 @@ class QMPShell(qmp.QEMUMonitorProtocol):
         for cmd in cmds['return']:
             self._completer.append(cmd['name'])
 
-    def __completer_setup(self) -> None:
+    def _completer_setup(self) -> None:
         self._completer = QMPCompleter()
         self._fill_completion()
         readline.set_history_length(1024)
@@ -196,7 +196,7 @@ class QMPShell(qmp.QEMUMonitorProtocol):
             LOG.warning(msg)
 
     @classmethod
-    def __parse_value(cls, val: str) -> object:
+    def _parse_value(cls, val: str) -> object:
         try:
             return int(val)
         except ValueError:
@@ -221,9 +221,9 @@ class QMPShell(qmp.QEMUMonitorProtocol):
                 pass
         return val
 
-    def __cli_expr(self,
-                   tokens: Sequence[str],
-                   parent: qmp.QMPObject) -> None:
+    def _cli_expr(self,
+                  tokens: Sequence[str],
+                  parent: qmp.QMPObject) -> None:
         for arg in tokens:
             (key, sep, val) = arg.partition('=')
             if sep != '=':
@@ -231,7 +231,7 @@ class QMPShell(qmp.QEMUMonitorProtocol):
                     f"Expected a key=value pair, got '{arg!s}'"
                 )
 
-            value = self.__parse_value(val)
+            value = self._parse_value(val)
             optpath = key.split('.')
             curpath = []
             for path in optpath[:-1]:
@@ -249,7 +249,7 @@ class QMPShell(qmp.QEMUMonitorProtocol):
                 raise QMPShellError(f'Cannot set "{key}" multiple times')
             parent[optpath[-1]] = value
 
-    def __build_cmd(self, cmdline: str) -> Optional[QMPMessage]:
+    def _build_cmd(self, cmdline: str) -> Optional[QMPMessage]:
         """
         Build a QMP input object from a user provided command-line in the
         following format:
@@ -289,13 +289,13 @@ class QMPShell(qmp.QEMUMonitorProtocol):
             if cmdargs[-1] == ')':
                 cmdargs.pop(-1)
                 finalize = True
-            self.__cli_expr(cmdargs[1:], action['data'])
+            self._cli_expr(cmdargs[1:], action['data'])
             self._actions.append(action)
-            return self.__build_cmd(')') if finalize else None
+            return self._build_cmd(')') if finalize else None
 
         # Standard command: parse and return it to be executed.
         qmpcmd = {'execute': cmdargs[0], 'arguments': {}}
-        self.__cli_expr(cmdargs[1:], qmpcmd['arguments'])
+        self._cli_expr(cmdargs[1:], qmpcmd['arguments'])
         return qmpcmd
 
     def _print(self, qmp_message: object) -> None:
@@ -306,7 +306,7 @@ class QMPShell(qmp.QEMUMonitorProtocol):
 
     def _execute_cmd(self, cmdline: str) -> bool:
         try:
-            qmpcmd = self.__build_cmd(cmdline)
+            qmpcmd = self._build_cmd(cmdline)
         except QMPShellError as err:
             print(
                 f"Error while parsing command line: {err!s}\n"
@@ -329,7 +329,7 @@ class QMPShell(qmp.QEMUMonitorProtocol):
 
     def connect(self, negotiate: bool = True) -> None:
         self._greeting = super().connect(negotiate)
-        self.__completer_setup()
+        self._completer_setup()
 
     def show_banner(self,
                     msg: str = 'Welcome to the QMP low-level shell!') -> None:
@@ -377,10 +377,10 @@ class HMPShell(QMPShell):
     def __init__(self, address: qmp.SocketAddrT,
                  pretty: bool = False, verbose: bool = False):
         super().__init__(address, pretty, verbose)
-        self.__cpu_index = 0
+        self._cpu_index = 0
 
-    def __cmd_completion(self) -> None:
-        for cmd in self.__cmd_passthrough('help')['return'].split('\r\n'):
+    def _cmd_completion(self) -> None:
+        for cmd in self._cmd_passthrough('help')['return'].split('\r\n'):
             if cmd and cmd[0] != '[' and cmd[0] != '\t':
                 name = cmd.split()[0]  # drop help text
                 if name == 'info':
@@ -396,22 +396,22 @@ class HMPShell(QMPShell):
                 self._completer.append(name)
                 self._completer.append('help ' + name)  # help completion
 
-    def __info_completion(self) -> None:
-        for cmd in self.__cmd_passthrough('info')['return'].split('\r\n'):
+    def _info_completion(self) -> None:
+        for cmd in self._cmd_passthrough('info')['return'].split('\r\n'):
             if cmd:
                 self._completer.append('info ' + cmd.split()[1])
 
-    def __other_completion(self) -> None:
+    def _other_completion(self) -> None:
         # special cases
         self._completer.append('help info')
 
     def _fill_completion(self) -> None:
-        self.__cmd_completion()
-        self.__info_completion()
-        self.__other_completion()
+        self._cmd_completion()
+        self._info_completion()
+        self._other_completion()
 
-    def __cmd_passthrough(self, cmdline: str,
-                          cpu_index: int = 0) -> QMPMessage:
+    def _cmd_passthrough(self, cmdline: str,
+                         cpu_index: int = 0) -> QMPMessage:
         return self.cmd_obj({
             'execute': 'human-monitor-command',
             'arguments': {
@@ -425,14 +425,14 @@ class HMPShell(QMPShell):
             # trap the cpu command, it requires special setting
             try:
                 idx = int(cmdline.split()[1])
-                if 'return' not in self.__cmd_passthrough('info version', idx):
+                if 'return' not in self._cmd_passthrough('info version', idx):
                     print('bad CPU index')
                     return True
-                self.__cpu_index = idx
+                self._cpu_index = idx
             except ValueError:
                 print('cpu command takes an integer argument')
                 return True
-        resp = self.__cmd_passthrough(cmdline, self.__cpu_index)
+        resp = self._cmd_passthrough(cmdline, self._cpu_index)
         if resp is None:
             print('Disconnected')
             return False
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 38/42] scripts/qmp-shell: make QMPShellError inherit QMPError
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (36 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 37/42] scripts/qmp-shell: remove double-underscores John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 39/42] scripts/qmp-shell: add docstrings John Snow
                   ` (4 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

In preparation for moving qmp-shell into the qemu.qmp package, make
QMPShellError inherit from QMPError so that all custom errors in this
package all derive from QMPError.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index 40ff9e0a82..1a8a4ba18a 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -123,7 +123,7 @@ class QMPCompleter:
         return None
 
 
-class QMPShellError(Exception):
+class QMPShellError(qmp.QMPError):
     pass
 
 
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 39/42] scripts/qmp-shell: add docstrings
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (37 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 38/42] scripts/qmp-shell: make QMPShellError inherit QMPError John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 40/42] scripts/qmp-shell: move to python/qemu/qmp/qmp_shell.py John Snow
                   ` (3 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell | 39 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 38 insertions(+), 1 deletion(-)

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index 1a8a4ba18a..15aedb80c2 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -106,15 +106,20 @@ LOG = logging.getLogger(__name__)
 
 
 class QMPCompleter:
+    """
+    QMPCompleter provides a readline library tab-complete behavior.
+    """
     # NB: Python 3.9+ will probably allow us to subclass list[str] directly,
     # but pylint as of today does not know that List[str] is simply 'list'.
     def __init__(self) -> None:
         self._matches: List[str] = []
 
     def append(self, value: str) -> None:
+        """Append a new valid completion to the list of possibilities."""
         return self._matches.append(value)
 
     def complete(self, text: str, state: int) -> Optional[str]:
+        """readline.set_completer() callback implementation."""
         for cmd in self._matches:
             if cmd.startswith(text):
                 if state == 0:
@@ -124,7 +129,9 @@ class QMPCompleter:
 
 
 class QMPShellError(qmp.QMPError):
-    pass
+    """
+    QMP Shell Base error class.
+    """
 
 
 class FuzzyJSON(ast.NodeTransformer):
@@ -137,6 +144,9 @@ class FuzzyJSON(ast.NodeTransformer):
     @classmethod
     def visit_Name(cls,  # pylint: disable=invalid-name
                    node: ast.Name) -> ast.AST:
+        """
+        Transform Name nodes with certain values into Constant (keyword) nodes.
+        """
         if node.id == 'true':
             return ast.Constant(value=True)
         if node.id == 'false':
@@ -147,6 +157,13 @@ class FuzzyJSON(ast.NodeTransformer):
 
 
 class QMPShell(qmp.QEMUMonitorProtocol):
+    """
+    QMPShell provides a basic readline-based QMP shell.
+
+    :param address: Address of the QMP server.
+    :param pretty: Pretty-print QMP messages.
+    :param verbose: Echo outgoing QMP messages to console.
+    """
     def __init__(self, address: qmp.SocketAddrT,
                  pretty: bool = False, verbose: bool = False):
         super().__init__(address)
@@ -333,6 +350,9 @@ class QMPShell(qmp.QEMUMonitorProtocol):
 
     def show_banner(self,
                     msg: str = 'Welcome to the QMP low-level shell!') -> None:
+        """
+        Print to stdio a greeting, and the QEMU version if available.
+        """
         print(msg)
         if not self._greeting:
             print('Connected')
@@ -342,6 +362,9 @@ class QMPShell(qmp.QEMUMonitorProtocol):
 
     @property
     def prompt(self) -> str:
+        """
+        Return the current shell prompt, including a trailing space.
+        """
         if self._transmode:
             return 'TRANS> '
         return '(QEMU) '
@@ -367,6 +390,9 @@ class QMPShell(qmp.QEMUMonitorProtocol):
         return self._execute_cmd(cmdline)
 
     def repl(self) -> Iterator[None]:
+        """
+        Return an iterator that implements the REPL.
+        """
         self.show_banner()
         while self.read_exec_command():
             yield
@@ -374,6 +400,13 @@ class QMPShell(qmp.QEMUMonitorProtocol):
 
 
 class HMPShell(QMPShell):
+    """
+    HMPShell provides a basic readline-based HMP shell, tunnelled via QMP.
+
+    :param address: Address of the QMP server.
+    :param pretty: Pretty-print QMP messages.
+    :param verbose: Echo outgoing QMP messages to console.
+    """
     def __init__(self, address: qmp.SocketAddrT,
                  pretty: bool = False, verbose: bool = False):
         super().__init__(address, pretty, verbose)
@@ -451,11 +484,15 @@ class HMPShell(QMPShell):
 
 
 def die(msg: str) -> NoReturn:
+    """Write an error to stderr, then exit with a return code of 1."""
     sys.stderr.write('ERROR: %s\n' % msg)
     sys.exit(1)
 
 
 def main() -> None:
+    """
+    qmp-shell entry point: parse command line arguments and start the REPL.
+    """
     parser = argparse.ArgumentParser()
     parser.add_argument('-H', '--hmp', action='store_true',
                         help='Use HMP interface')
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 40/42] scripts/qmp-shell: move to python/qemu/qmp/qmp_shell.py
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (38 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 39/42] scripts/qmp-shell: add docstrings John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 41/42] python: add qmp-shell entry point John Snow
                   ` (2 subsequent siblings)
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

The script will be unavailable for a commit or two, which will help
preserve development history attached to the new file. A forwarder will
be added shortly afterwards.

With qmp_shell in the python qemu.qmp package, now it is fully type
checked, linted, etc. via the Python CI. It will be quite a bit harder
to accidentally break it again in the future.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell => python/qemu/qmp/qmp_shell.py | 3 ---
 1 file changed, 3 deletions(-)
 rename scripts/qmp/qmp-shell => python/qemu/qmp/qmp_shell.py (99%)
 mode change 100755 => 100644

diff --git a/scripts/qmp/qmp-shell b/python/qemu/qmp/qmp_shell.py
old mode 100755
new mode 100644
similarity index 99%
rename from scripts/qmp/qmp-shell
rename to python/qemu/qmp/qmp_shell.py
index 15aedb80c2..337acfce2d
--- a/scripts/qmp/qmp-shell
+++ b/python/qemu/qmp/qmp_shell.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python3
 #
 # Copyright (C) 2009, 2010 Red Hat Inc.
 #
@@ -96,8 +95,6 @@
     Sequence,
 )
 
-
-sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
 from qemu import qmp
 from qemu.qmp import QMPMessage
 
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 41/42] python: add qmp-shell entry point
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (39 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 40/42] scripts/qmp-shell: move to python/qemu/qmp/qmp_shell.py John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-07 20:06 ` [PATCH 42/42] scripts/qmp-shell: add redirection shim John Snow
  2021-06-14 18:09 ` [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

now 'qmp-shell' should be available from the command line when
installing the python package.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 python/setup.cfg | 1 +
 1 file changed, 1 insertion(+)

diff --git a/python/setup.cfg b/python/setup.cfg
index 7f3c59d74e..85cecbb41b 100644
--- a/python/setup.cfg
+++ b/python/setup.cfg
@@ -51,6 +51,7 @@ console_scripts =
     qom-tree = qemu.qmp.qom:QOMTree.entry_point
     qom-fuse = qemu.qmp.qom_fuse:QOMFuse.entry_point [fuse]
     qemu-ga-client = qemu.qmp.qemu_ga_client:main
+    qmp-shell = qemu.qmp.qmp_shell:main
 
 [flake8]
 extend-ignore = E722  # Prefer pylint's bare-except checks to flake8's
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 42/42] scripts/qmp-shell: add redirection shim
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (40 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 41/42] python: add qmp-shell entry point John Snow
@ 2021-06-07 20:06 ` John Snow
  2021-06-14 18:09 ` [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-07 20:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S .,
	Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost

qmp-shell has a new home, add a redirect for a little while as the dust
settles.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qmp/qmp-shell | 11 +++++++++++
 1 file changed, 11 insertions(+)
 create mode 100755 scripts/qmp/qmp-shell

diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
new file mode 100755
index 0000000000..4a20f97db7
--- /dev/null
+++ b/scripts/qmp/qmp-shell
@@ -0,0 +1,11 @@
+#!/usr/bin/env python3
+
+import os
+import sys
+
+sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
+from qemu.qmp import qmp_shell
+
+
+if __name__ == '__main__':
+    qmp_shell.main()
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* Re: [PATCH 00/42] python: move qmp-shell into qemu.qmp package
  2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
                   ` (41 preceding siblings ...)
  2021-06-07 20:06 ` [PATCH 42/42] scripts/qmp-shell: add redirection shim John Snow
@ 2021-06-14 18:09 ` John Snow
  42 siblings, 0 replies; 44+ messages in thread
From: John Snow @ 2021-06-14 18:09 UTC (permalink / raw)
  To: qemu-devel
  Cc: Niteesh G . S ., Cleber Rosa, Markus Armbruster, Eduardo Habkost

On 6/7/21 4:06 PM, John Snow wrote:
> Based-on: <20210603003719.1321369-1-jsnow@redhat.com>
> Based-on: <20210604155532.1499282-1-jsnow@redhat.com>
> CI: https://gitlab.com/jsnow/qemu/-/pipelines/316425665
> GitLab: https://gitlab.com/jsnow/qemu/-/commits/python-package-qmp-shell
> 
> This series follows:
> 
> 1. [PATCH v3 00/19] Python: move /scripts/qmp/qom* to /python/qemu/qmp/qom*​
> 2. [PATCH 00/11] python: move /scripts/qmp/gemu-ga-client.py to qemu.qmp package
> 
> and finishes moving stuff in ./scripts/qmp/ into ./python/qemu/qmp/*.
> 
> The benefits of this are:
> 
> 1. Improved protection against accidental regression in qmp-using
> scripts as we refactor QMP to introduce Async QMP and OOB support
> 2. Availability of common qmp command-line tools as part of the qemu.qmp
> package
> 3. No more sys.path hacking for qemu.qmp-using utilities
> 4. Newly 100% clean linting baseline to use as an aid in reviewing
> future patches.
> 
> This series is largely minor refactors, linting and typing cleanups,
> followed by the move into the python packaging folder at the very end of
> the series.
> 
> John Snow (42):
>    scripts/qmp-shell: apply isort rules
>    scripts/qmp-shell: Apply flake8 rules
>    scripts/qmp-shell: fix show_banner signature
>    scripts/qmp-shell: fix exception handling
>    scripts/qmp-shell: fix connect method signature
>    scripts/qmp-shell: remove shadowed variable from _print()
>    scripts/qmp-shell: use @classmethod where appropriate
>    scripts/qmp-shell: Use python3-style super()
>    scripts/qmp-shell: declare verbose in __init__
>    scripts/qmp-shell: use triple-double-quote docstring style
>    scripts/qmp-shell: ignore visit_Name name
>    scripts/qmp-shell: make QMPCompleter returns explicit
>    scripts/qmp-shell: rename one and two-letter variables
>    scripts/qmp-shell: fix shell history exception handling
>    scripts/qmp-shell: remove if-raise-else patterns
>    scripts/qmp-shell: use isinstance() instead of type()
>    scripts/qmp-shell: use argparse
>    scripts/qmp-shell: Add pretty attribute to HMP shell
>    scripts/qmp-shell: Make verbose a public attribute
>    scripts/qmp-shell: move get_prompt() to prompt property
>    scripts/qmp-shell: remove prompt argument from read_exec_command
>    scripts/qmp-shell: move the REPL functionality into QMPShell
>    scripts/qmp-shell: Fix "FuzzyJSON" parser
>    scripts/qmp-shell: refactor QMPCompleter
>    scripts/qmp-shell: initialize completer early
>    python/qmp: add QMPObject type alias
>    scripts/qmp-shell: add mypy types
>    scripts/qmp-shell: Accept SocketAddrT instead of string
>    scripts/qmp-shell: unprivatize 'pretty' property
>    python/qmp: return generic type from context manager
>    scripts/qmp-shell: Use context manager instead of atexit
>    scripts/qmp-shell: use logging to show warnings
>    scripts/qmp-shell: remove TODO
>    scripts/qmp-shell: Fix empty-transaction invocation
>    scripts/qmp-shell: Remove too-broad-exception
>    scripts/qmp-shell: convert usage comment to docstring
>    scripts/qmp-shell: remove double-underscores
>    scripts/qmp-shell: make QMPShellError inherit QMPError
>    scripts/qmp-shell: add docstrings
>    scripts/qmp-shell: move to python/qemu/qmp/qmp_shell.py
>    python: add qmp-shell entry point
>    scripts/qmp-shell: add redirection shim
> 
>   python/qemu/qmp/__init__.py  |   8 +-
>   python/qemu/qmp/qmp_shell.py | 535 +++++++++++++++++++++++++++++++++++
>   python/setup.cfg             |   1 +
>   scripts/qmp/qmp-shell        | 437 +---------------------------
>   4 files changed, 546 insertions(+), 435 deletions(-)
>   create mode 100644 python/qemu/qmp/qmp_shell.py
> 

Preliminarily staged to my Python branch:
https://gitlab.com/jsnow/qemu/-/tree/python

CI: https://gitlab.com/jsnow/qemu/-/pipelines/320710762

I intend to send the PR collecting these 72 cleanup patches this Friday.

--js



^ permalink raw reply	[flat|nested] 44+ messages in thread

end of thread, other threads:[~2021-06-14 18:10 UTC | newest]

Thread overview: 44+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-06-07 20:06 [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow
2021-06-07 20:06 ` [PATCH 01/42] scripts/qmp-shell: apply isort rules John Snow
2021-06-07 20:06 ` [PATCH 02/42] scripts/qmp-shell: Apply flake8 rules John Snow
2021-06-07 20:06 ` [PATCH 03/42] scripts/qmp-shell: fix show_banner signature John Snow
2021-06-07 20:06 ` [PATCH 04/42] scripts/qmp-shell: fix exception handling John Snow
2021-06-07 20:06 ` [PATCH 05/42] scripts/qmp-shell: fix connect method signature John Snow
2021-06-07 20:06 ` [PATCH 06/42] scripts/qmp-shell: remove shadowed variable from _print() John Snow
2021-06-07 20:06 ` [PATCH 07/42] scripts/qmp-shell: use @classmethod where appropriate John Snow
2021-06-07 20:06 ` [PATCH 08/42] scripts/qmp-shell: Use python3-style super() John Snow
2021-06-07 20:06 ` [PATCH 09/42] scripts/qmp-shell: declare verbose in __init__ John Snow
2021-06-07 20:06 ` [PATCH 10/42] scripts/qmp-shell: use triple-double-quote docstring style John Snow
2021-06-07 20:06 ` [PATCH 11/42] scripts/qmp-shell: ignore visit_Name name John Snow
2021-06-07 20:06 ` [PATCH 12/42] scripts/qmp-shell: make QMPCompleter returns explicit John Snow
2021-06-07 20:06 ` [PATCH 13/42] scripts/qmp-shell: rename one and two-letter variables John Snow
2021-06-07 20:06 ` [PATCH 14/42] scripts/qmp-shell: fix shell history exception handling John Snow
2021-06-07 20:06 ` [PATCH 15/42] scripts/qmp-shell: remove if-raise-else patterns John Snow
2021-06-07 20:06 ` [PATCH 16/42] scripts/qmp-shell: use isinstance() instead of type() John Snow
2021-06-07 20:06 ` [PATCH 17/42] scripts/qmp-shell: use argparse John Snow
2021-06-07 20:06 ` [PATCH 18/42] scripts/qmp-shell: Add pretty attribute to HMP shell John Snow
2021-06-07 20:06 ` [PATCH 19/42] scripts/qmp-shell: Make verbose a public attribute John Snow
2021-06-07 20:06 ` [PATCH 20/42] scripts/qmp-shell: move get_prompt() to prompt property John Snow
2021-06-07 20:06 ` [PATCH 21/42] scripts/qmp-shell: remove prompt argument from read_exec_command John Snow
2021-06-07 20:06 ` [PATCH 22/42] scripts/qmp-shell: move the REPL functionality into QMPShell John Snow
2021-06-07 20:06 ` [PATCH 23/42] scripts/qmp-shell: Fix "FuzzyJSON" parser John Snow
2021-06-07 20:06 ` [PATCH 24/42] scripts/qmp-shell: refactor QMPCompleter John Snow
2021-06-07 20:06 ` [PATCH 25/42] scripts/qmp-shell: initialize completer early John Snow
2021-06-07 20:06 ` [PATCH 26/42] python/qmp: add QMPObject type alias John Snow
2021-06-07 20:06 ` [PATCH 27/42] scripts/qmp-shell: add mypy types John Snow
2021-06-07 20:06 ` [PATCH 28/42] scripts/qmp-shell: Accept SocketAddrT instead of string John Snow
2021-06-07 20:06 ` [PATCH 29/42] scripts/qmp-shell: unprivatize 'pretty' property John Snow
2021-06-07 20:06 ` [PATCH 30/42] python/qmp: return generic type from context manager John Snow
2021-06-07 20:06 ` [PATCH 31/42] scripts/qmp-shell: Use context manager instead of atexit John Snow
2021-06-07 20:06 ` [PATCH 32/42] scripts/qmp-shell: use logging to show warnings John Snow
2021-06-07 20:06 ` [PATCH 33/42] scripts/qmp-shell: remove TODO John Snow
2021-06-07 20:06 ` [PATCH 34/42] scripts/qmp-shell: Fix empty-transaction invocation John Snow
2021-06-07 20:06 ` [PATCH 35/42] scripts/qmp-shell: Remove too-broad-exception John Snow
2021-06-07 20:06 ` [PATCH 36/42] scripts/qmp-shell: convert usage comment to docstring John Snow
2021-06-07 20:06 ` [PATCH 37/42] scripts/qmp-shell: remove double-underscores John Snow
2021-06-07 20:06 ` [PATCH 38/42] scripts/qmp-shell: make QMPShellError inherit QMPError John Snow
2021-06-07 20:06 ` [PATCH 39/42] scripts/qmp-shell: add docstrings John Snow
2021-06-07 20:06 ` [PATCH 40/42] scripts/qmp-shell: move to python/qemu/qmp/qmp_shell.py John Snow
2021-06-07 20:06 ` [PATCH 41/42] python: add qmp-shell entry point John Snow
2021-06-07 20:06 ` [PATCH 42/42] scripts/qmp-shell: add redirection shim John Snow
2021-06-14 18:09 ` [PATCH 00/42] python: move qmp-shell into qemu.qmp package John Snow

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.