* [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp
@ 2020-11-04 0:34 John Snow
2020-11-04 0:34 ` [PATCH v2 01/72] python/qmp: Add qom script rewrites John Snow
` (71 more replies)
0 siblings, 72 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:34 UTC (permalink / raw)
To: qemu-devel; +Cc: Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
Based-on: <20201020193555.1493936-1-jsnow@redhat.com>
[PATCH v3 00/15] python: create installable package
Hi! This series looks big, but don't panic. Like many python refactoring
series, most of the individual patches are very tiny, isolated changes.
This series depends on the details of the installable package series,
so don't sweat too hard reviewing the particulars just yet, or at all.
I did want to showcase the process of moving scripts INTO the python
library, though, so here it is.
The purpose of this series is to move ./scripts/qmp/* to
./python/qemu/qmp/*.py. To do so, we need to ensure that these files
pass the various linters and code quality standards we have instituted
for the python library. Doing so allows us to check these tools with the
same type checking utilities that we use to enforce integrity in the
base library.
This way, changes to the library will cause integration tests to fail,
and we can ensure the continued stability of these scripts.
Nicest of all, by installing this python package to your working
environment, you will now have access to 'qom-set', 'qom-get',
'qmp-shell', etc no matter what your working directory is, and it will
Just Work.
Let's run down the list:
- qom-set, qom-get, qom-tree, and qom-list are merged into a 'qom' tool.
- qom-[set|get|tree|list] are still available under those names, too.
- qemu-ga-client is lightly refurbished and is also now available.
- qmp-shell is lightly polished and will be available.
Patches 1-3: Introduce 'qom' tool, deprecate qom-[set|get|tree|list]
Patches 4-16: port qom-fuse to qemu.qmp.qom tool
Patches 17-27: port qemu-ga-client to qemu.qmp package
Patches 28-72: port qmp-shell to qemu.qmp package
Reviewer notes:
- I just rewrote qom-xxx entirely, though it is based on the original
scripts. Doing it brick by brick was too slow and awkward.
- I added symlinks (not in git) to the old scripts in the new location
to run the linters against the files that haven't been moved yet, and
once they're almost done (except for import problems), I move them.
- After moving scripts, I add a forwarder to the new location from the
old location. The forwarders can be deleted eventually.
- some scripts disappear for a commit or two before being
re-established. This helps preserve git-blame history where it
happens; it was the best I could do.
John Snow (72):
python/qmp: Add qom script rewrites
python/qmp: add qom script entry points
scripts/qmp: redirect qom-xxx scripts to python/qemu/qmp/
scripts/qom-fuse: apply isort rules
scripts/qom-fuse: apply flake8 rules
python: Add 'fh' to known-good variable names
scripts/qom-fuse: Apply pylint rules
scripts/qom-fuse: Add docstrings
scripts/qom-fuse: Convert to QOMCommand
scripts/qom-fuse: use QOMCommand.qom_list()
scripts/qom-fuse: ensure QOMFuse.read always returns bytes
scripts/qom-fuse: add static type hints
scripts/qom-fuse: move to python/qemu/qmp/qom_fuse.py
scripts/qom-fuse: add redirection shim to python/qemu/qmp/qom-fuse.py
python: add fuse command to 'qom' tools
python: add optional fuse dependency
scripts/qemu-ga-client: apply isort rules
scripts/qemu-ga-client: apply (most) flake8 rules
scripts/qemu-ga-client: Fix exception handling
scripts/qemu-ga-client: replace deprecated optparse with argparse
scripts/qemu-ga-client: add module docstring
scripts/qemu-ga-client: apply (most) pylint rules
python/qmp: Correct type of QMPReturnValue
scripts/qmp/qemu-ga-client: add mypy type hints
scripts/qemu-ga-client: move to python/qemu/qmp/qemu_ga_client.py
python/qemu-ga-client: add entry point
scripts/qemu-ga-client: Add forwarder stub
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: explicitly chain exception context
scripts/qmp-shell: remove if-raise-else patterns
scripts/qmp-shell: use isinstance() instead of type()
scripts/qmp-shell: use argparse
python/qmp: Fix type of SocketAddrT
python/qmp: add parse_address classmethod
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/Pipfile.lock | 13 +-
python/qemu/qmp/__init__.py | 61 +++-
python/qemu/qmp/qemu_ga_client.py | 323 ++++++++++++++++++
python/qemu/qmp/qmp_shell.py | 535 ++++++++++++++++++++++++++++++
python/qemu/qmp/qom.py | 217 ++++++++++++
python/qemu/qmp/qom_common.py | 153 +++++++++
python/qemu/qmp/qom_fuse.py | 207 ++++++++++++
python/setup.cfg | 30 +-
scripts/qmp/qemu-ga-client | 297 +----------------
scripts/qmp/qmp-shell | 454 +------------------------
scripts/qmp/qom-fuse | 144 +-------
scripts/qmp/qom-get | 66 +---
scripts/qmp/qom-list | 63 +---
scripts/qmp/qom-set | 63 +---
scripts/qmp/qom-tree | 74 +----
15 files changed, 1546 insertions(+), 1154 deletions(-)
create mode 100644 python/qemu/qmp/qemu_ga_client.py
create mode 100644 python/qemu/qmp/qmp_shell.py
create mode 100644 python/qemu/qmp/qom.py
create mode 100644 python/qemu/qmp/qom_common.py
create mode 100644 python/qemu/qmp/qom_fuse.py
--
2.26.2
^ permalink raw reply [flat|nested] 73+ messages in thread
* [PATCH v2 01/72] python/qmp: Add qom script rewrites
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
@ 2020-11-04 0:34 ` John Snow
2020-11-04 0:34 ` [PATCH v2 02/72] python/qmp: add qom script entry points John Snow
` (70 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:34 UTC (permalink / raw)
To: qemu-devel; +Cc: Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
Inspired by qom-set, qom-get, qom-tree and qom-list; combine all four of
those scripts into one script.
A later addition of qom-fuse necessitates that some common features are
split out and shared between them.
Signed-off-by: John Snow <jsnow@redhat.com>
---
python/qemu/qmp/qom.py | 207 ++++++++++++++++++++++++++++++++++
python/qemu/qmp/qom_common.py | 153 +++++++++++++++++++++++++
2 files changed, 360 insertions(+)
create mode 100644 python/qemu/qmp/qom.py
create mode 100644 python/qemu/qmp/qom_common.py
diff --git a/python/qemu/qmp/qom.py b/python/qemu/qmp/qom.py
new file mode 100644
index 000000000000..912d1809e60d
--- /dev/null
+++ b/python/qemu/qmp/qom.py
@@ -0,0 +1,207 @@
+"""
+QEMU Object Model testing tools.
+
+usage: qom.py [-h] {set,get,list,tree} ...
+
+Query and manipulate QOM data
+
+optional arguments:
+ -h, --help show this help message and exit
+
+QOM commands:
+ {set,get,list,tree,fuse}
+ set Set a QOM property value
+ get Get a QOM property value
+ list List QOM properties at a given path
+ tree Show QOM tree from a given path
+"""
+##
+# Copyright John Snow 2020, for Red Hat, Inc.
+# Copyright IBM, Corp. 2011
+#
+# Authors:
+# John Snow <jsnow@redhat.com>
+# Anthony Liguori <aliguori@amazon.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or later.
+# See the COPYING file in the top-level directory.
+#
+# Based on ./scripts/qmp/qom-[set|get|tree|list]
+##
+
+import argparse
+import sys
+
+from . import QMPResponseError
+from .qom_common import QOMCommand
+
+
+class QOMSet(QOMCommand):
+ """QOM Command - Set a property to a given value."""
+ name = 'set'
+ help = 'Set a QOM property value'
+
+ @classmethod
+ def configure_parser(cls, parser: argparse.ArgumentParser) -> None:
+ super().configure_parser(parser)
+ cls.add_path_prop_arg(parser)
+ parser.add_argument(
+ 'value',
+ metavar='<value>',
+ action='store',
+ help='new QOM property value'
+ )
+
+ def __init__(self, args: argparse.Namespace):
+ super().__init__(args)
+ self.path, self.prop = args.path_prop.rsplit('.', 1)
+ self.value = args.value
+
+ def run(self) -> int:
+ rsp = self.qmp.command(
+ 'qom-set',
+ path=self.path,
+ property=self.prop,
+ value=self.value
+ )
+ print(rsp)
+ return 0
+
+
+class QOMGet(QOMCommand):
+ """QOM Command - Get a property's current value."""
+ name = 'get'
+ help = 'Get a QOM property value'
+
+ @classmethod
+ def configure_parser(cls, parser: argparse.ArgumentParser) -> None:
+ super().configure_parser(parser)
+ cls.add_path_prop_arg(parser)
+
+ def __init__(self, args: argparse.Namespace):
+ super().__init__(args)
+ try:
+ tmp = args.path_prop.rsplit('.', 1)
+ except ValueError as err:
+ raise ValueError('Invalid format for <path>.<property>') from err
+ self.path = tmp[0]
+ self.prop = tmp[1]
+
+ def run(self) -> int:
+ rsp = self.qmp.command(
+ 'qom-get',
+ path=self.path,
+ property=self.prop
+ )
+ if isinstance(rsp, dict):
+ for key, value in rsp.items():
+ print(f"{key}: {value}")
+ else:
+ print(rsp)
+ return 0
+
+
+class QOMList(QOMCommand):
+ """QOM Command - List the properties at a given path."""
+ name = 'list'
+ help = 'List QOM properties at a given path'
+
+ @classmethod
+ def configure_parser(cls, parser: argparse.ArgumentParser) -> None:
+ super().configure_parser(parser)
+ parser.add_argument(
+ 'path',
+ metavar='<path>',
+ action='store',
+ help='QOM path',
+ )
+
+ def __init__(self, args: argparse.Namespace):
+ super().__init__(args)
+ self.path = args.path
+
+ def run(self) -> int:
+ rsp = self.qom_list(self.path)
+ for item in rsp:
+ if item.child:
+ print(f"{item.name}/")
+ elif item.link:
+ print(f"@{item.name}/")
+ else:
+ print(item.name)
+ return 0
+
+
+class QOMTree(QOMCommand):
+ """QOM Command - Show the full tree below a given path."""
+ name = 'tree'
+ help = 'Show QOM tree from a given path'
+
+ @classmethod
+ def configure_parser(cls, parser: argparse.ArgumentParser) -> None:
+ super().configure_parser(parser)
+ parser.add_argument(
+ 'path',
+ metavar='<path>',
+ action='store',
+ help='QOM path',
+ nargs='?',
+ default='/'
+ )
+
+ def __init__(self, args: argparse.Namespace):
+ super().__init__(args)
+ self.path = args.path
+
+ def _list_node(self, path: str) -> None:
+ print(path)
+ items = self.qom_list(path)
+ for item in items:
+ if item.child:
+ continue
+ try:
+ rsp = self.qmp.command('qom-get', path=path,
+ property=item.name)
+ print(f" {item.name}: {rsp} ({item.type})")
+ except QMPResponseError as err:
+ print(f" {item.name}: <EXCEPTION: {err!s}> ({item.type})")
+ print('')
+ for item in items:
+ if not item.child:
+ continue
+ if path == '/':
+ path = ''
+ self._list_node(f"{path}/{item.name}")
+
+ def run(self) -> int:
+ self._list_node(self.path)
+ return 0
+
+
+def main() -> int:
+ """QOM script main entry point."""
+ parser = argparse.ArgumentParser(
+ description='Query and manipulate QOM data'
+ )
+ subparsers = parser.add_subparsers(
+ title='QOM commands',
+ dest='command'
+ )
+
+ for command in QOMCommand.__subclasses__():
+ command.register(subparsers)
+
+ args = parser.parse_args()
+
+ if args.command is None:
+ parser.error('Command not specified.')
+ return 1
+
+ assert issubclass(args.cmd_class, QOMCommand)
+ cmd = args.cmd_class(args)
+ assert isinstance(cmd, QOMCommand)
+ return cmd.run()
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/python/qemu/qmp/qom_common.py b/python/qemu/qmp/qom_common.py
new file mode 100644
index 000000000000..3890b247ae68
--- /dev/null
+++ b/python/qemu/qmp/qom_common.py
@@ -0,0 +1,153 @@
+"""
+QOM Command abstractions.
+"""
+##
+# Copyright John Snow 2020, for Red Hat, Inc.
+# Copyright IBM, Corp. 2011
+#
+# Authors:
+# John Snow <jsnow@redhat.com>
+# Anthony Liguori <aliguori@amazon.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or later.
+# See the COPYING file in the top-level directory.
+#
+# Based on ./scripts/qmp/qom-[set|get|tree|list]
+##
+
+import argparse
+import os
+from typing import (
+ Any,
+ Dict,
+ List,
+ Optional,
+)
+
+from . import QEMUMonitorProtocol
+
+
+Subparsers = argparse._SubParsersAction # pylint: disable=protected-access
+
+
+class ObjectPropertyInfo:
+ """
+ Represents the return type from e.g. qom-list.
+ """
+ def __init__(self, name: str, type_: str,
+ description: Optional[str] = None,
+ default_value: Optional[object] = None):
+ self.name = name
+ self.type = type_
+ self.description = description
+ self.default_value = default_value
+
+ @classmethod
+ def make(cls, value: Dict[str, Any]) -> 'ObjectPropertyInfo':
+ """
+ Build an ObjectPropertyInfo from a Dict with an unknown shape.
+ """
+ assert value.keys() >= {'name', 'type'}
+ assert value.keys() <= {'name', 'type', 'description', 'default-value'}
+ return cls(value['name'], value['type'],
+ value.get('description'),
+ value.get('default-value'))
+
+ @property
+ def child(self) -> bool:
+ """Is this property a child property?"""
+ return self.type.startswith('child<')
+
+ @property
+ def link(self) -> bool:
+ """Is this property a link property?"""
+ return self.type.startswith('link<')
+
+
+class QOMCommand:
+ """
+ Represents a QOM sub-command.
+
+ :param args: Parsed arguments, as returned from parser.parse_args.
+ """
+ name: str
+ help: str
+
+ def __init__(self, args: argparse.Namespace):
+ if args.socket is None:
+ raise Exception("No QMP socket path or address given")
+ self.qmp = QEMUMonitorProtocol(args.socket)
+ self.qmp.connect()
+
+ @classmethod
+ def register(cls, subparsers: Subparsers) -> None:
+ """
+ Register this command with the argument parser.
+
+ :param subparsers: argparse subparsers object, from "add_subparsers".
+ """
+ subparser = subparsers.add_parser(cls.name, help=cls.help,
+ description=cls.help)
+ cls.configure_parser(subparser)
+
+ @classmethod
+ def configure_parser(cls, parser: argparse.ArgumentParser) -> None:
+ """
+ Configure a parser with this command's arguments.
+
+ :param parser: argparse parser or subparser object.
+ """
+ default_path = os.environ.get('QMP_SOCKET')
+ parser.add_argument(
+ '--socket', '-s',
+ dest='socket',
+ action='store',
+ help='QMP socket path or address (addr:port).'
+ ' May also be set via QMP_SOCKET environment variable.',
+ default=default_path
+ )
+ parser.set_defaults(cmd_class=cls)
+
+ @classmethod
+ def add_path_prop_arg(cls, parser: argparse.ArgumentParser) -> None:
+ """
+ Add the <path>.<proptery> positional argument to this command.
+
+ :param parser: The parser to add the argument to.
+ """
+ parser.add_argument(
+ 'path_prop',
+ metavar='<path>.<property>',
+ action='store',
+ help="QOM path and property, separated by a period '.'"
+ )
+
+ def run(self) -> int:
+ """
+ Run this command.
+
+ :return: 0 on success, 1 otherwise.
+ """
+ raise NotImplementedError
+
+ def qom_list(self, path: str) -> List[ObjectPropertyInfo]:
+ """
+ :return: a strongly typed list from the 'qom-list' command.
+ """
+ rsp = self.qmp.command('qom-list', path=path)
+ # qom-list returns List[ObjectPropertyInfo]
+ assert isinstance(rsp, list)
+ return [ObjectPropertyInfo.make(x) for x in rsp]
+
+ @classmethod
+ def entry_point(cls) -> int:
+ """
+ Build this command's parser, parse arguments, and run the command.
+
+ :return: `run`'s return code.
+ """
+ parser = argparse.ArgumentParser(description=cls.help)
+ cls.configure_parser(parser)
+ args = parser.parse_args()
+ cmd = cls(args)
+ return cmd.run()
--
2.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 02/72] python/qmp: add qom script entry points
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
2020-11-04 0:34 ` [PATCH v2 01/72] python/qmp: Add qom script rewrites John Snow
@ 2020-11-04 0:34 ` John Snow
2020-11-04 0:34 ` [PATCH v2 03/72] scripts/qmp: redirect qom-xxx scripts to python/qemu/qmp/ John Snow
` (69 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:34 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé,
Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
Add the 'qom', 'qom-set', 'qom-get', 'qom-list', and 'qom-tree' scripts
to the qemu.qmp package. When you install this package, these scripts
will become available on your command line.
Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
---
python/setup.cfg | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/python/setup.cfg b/python/setup.cfg
index 2c12d9ab89b4..c8b7215996e9 100644
--- a/python/setup.cfg
+++ b/python/setup.cfg
@@ -28,6 +28,13 @@ devel =
pylint >= 2.6.0
pytest >= 3.0.0
+[options.entry_points]
+console_scripts =
+ qom = qemu.qmp.qom:main
+ qom-set = qemu.qmp.qom:QOMSet.entry_point
+ qom-get = qemu.qmp.qom:QOMGet.entry_point
+ qom-list = qemu.qmp.qom:QOMList.entry_point
+ qom-tree = qemu.qmp.qom:QOMTree.entry_point
[flake8]
extend-ignore = E722 # Prefer pylint's bare-except checks to flake8's
--
2.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 03/72] scripts/qmp: redirect qom-xxx scripts to python/qemu/qmp/
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
2020-11-04 0:34 ` [PATCH v2 01/72] python/qmp: Add qom script rewrites John Snow
2020-11-04 0:34 ` [PATCH v2 02/72] python/qmp: add qom script entry points John Snow
@ 2020-11-04 0:34 ` John Snow
2020-11-04 0:34 ` [PATCH v2 04/72] scripts/qom-fuse: apply isort rules John Snow
` (68 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:34 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé,
Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
Redirect to the new qom scripts. These forwarders can be deleted
eventually when there has been more time for the dust on the Python
packaging to settle and people understand how to find these commands.
Note: You can run these by setting $PYTHONPATH in your shell and then
running "python3 -m qemu.qmp.qom", or you can install the qemu namespace
package and use the "qom" or "qom-set" scripts.
I've written how to install the package elsewhere, but for the sake of
git-blame, cd to ./python, and then do:
- pip3 install [--user] [-e] .
--user will install to your local user install (will not work inside of
a venv), omitting this flag installs to your system-wide packages
(outside of a venv) or to your current virtual environment (inside the
venv).
When installing to a venv or to your system-wide packages, "qom"
should be in your $PATH already. If you do a user install, you may
need to add ~/.local/bin to your $PATH if you haven't already.
-e installs in editable mode: the installed package is effectively just
a symlink to this folder; so changes to your git working tree are
reflected in the installed package.
Alternatively to the above, If you have `pipenv` installed (`pip3
install --user pipenv`), you may also invoke 'pipenv shell' to enter a
pipenv-managed virtual environment (as a shell process that you may
leave with ctrt+d) that has 'qom' already in $PATH.
Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
---
scripts/qmp/qom-get | 66 +++------------------------------------
scripts/qmp/qom-list | 63 +++----------------------------------
scripts/qmp/qom-set | 63 +++----------------------------------
scripts/qmp/qom-tree | 74 +++-----------------------------------------
4 files changed, 16 insertions(+), 250 deletions(-)
diff --git a/scripts/qmp/qom-get b/scripts/qmp/qom-get
index 666df718320c..e4f3e0c01381 100755
--- a/scripts/qmp/qom-get
+++ b/scripts/qmp/qom-get
@@ -1,69 +1,11 @@
#!/usr/bin/env python3
-##
-# QEMU Object Model test tools
-#
-# Copyright IBM, Corp. 2011
-#
-# Authors:
-# Anthony Liguori <aliguori@us.ibm.com>
-#
-# This work is licensed under the terms of the GNU GPL, version 2 or later. See
-# the COPYING file in the top-level directory.
-##
-import sys
import os
+import sys
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
-from qemu.qmp import QEMUMonitorProtocol
+from qemu.qmp.qom import QOMGet
-cmd, args = sys.argv[0], sys.argv[1:]
-socket_path = None
-path = None
-prop = None
-def usage():
- return '''environment variables:
- QMP_SOCKET=<path | addr:port>
-usage:
- %s [-h] [-s <QMP socket path | addr:port>] <path>.<property>
-''' % cmd
-
-def usage_error(error_msg = "unspecified error"):
- sys.stderr.write('%s\nERROR: %s\n' % (usage(), error_msg))
- exit(1)
-
-if len(args) > 0:
- if args[0] == "-h":
- print(usage())
- exit(0);
- elif args[0] == "-s":
- try:
- socket_path = args[1]
- except:
- usage_error("missing argument: QMP socket path or address");
- args = args[2:]
-
-if not socket_path:
- if 'QMP_SOCKET' in os.environ:
- socket_path = os.environ['QMP_SOCKET']
- else:
- usage_error("no QMP socket path or address given");
-
-if len(args) > 0:
- try:
- path, prop = args[0].rsplit('.', 1)
- except:
- usage_error("invalid format for path/property/value")
-else:
- usage_error("not enough arguments")
-
-srv = QEMUMonitorProtocol(socket_path)
-srv.connect()
-
-rsp = srv.command('qom-get', path=path, property=prop)
-if type(rsp) == dict:
- for i in rsp.keys():
- print('%s: %s' % (i, rsp[i]))
-else:
- print(rsp)
+if __name__ == '__main__':
+ sys.exit(QOMGet.entry_point())
diff --git a/scripts/qmp/qom-list b/scripts/qmp/qom-list
index 5074fd939f4a..7a071a54e1e7 100755
--- a/scripts/qmp/qom-list
+++ b/scripts/qmp/qom-list
@@ -1,66 +1,11 @@
#!/usr/bin/env python3
-##
-# QEMU Object Model test tools
-#
-# Copyright IBM, Corp. 2011
-#
-# Authors:
-# Anthony Liguori <aliguori@us.ibm.com>
-#
-# This work is licensed under the terms of the GNU GPL, version 2 or later. See
-# the COPYING file in the top-level directory.
-##
-import sys
import os
+import sys
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
-from qemu.qmp import QEMUMonitorProtocol
+from qemu.qmp.qom import QOMList
-cmd, args = sys.argv[0], sys.argv[1:]
-socket_path = None
-path = None
-prop = None
-def usage():
- return '''environment variables:
- QMP_SOCKET=<path | addr:port>
-usage:
- %s [-h] [-s <QMP socket path | addr:port>] [<path>]
-''' % cmd
-
-def usage_error(error_msg = "unspecified error"):
- sys.stderr.write('%s\nERROR: %s\n' % (usage(), error_msg))
- exit(1)
-
-if len(args) > 0:
- if args[0] == "-h":
- print(usage())
- exit(0);
- elif args[0] == "-s":
- try:
- socket_path = args[1]
- except:
- usage_error("missing argument: QMP socket path or address");
- args = args[2:]
-
-if not socket_path:
- if 'QMP_SOCKET' in os.environ:
- socket_path = os.environ['QMP_SOCKET']
- else:
- usage_error("no QMP socket path or address given");
-
-srv = QEMUMonitorProtocol(socket_path)
-srv.connect()
-
-if len(args) == 0:
- print('/')
- sys.exit(0)
-
-for item in srv.command('qom-list', path=args[0]):
- if item['type'].startswith('child<'):
- print('%s/' % item['name'])
- elif item['type'].startswith('link<'):
- print('@%s/' % item['name'])
- else:
- print('%s' % item['name'])
+if __name__ == '__main__':
+ sys.exit(QOMList.entry_point())
diff --git a/scripts/qmp/qom-set b/scripts/qmp/qom-set
index 240a78187f9b..9ca9e2ba106b 100755
--- a/scripts/qmp/qom-set
+++ b/scripts/qmp/qom-set
@@ -1,66 +1,11 @@
#!/usr/bin/env python3
-##
-# QEMU Object Model test tools
-#
-# Copyright IBM, Corp. 2011
-#
-# Authors:
-# Anthony Liguori <aliguori@us.ibm.com>
-#
-# This work is licensed under the terms of the GNU GPL, version 2 or later. See
-# the COPYING file in the top-level directory.
-##
-import sys
import os
+import sys
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
-from qemu.qmp import QEMUMonitorProtocol
+from qemu.qmp.qom import QOMSet
-cmd, args = sys.argv[0], sys.argv[1:]
-socket_path = None
-path = None
-prop = None
-value = None
-def usage():
- return '''environment variables:
- QMP_SOCKET=<path | addr:port>
-usage:
- %s [-h] [-s <QMP socket path | addr:port>] <path>.<property> <value>
-''' % cmd
-
-def usage_error(error_msg = "unspecified error"):
- sys.stderr.write('%s\nERROR: %s\n' % (usage(), error_msg))
- exit(1)
-
-if len(args) > 0:
- if args[0] == "-h":
- print(usage())
- exit(0);
- elif args[0] == "-s":
- try:
- socket_path = args[1]
- except:
- usage_error("missing argument: QMP socket path or address");
- args = args[2:]
-
-if not socket_path:
- if 'QMP_SOCKET' in os.environ:
- socket_path = os.environ['QMP_SOCKET']
- else:
- usage_error("no QMP socket path or address given");
-
-if len(args) > 1:
- try:
- path, prop = args[0].rsplit('.', 1)
- except:
- usage_error("invalid format for path/property/value")
- value = args[1]
-else:
- usage_error("not enough arguments")
-
-srv = QEMUMonitorProtocol(socket_path)
-srv.connect()
-
-print(srv.command('qom-set', path=path, property=prop, value=value))
+if __name__ == '__main__':
+ sys.exit(QOMSet.entry_point())
diff --git a/scripts/qmp/qom-tree b/scripts/qmp/qom-tree
index 25b0781323cc..7d0ccca3a4dd 100755
--- a/scripts/qmp/qom-tree
+++ b/scripts/qmp/qom-tree
@@ -1,77 +1,11 @@
#!/usr/bin/env python3
-##
-# QEMU Object Model test tools
-#
-# Copyright IBM, Corp. 2011
-# Copyright (c) 2013 SUSE LINUX Products GmbH
-#
-# Authors:
-# Anthony Liguori <aliguori@amazon.com>
-# Andreas Faerber <afaerber@suse.de>
-#
-# This work is licensed under the terms of the GNU GPL, version 2 or later. See
-# the COPYING file in the top-level directory.
-##
-import sys
import os
+import sys
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
-from qemu.qmp import QEMUMonitorProtocol
+from qemu.qmp.qom import QOMTree
-cmd, args = sys.argv[0], sys.argv[1:]
-socket_path = None
-path = None
-prop = None
-def usage():
- return '''environment variables:
- QMP_SOCKET=<path | addr:port>
-usage:
- %s [-h] [-s <QMP socket path | addr:port>] [<path>]
-''' % cmd
-
-def usage_error(error_msg = "unspecified error"):
- sys.stderr.write('%s\nERROR: %s\n' % (usage(), error_msg))
- exit(1)
-
-if len(args) > 0:
- if args[0] == "-h":
- print(usage())
- exit(0);
- elif args[0] == "-s":
- try:
- socket_path = args[1]
- except:
- usage_error("missing argument: QMP socket path or address");
- args = args[2:]
-
-if not socket_path:
- if 'QMP_SOCKET' in os.environ:
- socket_path = os.environ['QMP_SOCKET']
- else:
- usage_error("no QMP socket path or address given");
-
-srv = QEMUMonitorProtocol(socket_path)
-srv.connect()
-
-def list_node(path):
- print('%s' % path)
- items = srv.command('qom-list', path=path)
- for item in items:
- if not item['type'].startswith('child<'):
- try:
- print(' %s: %s (%s)' % (item['name'], srv.command('qom-get', path=path, property=item['name']), item['type']))
- except:
- print(' %s: <EXCEPTION> (%s)' % (item['name'], item['type']))
- print('')
- for item in items:
- if item['type'].startswith('child<'):
- list_node((path if (path != '/') else '') + '/' + item['name'])
-
-if len(args) == 0:
- path = '/'
-else:
- path = args[0]
-
-list_node(path)
+if __name__ == '__main__':
+ sys.exit(QOMTree.entry_point())
--
2.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 04/72] scripts/qom-fuse: apply isort rules
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (2 preceding siblings ...)
2020-11-04 0:34 ` [PATCH v2 03/72] scripts/qmp: redirect qom-xxx scripts to python/qemu/qmp/ John Snow
@ 2020-11-04 0:34 ` John Snow
2020-11-04 0:34 ` [PATCH v2 05/72] scripts/qom-fuse: apply flake8 rules John Snow
` (67 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:34 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé,
Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
Hint: you can use symlinks to create qom_fuse.py in python/qemu/qmp/ and
point to scripts/qom-fuse to apply the standard linting rules to this
script.
Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
---
scripts/qmp/qom-fuse | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/scripts/qmp/qom-fuse b/scripts/qmp/qom-fuse
index 7c7cff8edfba..62deb9adb12d 100755
--- a/scripts/qmp/qom-fuse
+++ b/scripts/qmp/qom-fuse
@@ -13,14 +13,20 @@
# the COPYING file in the top-level directory.
##
-import fuse, stat
-from fuse import FUSE, FuseOSError, Operations
-import os, posix, sys
from errno import *
+import os
+import posix
+import stat
+import sys
+
+import fuse
+from fuse import FUSE, FuseOSError, Operations
+
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
from qemu.qmp import QEMUMonitorProtocol
+
fuse.fuse_python_api = (0, 2)
class QOMFS(Operations):
--
2.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 05/72] scripts/qom-fuse: apply flake8 rules
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (3 preceding siblings ...)
2020-11-04 0:34 ` [PATCH v2 04/72] scripts/qom-fuse: apply isort rules John Snow
@ 2020-11-04 0:34 ` John Snow
2020-11-04 0:34 ` [PATCH v2 06/72] python: Add 'fh' to known-good variable names John Snow
` (66 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:34 UTC (permalink / raw)
To: qemu-devel; +Cc: Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
flake8 still has one warning because of the sys.path hack, but that will
be going away by the end of this patch series.
Signed-off-by: John Snow <jsnow@redhat.com>
---
scripts/qmp/qom-fuse | 81 +++++++++++++++++++++++---------------------
1 file changed, 43 insertions(+), 38 deletions(-)
diff --git a/scripts/qmp/qom-fuse b/scripts/qmp/qom-fuse
index 62deb9adb12d..ca30e928679e 100755
--- a/scripts/qmp/qom-fuse
+++ b/scripts/qmp/qom-fuse
@@ -9,13 +9,12 @@
# Anthony Liguori <aliguori@us.ibm.com>
# Markus Armbruster <armbru@redhat.com>
#
-# This work is licensed under the terms of the GNU GPL, version 2 or later. See
-# the COPYING file in the top-level directory.
+# This work is licensed under the terms of the GNU GPL, version 2 or later.
+# See the COPYING file in the top-level directory.
##
-from errno import *
+from errno import ENOENT, EPERM
import os
-import posix
import stat
import sys
@@ -29,6 +28,7 @@ from qemu.qmp import QEMUMonitorProtocol
fuse.fuse_python_api = (0, 2)
+
class QOMFS(Operations):
def __init__(self, qmp):
self.qmp = qmp
@@ -45,7 +45,7 @@ class QOMFS(Operations):
def is_object(self, path):
try:
- items = self.qmp.command('qom-list', path=path)
+ self.qmp.command('qom-list', path=path)
return True
except:
return False
@@ -85,7 +85,7 @@ class QOMFS(Operations):
path = '/'
try:
data = self.qmp.command('qom-get', path=path, property=prop)
- data += '\n' # make values shell friendly
+ data += '\n' # make values shell friendly
except:
raise FuseOSError(EPERM)
@@ -104,38 +104,44 @@ class QOMFS(Operations):
def getattr(self, path, fh=None):
if self.is_link(path):
- value = { 'st_mode': 0o755 | stat.S_IFLNK,
- 'st_ino': self.get_ino(path),
- 'st_dev': 0,
- 'st_nlink': 2,
- 'st_uid': 1000,
- 'st_gid': 1000,
- 'st_size': 4096,
- 'st_atime': 0,
- 'st_mtime': 0,
- 'st_ctime': 0 }
+ value = {
+ 'st_mode': 0o755 | stat.S_IFLNK,
+ 'st_ino': self.get_ino(path),
+ 'st_dev': 0,
+ 'st_nlink': 2,
+ 'st_uid': 1000,
+ 'st_gid': 1000,
+ 'st_size': 4096,
+ 'st_atime': 0,
+ 'st_mtime': 0,
+ 'st_ctime': 0
+ }
elif self.is_object(path):
- value = { 'st_mode': 0o755 | stat.S_IFDIR,
- 'st_ino': self.get_ino(path),
- 'st_dev': 0,
- 'st_nlink': 2,
- 'st_uid': 1000,
- 'st_gid': 1000,
- 'st_size': 4096,
- 'st_atime': 0,
- 'st_mtime': 0,
- 'st_ctime': 0 }
+ value = {
+ 'st_mode': 0o755 | stat.S_IFDIR,
+ 'st_ino': self.get_ino(path),
+ 'st_dev': 0,
+ 'st_nlink': 2,
+ 'st_uid': 1000,
+ 'st_gid': 1000,
+ 'st_size': 4096,
+ 'st_atime': 0,
+ 'st_mtime': 0,
+ 'st_ctime': 0
+ }
elif self.is_property(path):
- value = { 'st_mode': 0o644 | stat.S_IFREG,
- 'st_ino': self.get_ino(path),
- 'st_dev': 0,
- 'st_nlink': 1,
- 'st_uid': 1000,
- 'st_gid': 1000,
- 'st_size': 4096,
- 'st_atime': 0,
- 'st_mtime': 0,
- 'st_ctime': 0 }
+ value = {
+ 'st_mode': 0o644 | stat.S_IFREG,
+ 'st_ino': self.get_ino(path),
+ 'st_dev': 0,
+ 'st_nlink': 1,
+ 'st_uid': 1000,
+ 'st_gid': 1000,
+ 'st_size': 4096,
+ 'st_atime': 0,
+ 'st_mtime': 0,
+ 'st_ctime': 0
+ }
else:
raise FuseOSError(ENOENT)
return value
@@ -146,8 +152,7 @@ class QOMFS(Operations):
for item in self.qmp.command('qom-list', path=path):
yield str(item['name'])
+
if __name__ == '__main__':
- import os
-
fuse = FUSE(QOMFS(QEMUMonitorProtocol(os.environ['QMP_SOCKET'])),
sys.argv[1], foreground=True)
--
2.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 06/72] python: Add 'fh' to known-good variable names
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (4 preceding siblings ...)
2020-11-04 0:34 ` [PATCH v2 05/72] scripts/qom-fuse: apply flake8 rules John Snow
@ 2020-11-04 0:34 ` John Snow
2020-11-04 0:34 ` [PATCH v2 07/72] scripts/qom-fuse: Apply pylint rules John Snow
` (65 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:34 UTC (permalink / raw)
To: qemu-devel; +Cc: Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
fd and fh are fine: we often use these for "file descriptor" or "file
handle" accordingly. It is rarely the case that you need to enforce a
more semantically meaningful name beyond "This is the file we are using
right now."
While we're here: add comments for all of the non-standard pylint
names. (And the underscore.)
Signed-off-by: John Snow <jsnow@redhat.com>
---
python/setup.cfg | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/python/setup.cfg b/python/setup.cfg
index c8b7215996e9..c963d05527aa 100644
--- a/python/setup.cfg
+++ b/python/setup.cfg
@@ -68,9 +68,10 @@ good-names=i,
k,
ex,
Run,
- _,
- fd,
- c,
+ _, # By convention: Unused variable
+ fh, # fh = open(...)
+ fd, # fd = os.open(...)
+ c, # for c in string: ...
[pylint.similarities]
# Ignore imports when computing similarities.
--
2.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 07/72] scripts/qom-fuse: Apply pylint rules
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (5 preceding siblings ...)
2020-11-04 0:34 ` [PATCH v2 06/72] python: Add 'fh' to known-good variable names John Snow
@ 2020-11-04 0:34 ` John Snow
2020-11-04 0:34 ` [PATCH v2 08/72] scripts/qom-fuse: Add docstrings John Snow
` (64 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:34 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé,
Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
- Catch specific exceptions from QMP
- Reraise errors with explicit context
- method parameters should match parent's names
Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
---
scripts/qmp/qom-fuse | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/scripts/qmp/qom-fuse b/scripts/qmp/qom-fuse
index ca30e928679e..805e99c8ecde 100755
--- a/scripts/qmp/qom-fuse
+++ b/scripts/qmp/qom-fuse
@@ -23,7 +23,7 @@ from fuse import FUSE, FuseOSError, Operations
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
-from qemu.qmp import QEMUMonitorProtocol
+from qemu.qmp import QEMUMonitorProtocol, QMPResponseError
fuse.fuse_python_api = (0, 2)
@@ -47,7 +47,7 @@ class QOMFS(Operations):
try:
self.qmp.command('qom-list', path=path)
return True
- except:
+ except QMPResponseError:
return False
def is_property(self, path):
@@ -59,7 +59,7 @@ class QOMFS(Operations):
if item['name'] == prop:
return True
return False
- except:
+ except QMPResponseError:
return False
def is_link(self, path):
@@ -73,10 +73,10 @@ class QOMFS(Operations):
return True
return False
return False
- except:
+ except QMPResponseError:
return False
- def read(self, path, length, offset, fh):
+ def read(self, path, size, offset, fh):
if not self.is_property(path):
return -ENOENT
@@ -86,13 +86,13 @@ class QOMFS(Operations):
try:
data = self.qmp.command('qom-get', path=path, property=prop)
data += '\n' # make values shell friendly
- except:
- raise FuseOSError(EPERM)
+ except QMPResponseError as err:
+ raise FuseOSError(EPERM) from err
if offset > len(data):
return ''
- return bytes(data[offset:][:length], encoding='utf-8')
+ return bytes(data[offset:][:size], encoding='utf-8')
def readlink(self, path):
if not self.is_link(path):
--
2.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 08/72] scripts/qom-fuse: Add docstrings
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (6 preceding siblings ...)
2020-11-04 0:34 ` [PATCH v2 07/72] scripts/qom-fuse: Apply pylint rules John Snow
@ 2020-11-04 0:34 ` John Snow
2020-11-04 0:34 ` [PATCH v2 09/72] scripts/qom-fuse: Convert to QOMCommand John Snow
` (63 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:34 UTC (permalink / raw)
To: qemu-devel; +Cc: Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
The methods inherited from fuse don't need docstrings; that's up to
fusepy to handle.
Signed-off-by: John Snow <jsnow@redhat.com>
---
scripts/qmp/qom-fuse | 22 ++++++++++++++++++++--
1 file changed, 20 insertions(+), 2 deletions(-)
diff --git a/scripts/qmp/qom-fuse b/scripts/qmp/qom-fuse
index 805e99c8ecde..5b80da9df66b 100755
--- a/scripts/qmp/qom-fuse
+++ b/scripts/qmp/qom-fuse
@@ -1,7 +1,20 @@
#!/usr/bin/env python3
+"""
+QEMU Object Model FUSE filesystem tool
+
+This script offers a simple FUSE filesystem within which the QOM tree
+may be browsed, queried and edited using traditional shell tooling.
+
+This script requires the 'fusepy' python package;
+you may install it by using ``pip3 install --user fusepy``.
+
+ENV:
+ QMP_SOCKET: Path to the QMP server socket
+
+Usage:
+ qom-fuse /mount/to/here
+"""
##
-# QEMU Object Model test tools
-#
# Copyright IBM, Corp. 2012
# Copyright (C) 2020 Red Hat, Inc.
#
@@ -30,6 +43,7 @@ fuse.fuse_python_api = (0, 2)
class QOMFS(Operations):
+ """QOMFS implements fuse.Operations to provide a QOM filesystem."""
def __init__(self, qmp):
self.qmp = qmp
self.qmp.connect()
@@ -37,6 +51,7 @@ class QOMFS(Operations):
self.ino_count = 1
def get_ino(self, path):
+ """Get an inode number for a given QOM path."""
if path in self.ino_map:
return self.ino_map[path]
self.ino_map[path] = self.ino_count
@@ -44,6 +59,7 @@ class QOMFS(Operations):
return self.ino_map[path]
def is_object(self, path):
+ """Is the given QOM path an object?"""
try:
self.qmp.command('qom-list', path=path)
return True
@@ -51,6 +67,7 @@ class QOMFS(Operations):
return False
def is_property(self, path):
+ """Is the given QOM path a property?"""
path, prop = path.rsplit('/', 1)
if path == '':
path = '/'
@@ -63,6 +80,7 @@ class QOMFS(Operations):
return False
def is_link(self, path):
+ """Is the given QOM path a link?"""
path, prop = path.rsplit('/', 1)
if path == '':
path = '/'
--
2.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 09/72] scripts/qom-fuse: Convert to QOMCommand
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (7 preceding siblings ...)
2020-11-04 0:34 ` [PATCH v2 08/72] scripts/qom-fuse: Add docstrings John Snow
@ 2020-11-04 0:34 ` John Snow
2020-11-04 0:35 ` [PATCH v2 10/72] scripts/qom-fuse: use QOMCommand.qom_list() John Snow
` (62 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:34 UTC (permalink / raw)
To: qemu-devel; +Cc: Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
Move qom-fuse onto the QOMCommand base established in
python/qemu/qmp/qom_common.py. The interface doesn't change
incompatibly, "qom-fuse mountpoint" still works as an invocation, and
QMP_SOCKET is still used as the environment variable.
Signed-off-by: John Snow <jsnow@redhat.com>
---
scripts/qmp/qom-fuse | 59 ++++++++++++++++++++++++++++++++++----------
1 file changed, 46 insertions(+), 13 deletions(-)
diff --git a/scripts/qmp/qom-fuse b/scripts/qmp/qom-fuse
index 5b80da9df66b..f9bf85f38217 100755
--- a/scripts/qmp/qom-fuse
+++ b/scripts/qmp/qom-fuse
@@ -8,11 +8,19 @@ may be browsed, queried and edited using traditional shell tooling.
This script requires the 'fusepy' python package;
you may install it by using ``pip3 install --user fusepy``.
-ENV:
- QMP_SOCKET: Path to the QMP server socket
-Usage:
- qom-fuse /mount/to/here
+usage: qom-fuse [-h] [--socket SOCKET] <mount>
+
+Mount a QOM tree as a FUSE filesystem
+
+positional arguments:
+ <mount> Mount point
+
+optional arguments:
+ -h, --help show this help message and exit
+ --socket SOCKET, -s SOCKET
+ QMP socket path or address (addr:port). May also be
+ set via QMP_SOCKET environment variable.
"""
##
# Copyright IBM, Corp. 2012
@@ -26,30 +34,56 @@ Usage:
# See the COPYING file in the top-level directory.
##
+import argparse
from errno import ENOENT, EPERM
import os
import stat
import sys
+from typing import Dict
import fuse
from fuse import FUSE, FuseOSError, Operations
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
-from qemu.qmp import QEMUMonitorProtocol, QMPResponseError
+from qemu.qmp import QMPResponseError
+from qemu.qmp.qom_common import QOMCommand
fuse.fuse_python_api = (0, 2)
-class QOMFS(Operations):
- """QOMFS implements fuse.Operations to provide a QOM filesystem."""
- def __init__(self, qmp):
- self.qmp = qmp
- self.qmp.connect()
- self.ino_map = {}
+class QOMFuse(QOMCommand, Operations):
+ """
+ QOMFuse implements both fuse.Operations and QOMCommand.
+
+ Operations implements the FS, and QOMCommand implements the CLI command.
+ """
+ name = 'fuse'
+ help = 'Mount a QOM tree as a FUSE filesystem'
+ fuse: FUSE
+
+ @classmethod
+ def configure_parser(cls, parser: argparse.ArgumentParser) -> None:
+ super().configure_parser(parser)
+ parser.add_argument(
+ 'mount',
+ metavar='<mount>',
+ action='store',
+ help="Mount point",
+ )
+
+ def __init__(self, args: argparse.Namespace):
+ super().__init__(args)
+ self.mount = args.mount
+ self.ino_map: Dict[str, int] = {}
self.ino_count = 1
+ def run(self) -> int:
+ print(f"Mounting QOMFS to '{self.mount}'", file=sys.stderr)
+ self.fuse = FUSE(self, self.mount, foreground=True)
+ return 0
+
def get_ino(self, path):
"""Get an inode number for a given QOM path."""
if path in self.ino_map:
@@ -172,5 +206,4 @@ class QOMFS(Operations):
if __name__ == '__main__':
- fuse = FUSE(QOMFS(QEMUMonitorProtocol(os.environ['QMP_SOCKET'])),
- sys.argv[1], foreground=True)
+ sys.exit(QOMFuse.entry_point())
--
2.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 10/72] scripts/qom-fuse: use QOMCommand.qom_list()
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (8 preceding siblings ...)
2020-11-04 0:34 ` [PATCH v2 09/72] scripts/qom-fuse: Convert to QOMCommand John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 11/72] scripts/qom-fuse: ensure QOMFuse.read always returns bytes John Snow
` (61 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
the qom_list method provides a type-safe object that's easier to type
check, so switch to using it.
Signed-off-by: John Snow <jsnow@redhat.com>
---
scripts/qmp/qom-fuse | 18 ++++++++----------
1 file changed, 8 insertions(+), 10 deletions(-)
diff --git a/scripts/qmp/qom-fuse b/scripts/qmp/qom-fuse
index f9bf85f38217..b1030273efaa 100755
--- a/scripts/qmp/qom-fuse
+++ b/scripts/qmp/qom-fuse
@@ -95,7 +95,7 @@ class QOMFuse(QOMCommand, Operations):
def is_object(self, path):
"""Is the given QOM path an object?"""
try:
- self.qmp.command('qom-list', path=path)
+ self.qom_list(path)
return True
except QMPResponseError:
return False
@@ -106,8 +106,8 @@ class QOMFuse(QOMCommand, Operations):
if path == '':
path = '/'
try:
- for item in self.qmp.command('qom-list', path=path):
- if item['name'] == prop:
+ for item in self.qom_list(path):
+ if item.name == prop:
return True
return False
except QMPResponseError:
@@ -119,11 +119,9 @@ class QOMFuse(QOMCommand, Operations):
if path == '':
path = '/'
try:
- for item in self.qmp.command('qom-list', path=path):
- if item['name'] == prop:
- if item['type'].startswith('link<'):
- return True
- return False
+ for item in self.qom_list(path):
+ if item.name == prop and item.link:
+ return True
return False
except QMPResponseError:
return False
@@ -201,8 +199,8 @@ class QOMFuse(QOMCommand, Operations):
def readdir(self, path, fh):
yield '.'
yield '..'
- for item in self.qmp.command('qom-list', path=path):
- yield str(item['name'])
+ for item in self.qom_list(path):
+ yield item.name
if __name__ == '__main__':
--
2.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 11/72] scripts/qom-fuse: ensure QOMFuse.read always returns bytes
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (9 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 10/72] scripts/qom-fuse: use QOMCommand.qom_list() John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 12/72] scripts/qom-fuse: add static type hints John Snow
` (60 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé,
Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
- Use FuseOSError to signal ENOENT instead of returning it
- Wrap qom-get in str(), as we don't always know its type
- The empty return should be b'', not ''.
Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
---
scripts/qmp/qom-fuse | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/scripts/qmp/qom-fuse b/scripts/qmp/qom-fuse
index b1030273efaa..b120b93391ba 100755
--- a/scripts/qmp/qom-fuse
+++ b/scripts/qmp/qom-fuse
@@ -128,19 +128,19 @@ class QOMFuse(QOMCommand, Operations):
def read(self, path, size, offset, fh):
if not self.is_property(path):
- return -ENOENT
+ raise FuseOSError(ENOENT)
path, prop = path.rsplit('/', 1)
if path == '':
path = '/'
try:
- data = self.qmp.command('qom-get', path=path, property=prop)
+ data = str(self.qmp.command('qom-get', path=path, property=prop))
data += '\n' # make values shell friendly
except QMPResponseError as err:
raise FuseOSError(EPERM) from err
if offset > len(data):
- return ''
+ return b''
return bytes(data[offset:][:size], encoding='utf-8')
--
2.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 12/72] scripts/qom-fuse: add static type hints
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (10 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 11/72] scripts/qom-fuse: ensure QOMFuse.read always returns bytes John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 13/72] scripts/qom-fuse: move to python/qemu/qmp/qom_fuse.py John Snow
` (59 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
Because fusepy does not have type hints, add some targeted warning
suppressions.
NOTE: Prior to this change, running 'mypy qemu' from the python
directory worked OK, but only coincidentally. Going forward, you will
need to run 'mypy -p qemu' instead. These invocation forms will be
codified in a CI test soon.
Signed-off-by: John Snow <jsnow@redhat.com>
---
python/setup.cfg | 8 ++++++++
scripts/qmp/qom-fuse | 26 +++++++++++++++++---------
2 files changed, 25 insertions(+), 9 deletions(-)
diff --git a/python/setup.cfg b/python/setup.cfg
index c963d05527aa..831555552f16 100644
--- a/python/setup.cfg
+++ b/python/setup.cfg
@@ -47,6 +47,14 @@ python_version = 3.6
warn_unused_configs = True
namespace_packages = True
+[mypy-qemu.qmp.qom_fuse]
+# fusepy has no type stubs:
+allow_subclassing_any = True
+
+[mypy-fuse]
+# fusepy has no type stubs:
+ignore_missing_imports = True
+
[pylint.messages control]
# Disable the message, report, category or checker with the given id(s). You
# can either give multiple identifiers separated by comma (,) or put this
diff --git a/scripts/qmp/qom-fuse b/scripts/qmp/qom-fuse
index b120b93391ba..22be36634967 100755
--- a/scripts/qmp/qom-fuse
+++ b/scripts/qmp/qom-fuse
@@ -39,7 +39,14 @@ from errno import ENOENT, EPERM
import os
import stat
import sys
-from typing import Dict
+from typing import (
+ IO,
+ Dict,
+ Iterator,
+ Mapping,
+ Optional,
+ Union,
+)
import fuse
from fuse import FUSE, FuseOSError, Operations
@@ -84,7 +91,7 @@ class QOMFuse(QOMCommand, Operations):
self.fuse = FUSE(self, self.mount, foreground=True)
return 0
- def get_ino(self, path):
+ def get_ino(self, path: str) -> int:
"""Get an inode number for a given QOM path."""
if path in self.ino_map:
return self.ino_map[path]
@@ -92,7 +99,7 @@ class QOMFuse(QOMCommand, Operations):
self.ino_count += 1
return self.ino_map[path]
- def is_object(self, path):
+ def is_object(self, path: str) -> bool:
"""Is the given QOM path an object?"""
try:
self.qom_list(path)
@@ -100,7 +107,7 @@ class QOMFuse(QOMCommand, Operations):
except QMPResponseError:
return False
- def is_property(self, path):
+ def is_property(self, path: str) -> bool:
"""Is the given QOM path a property?"""
path, prop = path.rsplit('/', 1)
if path == '':
@@ -113,7 +120,7 @@ class QOMFuse(QOMCommand, Operations):
except QMPResponseError:
return False
- def is_link(self, path):
+ def is_link(self, path: str) -> bool:
"""Is the given QOM path a link?"""
path, prop = path.rsplit('/', 1)
if path == '':
@@ -126,7 +133,7 @@ class QOMFuse(QOMCommand, Operations):
except QMPResponseError:
return False
- def read(self, path, size, offset, fh):
+ def read(self, path: str, size: int, offset: int, fh: IO[bytes]) -> bytes:
if not self.is_property(path):
raise FuseOSError(ENOENT)
@@ -144,7 +151,7 @@ class QOMFuse(QOMCommand, Operations):
return bytes(data[offset:][:size], encoding='utf-8')
- def readlink(self, path):
+ def readlink(self, path: str) -> Union[bool, str]:
if not self.is_link(path):
return False
path, prop = path.rsplit('/', 1)
@@ -152,7 +159,8 @@ class QOMFuse(QOMCommand, Operations):
return prefix + str(self.qmp.command('qom-get', path=path,
property=prop))
- def getattr(self, path, fh=None):
+ def getattr(self, path: str,
+ fh: Optional[IO[bytes]] = None) -> Mapping[str, object]:
if self.is_link(path):
value = {
'st_mode': 0o755 | stat.S_IFLNK,
@@ -196,7 +204,7 @@ class QOMFuse(QOMCommand, Operations):
raise FuseOSError(ENOENT)
return value
- def readdir(self, path, fh):
+ def readdir(self, path: str, fh: IO[bytes]) -> Iterator[str]:
yield '.'
yield '..'
for item in self.qom_list(path):
--
2.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 13/72] scripts/qom-fuse: move to python/qemu/qmp/qom_fuse.py
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (11 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 12/72] scripts/qom-fuse: add static type hints John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 14/72] scripts/qom-fuse: add redirection shim to python/qemu/qmp/qom-fuse.py John Snow
` (58 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
Move qom-fuse over to the python package now that it passes the
linter. Update the import paradigms so that it passes.
Immediately add 'fusepy' to the development requirements and re-lock the
pipenv so that the test suite continues to pass.
Signed-off-by: John Snow <jsnow@redhat.com>
---
python/Pipfile.lock | 13 ++++++++++---
scripts/qmp/qom-fuse => python/qemu/qmp/qom_fuse.py | 12 ++----------
python/setup.cfg | 1 +
3 files changed, 13 insertions(+), 13 deletions(-)
rename scripts/qmp/qom-fuse => python/qemu/qmp/qom_fuse.py (95%)
mode change 100755 => 100644
diff --git a/python/Pipfile.lock b/python/Pipfile.lock
index 0352a628ef6f..d1e56cbcafc6 100644
--- a/python/Pipfile.lock
+++ b/python/Pipfile.lock
@@ -46,6 +46,12 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==3.8.4"
},
+ "fusepy": {
+ "hashes": [
+ "sha256:72ff783ec2f43de3ab394e3f7457605bf04c8cf288a2f4068b4cde141d4ee6bd"
+ ],
+ "version": "==3.0.1"
+ },
"importlib-metadata": {
"hashes": [
"sha256:77a540690e24b0305878c37ffd421785a6f7e53c8b5720d211b211de8d0e95da",
@@ -208,10 +214,11 @@
},
"toml": {
"hashes": [
- "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f",
- "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"
+ "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
+ "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
],
- "version": "==0.10.1"
+ "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2'",
+ "version": "==0.10.2"
},
"typed-ast": {
"hashes": [
diff --git a/scripts/qmp/qom-fuse b/python/qemu/qmp/qom_fuse.py
old mode 100755
new mode 100644
similarity index 95%
rename from scripts/qmp/qom-fuse
rename to python/qemu/qmp/qom_fuse.py
index 22be36634967..b0adcc1f8e62
--- a/scripts/qmp/qom-fuse
+++ b/python/qemu/qmp/qom_fuse.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python3
"""
QEMU Object Model FUSE filesystem tool
@@ -36,7 +35,6 @@
import argparse
from errno import ENOENT, EPERM
-import os
import stat
import sys
from typing import (
@@ -51,10 +49,8 @@
import fuse
from fuse import FUSE, FuseOSError, Operations
-
-sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
-from qemu.qmp import QMPResponseError
-from qemu.qmp.qom_common import QOMCommand
+from . import QMPResponseError
+from .qom_common import QOMCommand
fuse.fuse_python_api = (0, 2)
@@ -209,7 +205,3 @@ def readdir(self, path: str, fh: IO[bytes]) -> Iterator[str]:
yield '..'
for item in self.qom_list(path):
yield item.name
-
-
-if __name__ == '__main__':
- sys.exit(QOMFuse.entry_point())
diff --git a/python/setup.cfg b/python/setup.cfg
index 831555552f16..a9a76784d45a 100644
--- a/python/setup.cfg
+++ b/python/setup.cfg
@@ -27,6 +27,7 @@ devel =
mypy >= 0.770
pylint >= 2.6.0
pytest >= 3.0.0
+ fusepy >= 2.0.4
[options.entry_points]
console_scripts =
--
2.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 14/72] scripts/qom-fuse: add redirection shim to python/qemu/qmp/qom-fuse.py
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (12 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 13/72] scripts/qom-fuse: move to python/qemu/qmp/qom_fuse.py John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 15/72] python: add fuse command to 'qom' tools John Snow
` (57 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel
Cc: Philippe Mathieu-Daudé,
Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
By leaving the script absent for a commit, git-blame travels to the new
file instead of staying on the shim.
Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
---
scripts/qmp/qom-fuse | 11 +++++++++++
1 file changed, 11 insertions(+)
create mode 100755 scripts/qmp/qom-fuse
diff --git a/scripts/qmp/qom-fuse b/scripts/qmp/qom-fuse
new file mode 100755
index 000000000000..a58c8ef9793b
--- /dev/null
+++ b/scripts/qmp/qom-fuse
@@ -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.qom_fuse import QOMFuse
+
+
+if __name__ == '__main__':
+ sys.exit(QOMFuse.entry_point())
--
2.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 15/72] python: add fuse command to 'qom' tools
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (13 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 14/72] scripts/qom-fuse: add redirection shim to python/qemu/qmp/qom-fuse.py John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 16/72] python: add optional fuse dependency John Snow
` (56 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
Signed-off-by: John Snow <jsnow@redhat.com>
---
python/qemu/qmp/qom.py | 12 +++++++++++-
python/setup.cfg | 1 +
2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/python/qemu/qmp/qom.py b/python/qemu/qmp/qom.py
index 912d1809e60d..52e06a949ab3 100644
--- a/python/qemu/qmp/qom.py
+++ b/python/qemu/qmp/qom.py
@@ -1,7 +1,7 @@
"""
QEMU Object Model testing tools.
-usage: qom.py [-h] {set,get,list,tree} ...
+usage: qom.py [-h] {set,get,list,tree,fuse} ...
Query and manipulate QOM data
@@ -14,6 +14,7 @@
get Get a QOM property value
list List QOM properties at a given path
tree Show QOM tree from a given path
+ fuse Mount a QOM tree as a FUSE filesystem
"""
##
# Copyright John Snow 2020, for Red Hat, Inc.
@@ -36,6 +37,15 @@
from .qom_common import QOMCommand
+try:
+ from .qom_fuse import QOMFuse
+except ImportError:
+ # either fusepy isn't installed, or qom_fuse itself is broken.
+ pass
+else:
+ assert issubclass(QOMFuse, QOMCommand)
+
+
class QOMSet(QOMCommand):
"""QOM Command - Set a property to a given value."""
name = 'set'
diff --git a/python/setup.cfg b/python/setup.cfg
index a9a76784d45a..908e53527ddc 100644
--- a/python/setup.cfg
+++ b/python/setup.cfg
@@ -36,6 +36,7 @@ console_scripts =
qom-get = qemu.qmp.qom:QOMGet.entry_point
qom-list = qemu.qmp.qom:QOMList.entry_point
qom-tree = qemu.qmp.qom:QOMTree.entry_point
+ qom-fuse = qemu.qmp.qom_fuse:QOMFuse.entry_point
[flake8]
extend-ignore = E722 # Prefer pylint's bare-except checks to flake8's
--
2.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 16/72] python: add optional fuse dependency
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (14 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 15/72] python: add fuse command to 'qom' tools John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 17/72] scripts/qemu-ga-client: apply isort rules John Snow
` (55 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
This can be opted into by installing "qemu[fuse]" instead of just
"qemu". The package ought to work perfectly well with or without it.
Signed-off-by: John Snow <jsnow@redhat.com>
---
python/setup.cfg | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/python/setup.cfg b/python/setup.cfg
index 908e53527ddc..4e1c638d72d5 100644
--- a/python/setup.cfg
+++ b/python/setup.cfg
@@ -29,6 +29,10 @@ devel =
pytest >= 3.0.0
fusepy >= 2.0.4
+# Provides qom-fuse functionality
+fuse =
+ fusepy >= 2.0.4
+
[options.entry_points]
console_scripts =
qom = qemu.qmp.qom:main
--
2.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 17/72] scripts/qemu-ga-client: apply isort rules
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (15 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 16/72] python: add optional fuse dependency John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 18/72] scripts/qemu-ga-client: apply (most) flake8 rules John Snow
` (54 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
Hint:
> ln -s scripts/qmp/qemu-ga-client python/qemu/qmp/qemu_ga_client.py
> cd python
> isort qemu
Signed-off-by: John Snow <jsnow@redhat.com>
---
scripts/qmp/qemu-ga-client | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/scripts/qmp/qemu-ga-client b/scripts/qmp/qemu-ga-client
index ce122984a9ca..ae71b30dfa43 100755
--- a/scripts/qmp/qemu-ga-client
+++ b/scripts/qmp/qemu-ga-client
@@ -36,10 +36,11 @@
# See also: https://wiki.qemu.org/Features/QAPI/GuestAgent
#
-import os
-import sys
import base64
+import os
import random
+import sys
+
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
from qemu import qmp
@@ -279,9 +280,9 @@ def main(address, cmd, args):
if __name__ == '__main__':
- import sys
- import os
import optparse
+ import os
+ import sys
address = os.environ['QGA_CLIENT_ADDRESS'] if 'QGA_CLIENT_ADDRESS' in os.environ else None
--
2.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 18/72] scripts/qemu-ga-client: apply (most) flake8 rules
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (16 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 17/72] scripts/qemu-ga-client: apply isort rules John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 19/72] scripts/qemu-ga-client: Fix exception handling John Snow
` (53 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
- Line length should be < 80
- You shouldn't perform unscoped imports except at the top of the module
Notably, the sys.path hack creates problems with the import rule. This
will be fixed later.
Signed-off-by: John Snow <jsnow@redhat.com>
---
scripts/qmp/qemu-ga-client | 28 ++++++++++++++++------------
1 file changed, 16 insertions(+), 12 deletions(-)
diff --git a/scripts/qmp/qemu-ga-client b/scripts/qmp/qemu-ga-client
index ae71b30dfa43..a12e0e9b2a6e 100755
--- a/scripts/qmp/qemu-ga-client
+++ b/scripts/qmp/qemu-ga-client
@@ -12,7 +12,8 @@
# Start QEMU with:
#
# # qemu [...] -chardev socket,path=/tmp/qga.sock,server,nowait,id=qga0 \
-# -device virtio-serial -device virtserialport,chardev=qga0,name=org.qemu.guest_agent.0
+# -device virtio-serial \
+# -device virtserialport,chardev=qga0,name=org.qemu.guest_agent.0
#
# Run the script:
#
@@ -37,6 +38,7 @@
#
import base64
+import optparse
import os
import random
import sys
@@ -94,9 +96,11 @@ class QemuGuestAgentClient:
msgs = []
msgs.append('version: ' + info['version'])
msgs.append('supported_commands:')
- enabled = [c['name'] for c in info['supported_commands'] if c['enabled']]
+ enabled = [c['name'] for c in info['supported_commands']
+ if c['enabled']]
msgs.append('\tenabled: ' + ', '.join(enabled))
- disabled = [c['name'] for c in info['supported_commands'] if not c['enabled']]
+ disabled = [c['name'] for c in info['supported_commands']
+ if not c['enabled']]
msgs.append('\tdisabled: ' + ', '.join(disabled))
return '\n'.join(msgs)
@@ -119,11 +123,11 @@ class QemuGuestAgentClient:
if ipaddr['ip-address-type'] == 'ipv4':
addr = ipaddr['ip-address']
mask = self.__gen_ipv4_netmask(int(ipaddr['prefix']))
- msgs.append("\tinet %s netmask %s" % (addr, mask))
+ msgs.append(f"\tinet {addr} netmask {mask}")
elif ipaddr['ip-address-type'] == 'ipv6':
addr = ipaddr['ip-address']
prefix = ipaddr['prefix']
- msgs.append("\tinet6 %s prefixlen %s" % (addr, prefix))
+ msgs.append(f"\tinet6 {addr} prefixlen {prefix}")
if nif['hardware-address'] != '00:00:00:00:00:00':
msgs.append("\tether " + nif['hardware-address'])
@@ -237,6 +241,8 @@ def _cmd_suspend(client, args):
def _cmd_shutdown(client, args):
client.shutdown()
+
+
_cmd_powerdown = _cmd_shutdown
@@ -280,17 +286,15 @@ def main(address, cmd, args):
if __name__ == '__main__':
- import optparse
- import os
- import sys
+ address = os.environ.get('QGA_CLIENT_ADDRESS')
- address = os.environ['QGA_CLIENT_ADDRESS'] if 'QGA_CLIENT_ADDRESS' in os.environ else None
-
- usage = "%prog [--address=<unix_path>|<ipv4_address>] <command> [args...]\n"
+ usage = ("%prog [--address=<unix_path>|<ipv4_address>]"
+ " <command> [args...]\n")
usage += '<command>: ' + ', '.join(commands)
parser = optparse.OptionParser(usage=usage)
parser.add_option('--address', action='store', type='string',
- default=address, help='Specify a ip:port pair or a unix socket path')
+ default=address,
+ help='Specify a ip:port pair or a unix socket path')
options, args = parser.parse_args()
address = options.address
--
2.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 19/72] scripts/qemu-ga-client: Fix exception handling
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (17 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 18/72] scripts/qemu-ga-client: apply (most) flake8 rules John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 20/72] scripts/qemu-ga-client: replace deprecated optparse with argparse John Snow
` (52 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
Fixes: 50d189c
Signed-off-by: John Snow <jsnow@redhat.com>
---
scripts/qmp/qemu-ga-client | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/scripts/qmp/qemu-ga-client b/scripts/qmp/qemu-ga-client
index a12e0e9b2a6e..c4fbb6165d54 100755
--- a/scripts/qmp/qemu-ga-client
+++ b/scripts/qmp/qemu-ga-client
@@ -56,8 +56,6 @@ class QemuGuestAgent(qmp.QEMUMonitorProtocol):
class QemuGuestAgentClient:
- error = QemuGuestAgent.error
-
def __init__(self, address):
self.qga = QemuGuestAgent(address)
self.qga.connect(negotiate=False)
@@ -137,7 +135,7 @@ class QemuGuestAgentClient:
self.qga.settimeout(timeout)
try:
self.qga.ping()
- except self.qga.timeout:
+ except TimeoutError:
return False
return True
@@ -269,11 +267,11 @@ def main(address, cmd, args):
try:
client = QemuGuestAgentClient(address)
- except QemuGuestAgent.error as e:
+ except OSError as err:
import errno
- print(e)
- if e.errno == errno.ECONNREFUSED:
+ print(err)
+ if err.errno == errno.ECONNREFUSED:
print('Hint: qemu is not running?')
sys.exit(1)
--
2.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 20/72] scripts/qemu-ga-client: replace deprecated optparse with argparse
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (18 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 19/72] scripts/qemu-ga-client: Fix exception handling John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 21/72] scripts/qemu-ga-client: add module docstring John Snow
` (51 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
Signed-off-by: John Snow <jsnow@redhat.com>
---
scripts/qmp/qemu-ga-client | 32 +++++++++++++++-----------------
1 file changed, 15 insertions(+), 17 deletions(-)
diff --git a/scripts/qmp/qemu-ga-client b/scripts/qmp/qemu-ga-client
index c4fbb6165d54..73d262e8beb4 100755
--- a/scripts/qmp/qemu-ga-client
+++ b/scripts/qmp/qemu-ga-client
@@ -37,8 +37,8 @@
# See also: https://wiki.qemu.org/Features/QAPI/GuestAgent
#
+import argparse
import base64
-import optparse
import os
import random
import sys
@@ -255,7 +255,7 @@ def _cmd_reboot(client, args):
commands = [m.replace('_cmd_', '') for m in dir() if '_cmd_' in m]
-def main(address, cmd, args):
+def send_command(address, cmd, args):
if not os.path.exists(address):
print('%s not found' % address)
sys.exit(1)
@@ -283,25 +283,23 @@ def main(address, cmd, args):
globals()['_cmd_' + cmd](client, args)
-if __name__ == '__main__':
+def main():
address = os.environ.get('QGA_CLIENT_ADDRESS')
- usage = ("%prog [--address=<unix_path>|<ipv4_address>]"
- " <command> [args...]\n")
- usage += '<command>: ' + ', '.join(commands)
- parser = optparse.OptionParser(usage=usage)
- parser.add_option('--address', action='store', type='string',
- default=address,
- help='Specify a ip:port pair or a unix socket path')
- options, args = parser.parse_args()
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--address', action='store',
+ default=address,
+ help='Specify a ip:port pair or a unix socket path')
+ parser.add_argument('command', choices=commands)
+ parser.add_argument('args', nargs='*')
- address = options.address
- if address is None:
+ args = parser.parse_args()
+ if args.address is None:
parser.error('address is not specified')
sys.exit(1)
- if len(args) == 0:
- parser.error('Less argument')
- sys.exit(1)
+ send_command(args.address, args.command, args.args)
- main(address, args[0], args[1:])
+
+if __name__ == '__main__':
+ main()
--
2.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 21/72] scripts/qemu-ga-client: add module docstring
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (19 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 20/72] scripts/qemu-ga-client: replace deprecated optparse with argparse John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 22/72] scripts/qemu-ga-client: apply (most) pylint rules John Snow
` (50 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
Turn that nice usage comment into a docstring.
Signed-off-by: John Snow <jsnow@redhat.com>
---
scripts/qmp/qemu-ga-client | 65 +++++++++++++++++++-------------------
1 file changed, 33 insertions(+), 32 deletions(-)
diff --git a/scripts/qmp/qemu-ga-client b/scripts/qmp/qemu-ga-client
index 73d262e8beb4..1061d2eb1437 100755
--- a/scripts/qmp/qemu-ga-client
+++ b/scripts/qmp/qemu-ga-client
@@ -1,41 +1,42 @@
#!/usr/bin/env python3
-# QEMU Guest Agent Client
-#
# Copyright (C) 2012 Ryota Ozaki <ozaki.ryota@gmail.com>
#
# 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 [...] -chardev socket,path=/tmp/qga.sock,server,nowait,id=qga0 \
-# -device virtio-serial \
-# -device virtserialport,chardev=qga0,name=org.qemu.guest_agent.0
-#
-# Run the script:
-#
-# $ qemu-ga-client --address=/tmp/qga.sock <command> [args...]
-#
-# or
-#
-# $ export QGA_CLIENT_ADDRESS=/tmp/qga.sock
-# $ qemu-ga-client <command> [args...]
-#
-# For example:
-#
-# $ qemu-ga-client cat /etc/resolv.conf
-# # Generated by NetworkManager
-# nameserver 10.0.2.3
-# $ qemu-ga-client fsfreeze status
-# thawed
-# $ qemu-ga-client fsfreeze freeze
-# 2 filesystems frozen
-#
-# See also: https://wiki.qemu.org/Features/QAPI/GuestAgent
-#
+
+"""
+QEMU Guest Agent Client
+
+Usage:
+
+Start QEMU with:
+
+# qemu [...] -chardev socket,path=/tmp/qga.sock,server,nowait,id=qga0 \
+ -device virtio-serial \
+ -device virtserialport,chardev=qga0,name=org.qemu.guest_agent.0
+
+Run the script:
+
+$ qemu-ga-client --address=/tmp/qga.sock <command> [args...]
+
+or
+
+$ export QGA_CLIENT_ADDRESS=/tmp/qga.sock
+$ qemu-ga-client <command> [args...]
+
+For example:
+
+$ qemu-ga-client cat /etc/resolv.conf
+# Generated by NetworkManager
+nameserver 10.0.2.3
+$ qemu-ga-client fsfreeze status
+thawed
+$ qemu-ga-client fsfreeze freeze
+2 filesystems frozen
+
+See also: https://wiki.qemu.org/Features/QAPI/GuestAgent
+"""
import argparse
import base64
--
2.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 22/72] scripts/qemu-ga-client: apply (most) pylint rules
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (20 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 21/72] scripts/qemu-ga-client: add module docstring John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 23/72] python/qmp: Correct type of QMPReturnValue John Snow
` (49 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
I'm only doing a very quick best-effort to preserve this script, to help
keep it from breaking further. I think there are pending ideas swirling
on the right way to implement better SDKs and better clients, and this
script might be a handy reference for those discussions. It presents
some interesting design problems, like static type safety when using an
RPC-like mechanism.
So; disable docstrings for now. If you would like to help improve this
script, please add docstrings alongside any refactors or rejuvenations
you might apply at that time.
Signed-off-by: John Snow <jsnow@redhat.com>
---
scripts/qmp/qemu-ga-client | 18 +++++++++++++++---
1 file changed, 15 insertions(+), 3 deletions(-)
diff --git a/scripts/qmp/qemu-ga-client b/scripts/qmp/qemu-ga-client
index 1061d2eb1437..59b91c78d884 100755
--- a/scripts/qmp/qemu-ga-client
+++ b/scripts/qmp/qemu-ga-client
@@ -40,6 +40,7 @@ See also: https://wiki.qemu.org/Features/QAPI/GuestAgent
import argparse
import base64
+import errno
import os
import random
import sys
@@ -49,6 +50,13 @@ sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
from qemu import qmp
+# This script has not seen many patches or careful attention in quite
+# some time. If you would like to improve it, please review the design
+# carefully and add docstrings at that point in time. Until then:
+
+# pylint: disable=missing-docstring
+
+
class QemuGuestAgent(qmp.QEMUMonitorProtocol):
def __getattr__(self, name):
def wrapper(**kwds):
@@ -104,7 +112,8 @@ class QemuGuestAgentClient:
return '\n'.join(msgs)
- def __gen_ipv4_netmask(self, prefixlen):
+ @classmethod
+ def __gen_ipv4_netmask(cls, prefixlen):
mask = int('1' * prefixlen + '0' * (32 - prefixlen), 2)
return '.'.join([str(mask >> 24),
str((mask >> 16) & 0xff),
@@ -207,10 +216,12 @@ def _cmd_fstrim(client, args):
def _cmd_ifconfig(client, args):
+ assert not args
print(client.ifconfig())
def _cmd_info(client, args):
+ assert not args
print(client.info())
@@ -239,6 +250,7 @@ def _cmd_suspend(client, args):
def _cmd_shutdown(client, args):
+ assert not args
client.shutdown()
@@ -246,10 +258,12 @@ _cmd_powerdown = _cmd_shutdown
def _cmd_halt(client, args):
+ assert not args
client.shutdown('halt')
def _cmd_reboot(client, args):
+ assert not args
client.shutdown('reboot')
@@ -269,8 +283,6 @@ def send_command(address, cmd, args):
try:
client = QemuGuestAgentClient(address)
except OSError as err:
- import errno
-
print(err)
if err.errno == errno.ECONNREFUSED:
print('Hint: qemu is not running?')
--
2.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 23/72] python/qmp: Correct type of QMPReturnValue
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (21 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 22/72] scripts/qemu-ga-client: apply (most) pylint rules John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 24/72] scripts/qmp/qemu-ga-client: add mypy type hints John Snow
` (48 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
It's only a Dict[str, Any] most of the time. It's not actually
guaranteed to be anything in particular. Fix this type to be
more accurate to the reality we live in.
Signed-off-by: John Snow <jsnow@redhat.com>
---
python/qemu/qmp/__init__.py | 25 +++++++++++++++----------
1 file changed, 15 insertions(+), 10 deletions(-)
diff --git a/python/qemu/qmp/__init__.py b/python/qemu/qmp/__init__.py
index 9606248a3d2e..7355a558796f 100644
--- a/python/qemu/qmp/__init__.py
+++ b/python/qemu/qmp/__init__.py
@@ -35,14 +35,19 @@
)
-# QMPMessage is a QMP Message of any kind.
-# e.g. {'yee': 'haw'}
-#
-# QMPReturnValue is the inner value of return values only.
-# {'return': {}} is the QMPMessage,
-# {} is the QMPReturnValue.
+#: QMPMessage is an entire QMP message of any kind.
QMPMessage = Dict[str, Any]
-QMPReturnValue = Dict[str, Any]
+
+#: QMPReturnValue is the 'return' value of a command.
+QMPReturnValue = 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.
+#
+# {'return': {}} is a QMPMessage,
+# {} is the QMPReturnValue.
+
InternetAddrT = Tuple[str, str]
UnixAddrT = str
@@ -271,8 +276,8 @@ def cmd_obj(self, qmp_cmd: QMPMessage) -> QMPMessage:
return resp
def cmd(self, name: str,
- args: Optional[Dict[str, Any]] = None,
- cmd_id: Optional[Any] = None) -> QMPMessage:
+ args: Optional[Dict[str, object]] = None,
+ cmd_id: Optional[object] = None) -> QMPMessage:
"""
Build a QMP command and send it to the QMP Monitor.
@@ -287,7 +292,7 @@ def cmd(self, name: str,
qmp_cmd['id'] = cmd_id
return self.cmd_obj(qmp_cmd)
- def command(self, cmd: str, **kwds: Any) -> QMPReturnValue:
+ def command(self, cmd: str, **kwds: object) -> QMPReturnValue:
"""
Build and send a QMP command to the monitor, report errors if any
"""
--
2.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 24/72] scripts/qmp/qemu-ga-client: add mypy type hints
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (22 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 23/72] python/qmp: Correct type of QMPReturnValue John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 25/72] scripts/qemu-ga-client: move to python/qemu/qmp/qemu_ga_client.py John Snow
` (47 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
This script is in slightly rough shape, but it still works. A lot of
care went into its initial development. In good faith, I'm updating it
to the latest Python coding standards. If there is in interest in this
script, though, I'll be asking for a contributor to take care of it
further.
Signed-off-by: John Snow <jsnow@redhat.com>
---
scripts/qmp/qemu-ga-client | 89 +++++++++++++++++++++-----------------
1 file changed, 49 insertions(+), 40 deletions(-)
diff --git a/scripts/qmp/qemu-ga-client b/scripts/qmp/qemu-ga-client
index 59b91c78d884..3e617e7e7abe 100755
--- a/scripts/qmp/qemu-ga-client
+++ b/scripts/qmp/qemu-ga-client
@@ -44,10 +44,18 @@ import errno
import os
import random
import sys
+from typing import (
+ Any,
+ Callable,
+ Dict,
+ Optional,
+ Sequence,
+)
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
from qemu import qmp
+from qemu.qmp import SocketAddrT
# This script has not seen many patches or careful attention in quite
@@ -58,18 +66,18 @@ from qemu import qmp
class QemuGuestAgent(qmp.QEMUMonitorProtocol):
- def __getattr__(self, name):
- def wrapper(**kwds):
+ def __getattr__(self, name: str) -> Callable[..., Any]:
+ def wrapper(**kwds: object) -> object:
return self.command('guest-' + name.replace('_', '-'), **kwds)
return wrapper
class QemuGuestAgentClient:
- def __init__(self, address):
+ def __init__(self, address: SocketAddrT):
self.qga = QemuGuestAgent(address)
self.qga.connect(negotiate=False)
- def sync(self, timeout=3):
+ def sync(self, timeout: Optional[float] = 3) -> None:
# Avoid being blocked forever
if not self.ping(timeout):
raise EnvironmentError('Agent seems not alive')
@@ -79,9 +87,9 @@ class QemuGuestAgentClient:
if isinstance(ret, int) and int(ret) == uid:
break
- def __file_read_all(self, handle):
+ def __file_read_all(self, handle: int) -> bytes:
eof = False
- data = ''
+ data = b''
while not eof:
ret = self.qga.file_read(handle=handle, count=1024)
_data = base64.b64decode(ret['buf-b64'])
@@ -89,7 +97,7 @@ class QemuGuestAgentClient:
eof = ret['eof']
return data
- def read(self, path):
+ def read(self, path: str) -> bytes:
handle = self.qga.file_open(path=path)
try:
data = self.__file_read_all(handle)
@@ -97,7 +105,7 @@ class QemuGuestAgentClient:
self.qga.file_close(handle=handle)
return data
- def info(self):
+ def info(self) -> str:
info = self.qga.info()
msgs = []
@@ -113,14 +121,14 @@ class QemuGuestAgentClient:
return '\n'.join(msgs)
@classmethod
- def __gen_ipv4_netmask(cls, prefixlen):
+ def __gen_ipv4_netmask(cls, prefixlen: int) -> str:
mask = int('1' * prefixlen + '0' * (32 - prefixlen), 2)
return '.'.join([str(mask >> 24),
str((mask >> 16) & 0xff),
str((mask >> 8) & 0xff),
str(mask & 0xff)])
- def ifconfig(self):
+ def ifconfig(self) -> str:
nifs = self.qga.network_get_interfaces()
msgs = []
@@ -141,7 +149,7 @@ class QemuGuestAgentClient:
return '\n'.join(msgs)
- def ping(self, timeout):
+ def ping(self, timeout: Optional[float]) -> bool:
self.qga.settimeout(timeout)
try:
self.qga.ping()
@@ -149,37 +157,40 @@ class QemuGuestAgentClient:
return False
return True
- def fsfreeze(self, cmd):
+ def fsfreeze(self, cmd: str) -> object:
if cmd not in ['status', 'freeze', 'thaw']:
raise Exception('Invalid command: ' + cmd)
-
+ # Can be int (freeze, thaw) or GuestFsfreezeStatus (status)
return getattr(self.qga, 'fsfreeze' + '_' + cmd)()
- def fstrim(self, minimum=0):
- return getattr(self.qga, 'fstrim')(minimum=minimum)
+ def fstrim(self, minimum: int) -> Dict[str, object]:
+ # returns GuestFilesystemTrimResponse
+ ret = getattr(self.qga, 'fstrim')(minimum=minimum)
+ assert isinstance(ret, dict)
+ return ret
- def suspend(self, mode):
+ def suspend(self, mode: str) -> None:
if mode not in ['disk', 'ram', 'hybrid']:
raise Exception('Invalid mode: ' + mode)
try:
getattr(self.qga, 'suspend' + '_' + mode)()
# On error exception will raise
- except self.qga.timeout:
+ except TimeoutError:
# On success command will timed out
return
- def shutdown(self, mode='powerdown'):
+ def shutdown(self, mode: str = 'powerdown') -> None:
if mode not in ['powerdown', 'halt', 'reboot']:
raise Exception('Invalid mode: ' + mode)
try:
self.qga.shutdown(mode=mode)
- except self.qga.timeout:
- return
+ except TimeoutError:
+ pass
-def _cmd_cat(client, args):
+def _cmd_cat(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
if len(args) != 1:
print('Invalid argument')
print('Usage: cat <file>')
@@ -187,7 +198,7 @@ def _cmd_cat(client, args):
print(client.read(args[0]))
-def _cmd_fsfreeze(client, args):
+def _cmd_fsfreeze(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
usage = 'Usage: fsfreeze status|freeze|thaw'
if len(args) != 1:
print('Invalid argument')
@@ -201,13 +212,14 @@ def _cmd_fsfreeze(client, args):
ret = client.fsfreeze(cmd)
if cmd == 'status':
print(ret)
- elif cmd == 'freeze':
- print("%d filesystems frozen" % ret)
- else:
- print("%d filesystems thawed" % ret)
+ return
+ assert isinstance(ret, int)
+ verb = 'frozen' if cmd == 'freeze' else 'thawed'
+ print(f"{ret:d} filesystems {verb}")
-def _cmd_fstrim(client, args):
+
+def _cmd_fstrim(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
if len(args) == 0:
minimum = 0
else:
@@ -215,28 +227,25 @@ def _cmd_fstrim(client, args):
print(client.fstrim(minimum))
-def _cmd_ifconfig(client, args):
+def _cmd_ifconfig(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
assert not args
print(client.ifconfig())
-def _cmd_info(client, args):
+def _cmd_info(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
assert not args
print(client.info())
-def _cmd_ping(client, args):
- if len(args) == 0:
- timeout = 3
- else:
- timeout = float(args[0])
+def _cmd_ping(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
+ timeout = 3.0 if len(args) == 0 else float(args[0])
alive = client.ping(timeout)
if not alive:
print("Not responded in %s sec" % args[0])
sys.exit(1)
-def _cmd_suspend(client, args):
+def _cmd_suspend(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
usage = 'Usage: suspend disk|ram|hybrid'
if len(args) != 1:
print('Less argument')
@@ -249,7 +258,7 @@ def _cmd_suspend(client, args):
client.suspend(args[0])
-def _cmd_shutdown(client, args):
+def _cmd_shutdown(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
assert not args
client.shutdown()
@@ -257,12 +266,12 @@ def _cmd_shutdown(client, args):
_cmd_powerdown = _cmd_shutdown
-def _cmd_halt(client, args):
+def _cmd_halt(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
assert not args
client.shutdown('halt')
-def _cmd_reboot(client, args):
+def _cmd_reboot(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
assert not args
client.shutdown('reboot')
@@ -270,7 +279,7 @@ def _cmd_reboot(client, args):
commands = [m.replace('_cmd_', '') for m in dir() if '_cmd_' in m]
-def send_command(address, cmd, args):
+def send_command(address: str, cmd: str, args: Sequence[str]) -> None:
if not os.path.exists(address):
print('%s not found' % address)
sys.exit(1)
@@ -296,7 +305,7 @@ def send_command(address, cmd, args):
globals()['_cmd_' + cmd](client, args)
-def main():
+def main() -> None:
address = os.environ.get('QGA_CLIENT_ADDRESS')
parser = argparse.ArgumentParser()
--
2.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 25/72] scripts/qemu-ga-client: move to python/qemu/qmp/qemu_ga_client.py
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (23 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 24/72] scripts/qmp/qemu-ga-client: add mypy type hints John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 26/72] python/qemu-ga-client: add entry point John Snow
` (46 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
The script will be unavailable for a few commits before being
restored. This helps move git history into the new file. To prevent
linter regressions, though, we do need to immediately touch up the
filename, remove the executable bit, and change the import paradigm.
Signed-off-by: John Snow <jsnow@redhat.com>
---
scripts/qmp/qemu-ga-client => python/qemu/qmp/qemu_ga_client.py | 2 --
1 file changed, 2 deletions(-)
rename scripts/qmp/qemu-ga-client => python/qemu/qmp/qemu_ga_client.py (99%)
diff --git a/scripts/qmp/qemu-ga-client b/python/qemu/qmp/qemu_ga_client.py
similarity index 99%
rename from scripts/qmp/qemu-ga-client
rename to python/qemu/qmp/qemu_ga_client.py
index 3e617e7e7abe..43abc023c63c 100755
--- a/scripts/qmp/qemu-ga-client
+++ b/python/qemu/qmp/qemu_ga_client.py
@@ -52,8 +52,6 @@
Sequence,
)
-
-sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
from qemu import qmp
from qemu.qmp import SocketAddrT
--
2.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 26/72] python/qemu-ga-client: add entry point
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (24 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 25/72] scripts/qemu-ga-client: move to python/qemu/qmp/qemu_ga_client.py John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 27/72] scripts/qemu-ga-client: Add forwarder stub John Snow
` (45 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
Remove the shebang, and add a package-defined entry point instead.
Signed-off-by: John Snow <jsnow@redhat.com>
---
python/qemu/qmp/qemu_ga_client.py | 2 --
python/setup.cfg | 1 +
2 files changed, 1 insertion(+), 2 deletions(-)
mode change 100755 => 100644 python/qemu/qmp/qemu_ga_client.py
diff --git a/python/qemu/qmp/qemu_ga_client.py b/python/qemu/qmp/qemu_ga_client.py
old mode 100755
new mode 100644
index 43abc023c63c..6915e906374b
--- a/python/qemu/qmp/qemu_ga_client.py
+++ b/python/qemu/qmp/qemu_ga_client.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python3
-
# Copyright (C) 2012 Ryota Ozaki <ozaki.ryota@gmail.com>
#
# This work is licensed under the terms of the GNU GPL, version 2. See
diff --git a/python/setup.cfg b/python/setup.cfg
index 4e1c638d72d5..f2f54bcaefe8 100644
--- a/python/setup.cfg
+++ b/python/setup.cfg
@@ -41,6 +41,7 @@ console_scripts =
qom-list = qemu.qmp.qom:QOMList.entry_point
qom-tree = qemu.qmp.qom:QOMTree.entry_point
qom-fuse = qemu.qmp.qom_fuse:QOMFuse.entry_point
+ qemu-ga-client = qemu.qmp.qemu_ga_client:main
[flake8]
extend-ignore = E722 # Prefer pylint's bare-except checks to flake8's
--
2.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 27/72] scripts/qemu-ga-client: Add forwarder stub
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (25 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 26/72] python/qemu-ga-client: add entry point John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 28/72] scripts/qmp-shell: apply isort rules John Snow
` (44 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
Mimicking other recently moved tools: add a little forwarder stub until
we are sure that everyone is comfortable understanding how to use the
tools in their new location.
Signed-off-by: John Snow <jsnow@redhat.com>
---
scripts/qmp/qemu-ga-client | 11 +++++++++++
1 file changed, 11 insertions(+)
create mode 100755 scripts/qmp/qemu-ga-client
diff --git a/scripts/qmp/qemu-ga-client b/scripts/qmp/qemu-ga-client
new file mode 100755
index 000000000000..102fd2cad93f
--- /dev/null
+++ b/scripts/qmp/qemu-ga-client
@@ -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 qemu_ga_client
+
+
+if __name__ == '__main__':
+ sys.exit(qemu_ga_client.main())
--
2.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 28/72] scripts/qmp-shell: apply isort rules
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (26 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 27/72] scripts/qemu-ga-client: Add forwarder stub John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 29/72] scripts/qmp-shell: Apply flake8 rules John Snow
` (43 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: 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 b4d06096abd6..7ed45b8195b1 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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 29/72] scripts/qmp-shell: Apply flake8 rules
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (27 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 28/72] scripts/qmp-shell: apply isort rules John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 30/72] scripts/qmp-shell: fix show_banner signature John Snow
` (42 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: 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 | 68 +++++++++++++++++++++++++++++--------------
1 file changed, 46 insertions(+), 22 deletions(-)
diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index 7ed45b8195b1..20c420ba4251 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -88,12 +88,15 @@ class QMPCompleter(list):
else:
state -= 1
+
class QMPShellError(Exception):
pass
+
class QMPShellBadPort(QMPShellError):
pass
+
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
@@ -107,6 +110,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,7 +135,7 @@ class QMPShell(qmp.QEMUMonitorProtocol):
port = int(addr[1])
except ValueError:
raise QMPShellBadPort
- return ( addr[0], port )
+ return addr[0], port
# socket path
return arg
@@ -148,8 +152,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)
@@ -197,7 +201,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('.')
@@ -206,14 +212,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):
@@ -223,7 +231,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(':
@@ -232,9 +241,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
@@ -245,7 +257,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
@@ -254,7 +266,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
@@ -295,7 +307,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:
@@ -324,6 +336,7 @@ class QMPShell(qmp.QEMUMonitorProtocol):
def set_verbosity(self, verbose):
self._verbose = verbose
+
class HMPShell(QMPShell):
def __init__(self, address):
QMPShell.__init__(self, address)
@@ -332,7 +345,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:
@@ -344,7 +357,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'):
@@ -360,17 +373,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
@@ -394,20 +411,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
@@ -457,5 +480,6 @@ def main():
pass
qemu.close()
+
if __name__ == '__main__':
main()
--
2.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 30/72] scripts/qmp-shell: fix show_banner signature
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (28 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 29/72] scripts/qmp-shell: Apply flake8 rules John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 31/72] scripts/qmp-shell: fix exception handling John Snow
` (41 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: 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 20c420ba4251..e2ad979aad98 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -408,8 +408,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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 31/72] scripts/qmp-shell: fix exception handling
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (29 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 30/72] scripts/qmp-shell: fix show_banner signature John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 32/72] scripts/qmp-shell: fix connect method signature John Snow
` (40 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: 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 e2ad979aad98..330f8b32f9c2 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -471,7 +471,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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 32/72] scripts/qmp-shell: fix connect method signature
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (30 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 31/72] scripts/qmp-shell: fix exception handling John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 33/72] scripts/qmp-shell: remove shadowed variable from _print() John Snow
` (39 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
It needs to match the parent's. (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 330f8b32f9c2..27632ed5fee7 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -297,7 +297,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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 33/72] scripts/qmp-shell: remove shadowed variable from _print()
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (31 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 32/72] scripts/qmp-shell: fix connect method signature John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 34/72] scripts/qmp-shell: use @classmethod where appropriate John Snow
` (38 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: 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 27632ed5fee7..6767a01eaaed 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -270,11 +270,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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 34/72] scripts/qmp-shell: use @classmethod where appropriate
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (32 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 33/72] scripts/qmp-shell: remove shadowed variable from _print() John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 35/72] scripts/qmp-shell: Use python3-style super() John Snow
` (37 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: 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 | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index 6767a01eaaed..69359ec8ad7f 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -101,7 +101,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':
@@ -124,7 +125,8 @@ class QMPShell(qmp.QEMUMonitorProtocol):
self._histfile = os.path.join(os.path.expanduser('~'),
'.qmp-shell_history')
- def __get_address(self, arg):
+ @classmethod
+ def __get_address(cls, arg):
"""
Figure out if the argument is in the port:host form, if it's not it's
probably a file path.
@@ -171,7 +173,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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 35/72] scripts/qmp-shell: Use python3-style super()
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (33 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 34/72] scripts/qmp-shell: use @classmethod where appropriate John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 36/72] scripts/qmp-shell: declare verbose in __init__ John Snow
` (36 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: 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 69359ec8ad7f..2e9ae5d59bd9 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -116,7 +116,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.__get_address(address))
+ super().__init__(self.__get_address(address))
self._greeting = None
self._completer = None
self._pretty = pretty
@@ -301,7 +301,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!'):
@@ -342,7 +342,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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 36/72] scripts/qmp-shell: declare verbose in __init__
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (34 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 35/72] scripts/qmp-shell: Use python3-style super() John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 37/72] scripts/qmp-shell: use triple-double-quote docstring style John Snow
` (35 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
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 2e9ae5d59bd9..7233c4e00b89 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -124,6 +124,7 @@ class QMPShell(qmp.QEMUMonitorProtocol):
self._actions = list()
self._histfile = os.path.join(os.path.expanduser('~'),
'.qmp-shell_history')
+ self._verbose = False
@classmethod
def __get_address(cls, arg):
--
2.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 37/72] scripts/qmp-shell: use triple-double-quote docstring style
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (35 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 36/72] scripts/qmp-shell: declare verbose in __init__ John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 38/72] scripts/qmp-shell: ignore visit_Name name John Snow
` (34 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: 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 7233c4e00b89..73b2c099f0f0 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -98,9 +98,12 @@ class QMPShellBadPort(QMPShellError):
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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 38/72] scripts/qmp-shell: ignore visit_Name name
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (36 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 37/72] scripts/qmp-shell: use triple-double-quote docstring style John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 39/72] scripts/qmp-shell: make QMPCompleter returns explicit John Snow
` (33 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: 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 73b2c099f0f0..0f03c6c89575 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -105,7 +105,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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 39/72] scripts/qmp-shell: make QMPCompleter returns explicit
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (37 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 38/72] scripts/qmp-shell: ignore visit_Name name John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 40/72] scripts/qmp-shell: rename one and two-letter variables John Snow
` (32 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: 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 0f03c6c89575..e4163daf438d 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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 40/72] scripts/qmp-shell: rename one and two-letter variables
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (38 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 39/72] scripts/qmp-shell: make QMPCompleter returns explicit John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 41/72] scripts/qmp-shell: fix shell history exception handling John Snow
` (31 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
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 e4163daf438d..2fd677e3dabd 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -196,8 +196,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:
@@ -215,14 +215,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'
@@ -287,8 +287,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
@@ -333,8 +333,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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 41/72] scripts/qmp-shell: fix shell history exception handling
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (39 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 40/72] scripts/qmp-shell: rename one and two-letter variables John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 42/72] scripts/qmp-shell: explicitly chain exception context John Snow
` (30 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: 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 2fd677e3dabd..6ad9384c1804 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
@@ -163,19 +162,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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 42/72] scripts/qmp-shell: explicitly chain exception context
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (40 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 41/72] scripts/qmp-shell: fix shell history exception handling John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 43/72] scripts/qmp-shell: remove if-raise-else patterns John Snow
` (29 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
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 6ad9384c1804..b3ab735397d4 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -138,8 +138,8 @@ class QMPShell(qmp.QEMUMonitorProtocol):
if len(addr) == 2:
try:
port = int(addr[1])
- except ValueError:
- raise QMPShellBadPort
+ except ValueError as err:
+ raise QMPShellBadPort from err
return addr[0], port
# socket path
return arg
--
2.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 43/72] scripts/qmp-shell: remove if-raise-else patterns
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (41 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 42/72] scripts/qmp-shell: explicitly chain exception context John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 44/72] scripts/qmp-shell: use isinstance() instead of type() John Snow
` (28 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
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 b3ab735397d4..47863a604f51 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -224,8 +224,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):
@@ -329,13 +328,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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 44/72] scripts/qmp-shell: use isinstance() instead of type()
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (42 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 43/72] scripts/qmp-shell: remove if-raise-else patterns John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 45/72] scripts/qmp-shell: use argparse John Snow
` (27 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
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 47863a604f51..0cd3ed7d43c7 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -215,13 +215,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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 45/72] scripts/qmp-shell: use argparse
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (43 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 44/72] scripts/qmp-shell: use isinstance() instead of type() John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 46/72] python/qmp: Fix type of SocketAddrT John Snow
` (26 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: 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 0cd3ed7d43c7..d5496aeac0bd 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
@@ -421,65 +421,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 QMPShellBadPort:
- 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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 46/72] python/qmp: Fix type of SocketAddrT
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (44 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 45/72] scripts/qmp-shell: use argparse John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 47/72] python/qmp: add parse_address classmethod John Snow
` (25 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
This is an integer, not a string. We didn't catch this before because we
have not been type checking any of the clients that use this, and it was
self-consistent enough.
Mypy was not introspecting into the socket() interface to realize we
were passing a bad type. Fixed now.
Signed-off-by: John Snow <jsnow@redhat.com>
---
python/qemu/qmp/__init__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/python/qemu/qmp/__init__.py b/python/qemu/qmp/__init__.py
index 7355a558796f..dd211e3f6511 100644
--- a/python/qemu/qmp/__init__.py
+++ b/python/qemu/qmp/__init__.py
@@ -49,7 +49,7 @@
# {} is the QMPReturnValue.
-InternetAddrT = Tuple[str, str]
+InternetAddrT = Tuple[str, int]
UnixAddrT = str
SocketAddrT = Union[InternetAddrT, UnixAddrT]
--
2.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 47/72] python/qmp: add parse_address classmethod
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (45 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 46/72] python/qmp: Fix type of SocketAddrT John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 48/72] scripts/qmp-shell: Add pretty attribute to HMP shell John Snow
` (24 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
This takes the place of qmp-shell's __get_address function.
Signed-off-by: John Snow <jsnow@redhat.com>
---
python/qemu/qmp/__init__.py | 26 ++++++++++++++++++++++++++
scripts/qmp/qmp-shell | 27 ++-------------------------
2 files changed, 28 insertions(+), 25 deletions(-)
diff --git a/python/qemu/qmp/__init__.py b/python/qemu/qmp/__init__.py
index dd211e3f6511..a6e1a7b85775 100644
--- a/python/qemu/qmp/__init__.py
+++ b/python/qemu/qmp/__init__.py
@@ -97,6 +97,12 @@ def __init__(self, reply: QMPMessage):
self.reply = reply
+class QMPBadPortError(QMPError):
+ """
+ Unable to parse socket address: Port was non-numerical.
+ """
+
+
class QEMUMonitorProtocol:
"""
Provide an API to connect to QEMU via QEMU Monitor Protocol (QMP) and then
@@ -224,6 +230,26 @@ def __exit__(self,
# Implement context manager exit function.
self.close()
+ @classmethod
+ def parse_address(cls, address: str) -> SocketAddrT:
+ """
+ Parse a string into a QMP address.
+
+ Figure out if the argument is in the port:host form.
+ If it's not, it's probably a file path.
+ """
+ components = address.split(':')
+ if len(components) == 2:
+ try:
+ port = int(components[1])
+ except ValueError:
+ msg = f"Bad port: '{components[1]}' in '{address}'."
+ raise QMPBadPortError(msg) from None
+ return (components[0], port)
+
+ # Treat as filepath.
+ return address
+
def connect(self, negotiate: bool = True) -> Optional[QMPMessage]:
"""
Connect to the QMP Monitor and perform capabilities negotiation.
diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index d5496aeac0bd..5a72b9d39502 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -92,10 +92,6 @@ class QMPShellError(Exception):
pass
-class QMPShellBadPort(QMPShellError):
- pass
-
-
class FuzzyJSON(ast.NodeTransformer):
"""
This extension of ast.NodeTransformer filters literal "true/false/null"
@@ -118,7 +114,7 @@ class FuzzyJSON(ast.NodeTransformer):
# _execute_cmd()). Let's design a better one.
class QMPShell(qmp.QEMUMonitorProtocol):
def __init__(self, address, pretty=False):
- super().__init__(self.__get_address(address))
+ super().__init__(self.parse_address(address))
self._greeting = None
self._completer = None
self._pretty = pretty
@@ -128,22 +124,6 @@ class QMPShell(qmp.QEMUMonitorProtocol):
'.qmp-shell_history')
self._verbose = False
- @classmethod
- def __get_address(cls, arg):
- """
- Figure out if the argument is in the port:host form, if it's not it's
- probably a file path.
- """
- addr = arg.split(':')
- if len(addr) == 2:
- try:
- port = int(addr[1])
- except ValueError as err:
- raise QMPShellBadPort from err
- return addr[0], port
- # socket path
- return arg
-
def _fill_completion(self):
cmds = self.cmd('query-commands')
if 'error' in cmds:
@@ -337,9 +317,6 @@ class QMPShell(qmp.QEMUMonitorProtocol):
return self._execute_cmd(cmdline)
- def set_verbosity(self, verbose):
- self._verbose = verbose
-
class HMPShell(QMPShell):
def __init__(self, address):
@@ -447,7 +424,7 @@ def main():
qemu = HMPShell(args.qmp_server)
else:
qemu = QMPShell(args.qmp_server, args.pretty)
- except QMPShellBadPort:
+ except qmp.QMPBadPortError:
parser.error(f"Bad port number: {args.qmp_server}")
return # pycharm doesn't know error() is noreturn
--
2.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 48/72] scripts/qmp-shell: Add pretty attribute to HMP shell
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (46 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 47/72] python/qmp: add parse_address classmethod John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 49/72] scripts/qmp-shell: Make verbose a public attribute John Snow
` (23 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
It's less useful, but it makes the initialization methods LSP
consistent.
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 5a72b9d39502..ba27e9801c68 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -319,8 +319,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):
@@ -418,12 +418,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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 49/72] scripts/qmp-shell: Make verbose a public attribute
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (47 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 48/72] scripts/qmp-shell: Add pretty attribute to HMP shell John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 50/72] scripts/qmp-shell: move get_prompt() to prompt property John Snow
` (22 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: 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 | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index ba27e9801c68..cfcefb95f9da 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:
@@ -319,8 +319,8 @@ class QMPShell(qmp.QEMUMonitorProtocol):
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):
@@ -420,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
@@ -435,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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 50/72] scripts/qmp-shell: move get_prompt() to prompt property
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (48 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 49/72] scripts/qmp-shell: Make verbose a public attribute John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 51/72] scripts/qmp-shell: remove prompt argument from read_exec_command John Snow
` (21 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: 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 cfcefb95f9da..3b86ef7d8844 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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 51/72] scripts/qmp-shell: remove prompt argument from read_exec_command
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (49 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 50/72] scripts/qmp-shell: move get_prompt() to prompt property John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 52/72] scripts/qmp-shell: move the REPL functionality into QMPShell John Snow
` (20 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: 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 3b86ef7d8844..31269859c4c4 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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 52/72] scripts/qmp-shell: move the REPL functionality into QMPShell
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (50 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 51/72] scripts/qmp-shell: remove prompt argument from read_exec_command John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 53/72] scripts/qmp-shell: Fix "FuzzyJSON" parser John Snow
` (19 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
Instead of doing this in main, move it into the class.
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 31269859c4c4..aa148517a864 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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 53/72] scripts/qmp-shell: Fix "FuzzyJSON" parser
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (51 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 52/72] scripts/qmp-shell: move the REPL functionality into QMPShell John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 54/72] scripts/qmp-shell: refactor QMPCompleter John Snow
` (18 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: 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 aa148517a864..847d34890f97 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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 54/72] scripts/qmp-shell: refactor QMPCompleter
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (52 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 53/72] scripts/qmp-shell: Fix "FuzzyJSON" parser John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 55/72] scripts/qmp-shell: initialize completer early John Snow
` (17 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: 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 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 847d34890f97..73694035b203 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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 55/72] scripts/qmp-shell: initialize completer early
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (53 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 54/72] scripts/qmp-shell: refactor QMPCompleter John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 56/72] python/qmp: add QMPObject type alias John Snow
` (16 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: 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 73694035b203..670361322c51 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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 56/72] python/qmp: add QMPObject type alias
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (54 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 55/72] scripts/qmp-shell: initialize completer early John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 57/72] scripts/qmp-shell: add mypy types John Snow
` (15 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: 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 a6e1a7b85775..ba0d2281d678 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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 57/72] scripts/qmp-shell: add mypy types
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (55 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 56/72] python/qmp: add QMPObject type alias John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 58/72] scripts/qmp-shell: Accept SocketAddrT instead of string John Snow
` (14 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: 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 670361322c51..2d0e85b5f723 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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 58/72] scripts/qmp-shell: Accept SocketAddrT instead of string
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (56 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 57/72] scripts/qmp-shell: add mypy types John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 59/72] scripts/qmp-shell: unprivatize 'pretty' property John Snow
` (13 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
Don't extend QEMUMonitorProtocol but change the argument types; move the
string parsing just outside of the class.
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 2d0e85b5f723..b465c7f9e2d6 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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 59/72] scripts/qmp-shell: unprivatize 'pretty' property
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (57 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 58/72] scripts/qmp-shell: Accept SocketAddrT instead of string John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 60/72] python/qmp: return generic type from context manager John Snow
` (12 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: 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 b465c7f9e2d6..f14fe211cca4 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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 60/72] python/qmp: return generic type from context manager
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (58 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 59/72] scripts/qmp-shell: unprivatize 'pretty' property John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 61/72] scripts/qmp-shell: Use context manager instead of atexit John Snow
` (11 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
__enter__ can be invoked from a subclass, so it needs a more specific
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 ba0d2281d678..376954cb6d27 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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 61/72] scripts/qmp-shell: Use context manager instead of atexit
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (59 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 60/72] python/qmp: return generic type from context manager John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 62/72] scripts/qmp-shell: use logging to show warnings John Snow
` (10 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: 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 f14fe211cca4..ec028d662e8e 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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 62/72] scripts/qmp-shell: use logging to show warnings
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (60 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 61/72] scripts/qmp-shell: Use context manager instead of atexit John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 63/72] scripts/qmp-shell: remove TODO John Snow
` (9 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
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 ec028d662e8e..0199a13a3428 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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 63/72] scripts/qmp-shell: remove TODO
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (61 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 62/72] scripts/qmp-shell: use logging to show warnings John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 64/72] scripts/qmp-shell: Fix empty-transaction invocation John Snow
` (8 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: 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 0199a13a3428..3c32b576a37d 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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 64/72] scripts/qmp-shell: Fix empty-transaction invocation
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (62 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 63/72] scripts/qmp-shell: remove TODO John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 65/72] scripts/qmp-shell: Remove too-broad-exception John Snow
` (7 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: 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 3c32b576a37d..78e4eae00754 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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 65/72] scripts/qmp-shell: Remove too-broad-exception
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (63 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 64/72] scripts/qmp-shell: Fix empty-transaction invocation John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 66/72] scripts/qmp-shell: convert usage comment to docstring John Snow
` (6 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: 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 78e4eae00754..8d5845ab4815 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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 66/72] scripts/qmp-shell: convert usage comment to docstring
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (64 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 65/72] scripts/qmp-shell: Remove too-broad-exception John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 67/72] scripts/qmp-shell: remove double-underscores John Snow
` (5 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
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 8d5845ab4815..82fe16cff820 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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 67/72] scripts/qmp-shell: remove double-underscores
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (65 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 66/72] scripts/qmp-shell: convert usage comment to docstring John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 68/72] scripts/qmp-shell: make QMPShellError inherit QMPError John Snow
` (4 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: 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 82fe16cff820..40ff9e0a82bd 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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 68/72] scripts/qmp-shell: make QMPShellError inherit QMPError
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (66 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 67/72] scripts/qmp-shell: remove double-underscores John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:35 ` [PATCH v2 69/72] scripts/qmp-shell: add docstrings John Snow
` (3 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: Cleber Rosa, John Snow, Markus Armbruster, Eduardo Habkost
This way, all exceptions from the qemu.qmp namespace 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 40ff9e0a82bd..1a8a4ba18ab4 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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 69/72] scripts/qmp-shell: add docstrings
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (67 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 68/72] scripts/qmp-shell: make QMPShellError inherit QMPError John Snow
@ 2020-11-04 0:35 ` John Snow
2020-11-04 0:36 ` [PATCH v2 70/72] scripts/qmp-shell: move to python/qemu/qmp/qmp_shell.py John Snow
` (2 subsequent siblings)
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:35 UTC (permalink / raw)
To: qemu-devel; +Cc: 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 1a8a4ba18ab4..15aedb80c2af 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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 70/72] scripts/qmp-shell: move to python/qemu/qmp/qmp_shell.py
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (68 preceding siblings ...)
2020-11-04 0:35 ` [PATCH v2 69/72] scripts/qmp-shell: add docstrings John Snow
@ 2020-11-04 0:36 ` John Snow
2020-11-04 0:36 ` [PATCH v2 71/72] python: add qmp-shell entry point John Snow
2020-11-04 0:36 ` [PATCH v2 72/72] scripts/qmp-shell: add redirection shim John Snow
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:36 UTC (permalink / raw)
To: qemu-devel; +Cc: 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.
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 15aedb80c2af..337acfce2d26
--- 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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 71/72] python: add qmp-shell entry point
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (69 preceding siblings ...)
2020-11-04 0:36 ` [PATCH v2 70/72] scripts/qmp-shell: move to python/qemu/qmp/qmp_shell.py John Snow
@ 2020-11-04 0:36 ` John Snow
2020-11-04 0:36 ` [PATCH v2 72/72] scripts/qmp-shell: add redirection shim John Snow
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:36 UTC (permalink / raw)
To: qemu-devel; +Cc: 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 f2f54bcaefe8..230fd870ea91 100644
--- a/python/setup.cfg
+++ b/python/setup.cfg
@@ -42,6 +42,7 @@ console_scripts =
qom-tree = qemu.qmp.qom:QOMTree.entry_point
qom-fuse = qemu.qmp.qom_fuse:QOMFuse.entry_point
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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
* [PATCH v2 72/72] scripts/qmp-shell: add redirection shim
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
` (70 preceding siblings ...)
2020-11-04 0:36 ` [PATCH v2 71/72] python: add qmp-shell entry point John Snow
@ 2020-11-04 0:36 ` John Snow
71 siblings, 0 replies; 73+ messages in thread
From: John Snow @ 2020-11-04 0:36 UTC (permalink / raw)
To: qemu-devel; +Cc: 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 000000000000..4a20f97db708
--- /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.26.2
^ permalink raw reply related [flat|nested] 73+ messages in thread
end of thread, other threads:[~2020-11-04 1:32 UTC | newest]
Thread overview: 73+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-11-04 0:34 [PATCH v2 00/72] python: move scripts/qmp to python/qemu/qmp John Snow
2020-11-04 0:34 ` [PATCH v2 01/72] python/qmp: Add qom script rewrites John Snow
2020-11-04 0:34 ` [PATCH v2 02/72] python/qmp: add qom script entry points John Snow
2020-11-04 0:34 ` [PATCH v2 03/72] scripts/qmp: redirect qom-xxx scripts to python/qemu/qmp/ John Snow
2020-11-04 0:34 ` [PATCH v2 04/72] scripts/qom-fuse: apply isort rules John Snow
2020-11-04 0:34 ` [PATCH v2 05/72] scripts/qom-fuse: apply flake8 rules John Snow
2020-11-04 0:34 ` [PATCH v2 06/72] python: Add 'fh' to known-good variable names John Snow
2020-11-04 0:34 ` [PATCH v2 07/72] scripts/qom-fuse: Apply pylint rules John Snow
2020-11-04 0:34 ` [PATCH v2 08/72] scripts/qom-fuse: Add docstrings John Snow
2020-11-04 0:34 ` [PATCH v2 09/72] scripts/qom-fuse: Convert to QOMCommand John Snow
2020-11-04 0:35 ` [PATCH v2 10/72] scripts/qom-fuse: use QOMCommand.qom_list() John Snow
2020-11-04 0:35 ` [PATCH v2 11/72] scripts/qom-fuse: ensure QOMFuse.read always returns bytes John Snow
2020-11-04 0:35 ` [PATCH v2 12/72] scripts/qom-fuse: add static type hints John Snow
2020-11-04 0:35 ` [PATCH v2 13/72] scripts/qom-fuse: move to python/qemu/qmp/qom_fuse.py John Snow
2020-11-04 0:35 ` [PATCH v2 14/72] scripts/qom-fuse: add redirection shim to python/qemu/qmp/qom-fuse.py John Snow
2020-11-04 0:35 ` [PATCH v2 15/72] python: add fuse command to 'qom' tools John Snow
2020-11-04 0:35 ` [PATCH v2 16/72] python: add optional fuse dependency John Snow
2020-11-04 0:35 ` [PATCH v2 17/72] scripts/qemu-ga-client: apply isort rules John Snow
2020-11-04 0:35 ` [PATCH v2 18/72] scripts/qemu-ga-client: apply (most) flake8 rules John Snow
2020-11-04 0:35 ` [PATCH v2 19/72] scripts/qemu-ga-client: Fix exception handling John Snow
2020-11-04 0:35 ` [PATCH v2 20/72] scripts/qemu-ga-client: replace deprecated optparse with argparse John Snow
2020-11-04 0:35 ` [PATCH v2 21/72] scripts/qemu-ga-client: add module docstring John Snow
2020-11-04 0:35 ` [PATCH v2 22/72] scripts/qemu-ga-client: apply (most) pylint rules John Snow
2020-11-04 0:35 ` [PATCH v2 23/72] python/qmp: Correct type of QMPReturnValue John Snow
2020-11-04 0:35 ` [PATCH v2 24/72] scripts/qmp/qemu-ga-client: add mypy type hints John Snow
2020-11-04 0:35 ` [PATCH v2 25/72] scripts/qemu-ga-client: move to python/qemu/qmp/qemu_ga_client.py John Snow
2020-11-04 0:35 ` [PATCH v2 26/72] python/qemu-ga-client: add entry point John Snow
2020-11-04 0:35 ` [PATCH v2 27/72] scripts/qemu-ga-client: Add forwarder stub John Snow
2020-11-04 0:35 ` [PATCH v2 28/72] scripts/qmp-shell: apply isort rules John Snow
2020-11-04 0:35 ` [PATCH v2 29/72] scripts/qmp-shell: Apply flake8 rules John Snow
2020-11-04 0:35 ` [PATCH v2 30/72] scripts/qmp-shell: fix show_banner signature John Snow
2020-11-04 0:35 ` [PATCH v2 31/72] scripts/qmp-shell: fix exception handling John Snow
2020-11-04 0:35 ` [PATCH v2 32/72] scripts/qmp-shell: fix connect method signature John Snow
2020-11-04 0:35 ` [PATCH v2 33/72] scripts/qmp-shell: remove shadowed variable from _print() John Snow
2020-11-04 0:35 ` [PATCH v2 34/72] scripts/qmp-shell: use @classmethod where appropriate John Snow
2020-11-04 0:35 ` [PATCH v2 35/72] scripts/qmp-shell: Use python3-style super() John Snow
2020-11-04 0:35 ` [PATCH v2 36/72] scripts/qmp-shell: declare verbose in __init__ John Snow
2020-11-04 0:35 ` [PATCH v2 37/72] scripts/qmp-shell: use triple-double-quote docstring style John Snow
2020-11-04 0:35 ` [PATCH v2 38/72] scripts/qmp-shell: ignore visit_Name name John Snow
2020-11-04 0:35 ` [PATCH v2 39/72] scripts/qmp-shell: make QMPCompleter returns explicit John Snow
2020-11-04 0:35 ` [PATCH v2 40/72] scripts/qmp-shell: rename one and two-letter variables John Snow
2020-11-04 0:35 ` [PATCH v2 41/72] scripts/qmp-shell: fix shell history exception handling John Snow
2020-11-04 0:35 ` [PATCH v2 42/72] scripts/qmp-shell: explicitly chain exception context John Snow
2020-11-04 0:35 ` [PATCH v2 43/72] scripts/qmp-shell: remove if-raise-else patterns John Snow
2020-11-04 0:35 ` [PATCH v2 44/72] scripts/qmp-shell: use isinstance() instead of type() John Snow
2020-11-04 0:35 ` [PATCH v2 45/72] scripts/qmp-shell: use argparse John Snow
2020-11-04 0:35 ` [PATCH v2 46/72] python/qmp: Fix type of SocketAddrT John Snow
2020-11-04 0:35 ` [PATCH v2 47/72] python/qmp: add parse_address classmethod John Snow
2020-11-04 0:35 ` [PATCH v2 48/72] scripts/qmp-shell: Add pretty attribute to HMP shell John Snow
2020-11-04 0:35 ` [PATCH v2 49/72] scripts/qmp-shell: Make verbose a public attribute John Snow
2020-11-04 0:35 ` [PATCH v2 50/72] scripts/qmp-shell: move get_prompt() to prompt property John Snow
2020-11-04 0:35 ` [PATCH v2 51/72] scripts/qmp-shell: remove prompt argument from read_exec_command John Snow
2020-11-04 0:35 ` [PATCH v2 52/72] scripts/qmp-shell: move the REPL functionality into QMPShell John Snow
2020-11-04 0:35 ` [PATCH v2 53/72] scripts/qmp-shell: Fix "FuzzyJSON" parser John Snow
2020-11-04 0:35 ` [PATCH v2 54/72] scripts/qmp-shell: refactor QMPCompleter John Snow
2020-11-04 0:35 ` [PATCH v2 55/72] scripts/qmp-shell: initialize completer early John Snow
2020-11-04 0:35 ` [PATCH v2 56/72] python/qmp: add QMPObject type alias John Snow
2020-11-04 0:35 ` [PATCH v2 57/72] scripts/qmp-shell: add mypy types John Snow
2020-11-04 0:35 ` [PATCH v2 58/72] scripts/qmp-shell: Accept SocketAddrT instead of string John Snow
2020-11-04 0:35 ` [PATCH v2 59/72] scripts/qmp-shell: unprivatize 'pretty' property John Snow
2020-11-04 0:35 ` [PATCH v2 60/72] python/qmp: return generic type from context manager John Snow
2020-11-04 0:35 ` [PATCH v2 61/72] scripts/qmp-shell: Use context manager instead of atexit John Snow
2020-11-04 0:35 ` [PATCH v2 62/72] scripts/qmp-shell: use logging to show warnings John Snow
2020-11-04 0:35 ` [PATCH v2 63/72] scripts/qmp-shell: remove TODO John Snow
2020-11-04 0:35 ` [PATCH v2 64/72] scripts/qmp-shell: Fix empty-transaction invocation John Snow
2020-11-04 0:35 ` [PATCH v2 65/72] scripts/qmp-shell: Remove too-broad-exception John Snow
2020-11-04 0:35 ` [PATCH v2 66/72] scripts/qmp-shell: convert usage comment to docstring John Snow
2020-11-04 0:35 ` [PATCH v2 67/72] scripts/qmp-shell: remove double-underscores John Snow
2020-11-04 0:35 ` [PATCH v2 68/72] scripts/qmp-shell: make QMPShellError inherit QMPError John Snow
2020-11-04 0:35 ` [PATCH v2 69/72] scripts/qmp-shell: add docstrings John Snow
2020-11-04 0:36 ` [PATCH v2 70/72] scripts/qmp-shell: move to python/qemu/qmp/qmp_shell.py John Snow
2020-11-04 0:36 ` [PATCH v2 71/72] python: add qmp-shell entry point John Snow
2020-11-04 0:36 ` [PATCH v2 72/72] scripts/qmp-shell: add redirection shim 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.