From 7c4c595f13fa9bb19d8b6a1ed328aab5469c063a Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 2 Jun 2021 20:37:05 -0400 Subject: python/qmp: add qom script entry points 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. (e.g. when inside of a venv, `cd python && pip install .` will add 'qom', 'qom-set', etc to your $PATH.) Signed-off-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Message-id: 20210603003719.1321369-6-jsnow@redhat.com Signed-off-by: John Snow --- python/setup.cfg | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'python/setup.cfg') diff --git a/python/setup.cfg b/python/setup.cfg index 0fcdec6f32..a19029d538 100644 --- a/python/setup.cfg +++ b/python/setup.cfg @@ -37,6 +37,14 @@ devel = pylint >= 2.8.0 tox >= 3.18.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 exclude = __pycache__, -- cgit v1.2.3-55-g7522 From d229f1c83d698ed5f605bcc2eab96e05afeddefb Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 2 Jun 2021 20:37:09 -0400 Subject: python: Add 'fh' to known-good variable names 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 Message-id: 20210603003719.1321369-10-jsnow@redhat.com Signed-off-by: John Snow --- python/setup.cfg | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'python/setup.cfg') diff --git a/python/setup.cfg b/python/setup.cfg index a19029d538..c9b9445af9 100644 --- a/python/setup.cfg +++ b/python/setup.cfg @@ -78,9 +78,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. -- cgit v1.2.3-55-g7522 From 30ec845c599acc0616a57811316bc506a08344f2 Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 2 Jun 2021 20:37:15 -0400 Subject: scripts/qom-fuse: add static type hints Because fusepy does not have type hints, add some targeted warning suppressions. Namely, we need to allow subclassing something of an unknown type (in qom_fuse.py), and we need to allow missing imports (recorded against fuse itself) because mypy will be unable to import fusepy (even when installed) as it has no types nor type stubs available. Note: Until now, it was possible to run invocations like 'mypy qemu/' from ./python and have that work. However, these targeted suppressions require that you run 'mypy -p qemu/' instead. The correct, canonical invocation is recorded in ./python/tests/mypy.sh and all of the various CI invocations always use this correct form. Signed-off-by: John Snow Message-id: 20210603003719.1321369-16-jsnow@redhat.com Signed-off-by: John Snow --- python/setup.cfg | 8 ++++++++ scripts/qmp/qom-fuse | 26 +++++++++++++++++--------- 2 files changed, 25 insertions(+), 9 deletions(-) (limited to 'python/setup.cfg') diff --git a/python/setup.cfg b/python/setup.cfg index c9b9445af9..ba8d29fd62 100644 --- a/python/setup.cfg +++ b/python/setup.cfg @@ -57,6 +57,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 0d11f73152..a5a7a304a3 100755 --- a/scripts/qmp/qom-fuse +++ b/scripts/qmp/qom-fuse @@ -38,7 +38,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 @@ -83,7 +90,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] @@ -91,7 +98,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) @@ -99,7 +106,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 == '': @@ -112,7 +119,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 == '': @@ -125,7 +132,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) @@ -143,7 +150,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) @@ -151,7 +158,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, @@ -195,7 +203,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): -- cgit v1.2.3-55-g7522 From c63f3b0b29f8320e77a83acd9d055e6607f02f85 Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 2 Jun 2021 20:37:16 -0400 Subject: python: add optional FUSE dependencies In preparation for moving qom-fuse over to the python package, we need some new dependencies to support it. Add an optional 'fusepy' dependency that users of the package can opt into with e.g. "pip install qemu[fuse]" which installs the requirements necessary to obtain the additional functionality. Add the same fusepy dependency to the 'devel' extras group -- unfortunately I do not see a way for optional groups to imply other optional groups at present, so the dependency is repeated. The development group needs to include the full set of dependencies for the purpose of static analysis of all features offered by this library. Lastly, add the [fuse] extras group to tox's configuration as a workaround so that if a stale tox environment is found when running `make check-tox`, tox will know to rebuild its environments. Signed-off-by: John Snow Message-id: 20210603003719.1321369-17-jsnow@redhat.com Signed-off-by: John Snow --- python/Pipfile.lock | 6 ++++++ python/setup.cfg | 9 ++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'python/setup.cfg') diff --git a/python/Pipfile.lock b/python/Pipfile.lock index f2a3f91d0f..5bb3f1b635 100644 --- a/python/Pipfile.lock +++ b/python/Pipfile.lock @@ -67,6 +67,12 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==3.9.2" }, + "fusepy": { + "hashes": [ + "sha256:72ff783ec2f43de3ab394e3f7457605bf04c8cf288a2f4068b4cde141d4ee6bd" + ], + "version": "==3.0.1" + }, "importlib-metadata": { "hashes": [ "sha256:8c501196e49fb9df5df43833bdb1e4328f64847763ec8a50703148b73784d581", diff --git a/python/setup.cfg b/python/setup.cfg index ba8d29fd62..aca6f31185 100644 --- a/python/setup.cfg +++ b/python/setup.cfg @@ -32,11 +32,16 @@ packages = devel = avocado-framework >= 87.0 flake8 >= 3.6.0 + fusepy >= 2.0.4 isort >= 5.1.2 mypy >= 0.770 pylint >= 2.8.0 tox >= 3.18.0 +# Provides qom-fuse functionality +fuse = + fusepy >= 2.0.4 + [options.entry_points] console_scripts = qom = qemu.qmp.qom:main @@ -114,6 +119,8 @@ envlist = py36, py37, py38, py39, py310 [testenv] allowlist_externals = make -deps = .[devel] +deps = + .[devel] + .[fuse] # Workaround to trigger tox venv rebuild commands = make check -- cgit v1.2.3-55-g7522 From 176c5490724b813704bd5b083b9f5c9b857fdaad Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 2 Jun 2021 20:37:19 -0400 Subject: python/qmp: add fuse command to 'qom' tools The 'fuse' command will be unavailable if 'fusepy' is not installed. It will simply not load and subsequently be unavailable as a subcommand. Signed-off-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Message-id: 20210603003719.1321369-20-jsnow@redhat.com Signed-off-by: John Snow --- python/qemu/qmp/qom.py | 14 ++++++++++++-- python/setup.cfg | 1 + 2 files changed, 13 insertions(+), 2 deletions(-) (limited to 'python/setup.cfg') diff --git a/python/qemu/qmp/qom.py b/python/qemu/qmp/qom.py index 7fe1448b5d..7ec7843d57 100644 --- a/python/qemu/qmp/qom.py +++ b/python/qemu/qmp/qom.py @@ -1,7 +1,7 @@ """ QEMU Object Model testing tools. -usage: qom [-h] {set,get,list,tree} ... +usage: qom [-h] {set,get,list,tree,fuse} ... Query and manipulate QOM data @@ -9,11 +9,12 @@ optional arguments: -h, --help show this help message and exit QOM commands: - {set,get,list,tree} + {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 + fuse Mount a QOM tree as a FUSE filesystem """ ## # Copyright John Snow 2020, for Red Hat, Inc. @@ -35,6 +36,15 @@ from . import QMPResponseError from .qom_common import QOMCommand +try: + from .qom_fuse import QOMFuse +except ModuleNotFoundError as err: + if err.name != 'fuse': + raise +else: + assert issubclass(QOMFuse, QOMCommand) + + class QOMSet(QOMCommand): """ QOM Command - Set a property to a given value. diff --git a/python/setup.cfg b/python/setup.cfg index aca6f31185..6b6be8b03c 100644 --- a/python/setup.cfg +++ b/python/setup.cfg @@ -49,6 +49,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 [fuse] [flake8] extend-ignore = E722 # Prefer pylint's bare-except checks to flake8's -- cgit v1.2.3-55-g7522 From 7e7c2a0de74d56c3f47205df3dcf68ed7d43d30d Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 4 Jun 2021 11:55:31 -0400 Subject: python/qemu-ga-client: add entry point Remove the shebang, and add a package-defined entry point instead. Now, it can be accessed using 'qemu-ga-client' from the command line after installing the package. The next commit adds a forwarder shim that allows the running of this script without needing to install the package again. Signed-off-by: John Snow Message-id: 20210604155532.1499282-11-jsnow@redhat.com Signed-off-by: John Snow --- 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 (limited to 'python/setup.cfg') 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 d2938ad47c..67ac0b4211 --- a/python/qemu/qmp/qemu_ga_client.py +++ b/python/qemu/qmp/qemu_ga_client.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 - """ QEMU Guest Agent Client diff --git a/python/setup.cfg b/python/setup.cfg index 6b6be8b03c..7f3c59d74e 100644 --- a/python/setup.cfg +++ b/python/setup.cfg @@ -50,6 +50,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 [fuse] + qemu-ga-client = qemu.qmp.qemu_ga_client:main [flake8] extend-ignore = E722 # Prefer pylint's bare-except checks to flake8's -- cgit v1.2.3-55-g7522 From 957f3c5cee0ffd706cfabf4cf0e6b1247615fc59 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 7 Jun 2021 16:06:48 -0400 Subject: python: add qmp-shell entry point now 'qmp-shell' should be available from the command line when installing the python package. Signed-off-by: John Snow Message-id: 20210607200649.1840382-42-jsnow@redhat.com Signed-off-by: John Snow --- python/setup.cfg | 1 + 1 file changed, 1 insertion(+) (limited to 'python/setup.cfg') diff --git a/python/setup.cfg b/python/setup.cfg index 7f3c59d74e..85cecbb41b 100644 --- a/python/setup.cfg +++ b/python/setup.cfg @@ -51,6 +51,7 @@ console_scripts = qom-tree = qemu.qmp.qom:QOMTree.entry_point qom-fuse = qemu.qmp.qom_fuse:QOMFuse.entry_point [fuse] qemu-ga-client = qemu.qmp.qemu_ga_client:main + qmp-shell = qemu.qmp.qmp_shell:main [flake8] extend-ignore = E722 # Prefer pylint's bare-except checks to flake8's -- cgit v1.2.3-55-g7522