[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH v2 23/25] python: remove the old QMP package
From: |
Beraldo Leal |
Subject: |
Re: [PATCH v2 23/25] python: remove the old QMP package |
Date: |
Thu, 16 Dec 2021 11:17:54 -0300 |
On Wed, Dec 15, 2021 at 02:39:37PM -0500, John Snow wrote:
> Thank you for your service!
>
> Signed-off-by: John Snow <jsnow@redhat.com>
> ---
> python/PACKAGE.rst | 4 +-
> python/README.rst | 2 +-
> python/qemu/qmp/README.rst | 9 -
> python/qemu/qmp/__init__.py | 396 ------------------------------------
> python/qemu/qmp/py.typed | 0
> python/setup.cfg | 3 +-
> 6 files changed, 4 insertions(+), 410 deletions(-)
> delete mode 100644 python/qemu/qmp/README.rst
> delete mode 100644 python/qemu/qmp/__init__.py
> delete mode 100644 python/qemu/qmp/py.typed
>
> diff --git a/python/PACKAGE.rst b/python/PACKAGE.rst
> index b0b86cc4c3..ddfa9ba3f5 100644
> --- a/python/PACKAGE.rst
> +++ b/python/PACKAGE.rst
> @@ -8,11 +8,11 @@ to change at any time.
> Usage
> -----
>
> -The ``qemu.qmp`` subpackage provides a library for communicating with
> +The ``qemu.aqmp`` subpackage provides a library for communicating with
> QMP servers. The ``qemu.machine`` subpackage offers rudimentary
> facilities for launching and managing QEMU processes. Refer to each
> package's documentation
> -(``>>> help(qemu.qmp)``, ``>>> help(qemu.machine)``)
> +(``>>> help(qemu.aqmp)``, ``>>> help(qemu.machine)``)
> for more information.
>
> Contributing
> diff --git a/python/README.rst b/python/README.rst
> index fcf74f69ea..eb5213337d 100644
> --- a/python/README.rst
> +++ b/python/README.rst
> @@ -3,7 +3,7 @@ QEMU Python Tooling
>
> This directory houses Python tooling used by the QEMU project to build,
> configure, and test QEMU. It is organized by namespace (``qemu``), and
> -then by package (e.g. ``qemu/machine``, ``qemu/qmp``, etc).
> +then by package (e.g. ``qemu/machine``, ``qemu/aqmp``, etc).
>
> ``setup.py`` is used by ``pip`` to install this tooling to the current
> environment. ``setup.cfg`` provides the packaging configuration used by
> diff --git a/python/qemu/qmp/README.rst b/python/qemu/qmp/README.rst
> deleted file mode 100644
> index 5bfb82535f..0000000000
> --- a/python/qemu/qmp/README.rst
> +++ /dev/null
> @@ -1,9 +0,0 @@
> -qemu.qmp package
> -================
> -
> -This package provides a library used for connecting to and communicating
> -with QMP servers. It is used extensively by iotests, vm tests,
> -avocado tests, and other utilities in the ./scripts directory. It is
> -not a fully-fledged SDK and is subject to change at any time.
> -
> -See the documentation in ``__init__.py`` for more information.
> diff --git a/python/qemu/qmp/__init__.py b/python/qemu/qmp/__init__.py
> deleted file mode 100644
> index 4e08641154..0000000000
> --- a/python/qemu/qmp/__init__.py
> +++ /dev/null
> @@ -1,396 +0,0 @@
> -"""
> -QEMU Monitor Protocol (QMP) development library & tooling.
> -
> -This package provides a fairly low-level class for communicating to QMP
> -protocol servers, as implemented by QEMU, the QEMU Guest Agent, and the
> -QEMU Storage Daemon. This library is not intended for production use.
> -
> -`QEMUMonitorProtocol` is the primary class of interest, and all errors
> -raised derive from `QMPError`.
> -"""
> -
> -# Copyright (C) 2009, 2010 Red Hat Inc.
> -#
> -# Authors:
> -# Luiz Capitulino <lcapitulino@redhat.com>
> -#
> -# This work is licensed under the terms of the GNU GPL, version 2. See
> -# the COPYING file in the top-level directory.
> -
> -import errno
> -import json
> -import logging
> -import socket
> -import struct
> -from types import TracebackType
> -from typing import (
> - Any,
> - Dict,
> - List,
> - Optional,
> - TextIO,
> - Tuple,
> - Type,
> - TypeVar,
> - Union,
> - cast,
> -)
> -
> -
> -#: QMPMessage is an entire QMP message of any kind.
> -QMPMessage = Dict[str, Any]
> -
> -#: 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.
> -#
> -# {'return': {}} is a QMPMessage,
> -# {} is the QMPReturnValue.
> -
> -
> -InternetAddrT = Tuple[str, int]
> -UnixAddrT = str
> -SocketAddrT = Union[InternetAddrT, UnixAddrT]
> -
> -
> -class QMPError(Exception):
> - """
> - QMP base exception
> - """
> -
> -
> -class QMPConnectError(QMPError):
> - """
> - QMP connection exception
> - """
> -
> -
> -class QMPCapabilitiesError(QMPError):
> - """
> - QMP negotiate capabilities exception
> - """
> -
> -
> -class QMPTimeoutError(QMPError):
> - """
> - QMP timeout exception
> - """
> -
> -
> -class QMPProtocolError(QMPError):
> - """
> - QMP protocol error; unexpected response
> - """
> -
> -
> -class QMPResponseError(QMPError):
> - """
> - Represents erroneous QMP monitor reply
> - """
> - def __init__(self, reply: QMPMessage):
> - try:
> - desc = reply['error']['desc']
> - except KeyError:
> - desc = reply
> - super().__init__(desc)
> - self.reply = reply
> -
> -
> -class QEMUMonitorProtocol:
> - """
> - Provide an API to connect to QEMU via QEMU Monitor Protocol (QMP) and
> then
> - allow to handle commands and events.
> - """
> -
> - #: Logger object for debugging messages
> - logger = logging.getLogger('QMP')
> -
> - def __init__(self, address: SocketAddrT,
> - server: bool = False,
> - nickname: Optional[str] = None):
> - """
> - Create a QEMUMonitorProtocol class.
> -
> - @param address: QEMU address, can be either a unix socket path
> (string)
> - or a tuple in the form ( address, port ) for a TCP
> - connection
> - @param server: server mode listens on the socket (bool)
> - @raise OSError on socket connection errors
> - @note No connection is established, this is done by the connect() or
> - accept() methods
> - """
> - self.__events: List[QMPMessage] = []
> - self.__address = address
> - self.__sock = self.__get_sock()
> - self.__sockfile: Optional[TextIO] = None
> - self._nickname = nickname
> - if self._nickname:
> - self.logger = logging.getLogger('QMP').getChild(self._nickname)
> - if server:
> - self.__sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
> - self.__sock.bind(self.__address)
> - self.__sock.listen(1)
> -
> - def __get_sock(self) -> socket.socket:
> - if isinstance(self.__address, tuple):
> - family = socket.AF_INET
> - else:
> - family = socket.AF_UNIX
> - return socket.socket(family, socket.SOCK_STREAM)
> -
> - def __negotiate_capabilities(self) -> QMPMessage:
> - greeting = self.__json_read()
> - if greeting is None or "QMP" not in greeting:
> - raise QMPConnectError
> - # Greeting seems ok, negotiate capabilities
> - resp = self.cmd('qmp_capabilities')
> - if resp and "return" in resp:
> - return greeting
> - raise QMPCapabilitiesError
> -
> - def __json_read(self, only_event: bool = False) -> Optional[QMPMessage]:
> - assert self.__sockfile is not None
> - while True:
> - data = self.__sockfile.readline()
> - if not data:
> - return None
> - # By definition, any JSON received from QMP is a QMPMessage,
> - # and we are asserting only at static analysis time that it
> - # has a particular shape.
> - resp: QMPMessage = json.loads(data)
> - if 'event' in resp:
> - self.logger.debug("<<< %s", resp)
> - self.__events.append(resp)
> - if not only_event:
> - continue
> - return resp
> -
> - def __get_events(self, wait: Union[bool, float] = False) -> None:
> - """
> - Check for new events in the stream and cache them in __events.
> -
> - @param wait (bool): block until an event is available.
> - @param wait (float): If wait is a float, treat it as a timeout value.
> -
> - @raise QMPTimeoutError: If a timeout float is provided and the
> timeout
> - period elapses.
> - @raise QMPConnectError: If wait is True but no events could be
> - retrieved or if some other error occurred.
> - """
> -
> - # Current timeout and blocking status
> - current_timeout = self.__sock.gettimeout()
> -
> - # Check for new events regardless and pull them into the cache:
> - self.__sock.settimeout(0) # i.e. setblocking(False)
> - try:
> - self.__json_read()
> - except OSError as err:
> - # EAGAIN: No data available; not critical
> - if err.errno != errno.EAGAIN:
> - raise
> - finally:
> - self.__sock.settimeout(current_timeout)
> -
> - # Wait for new events, if needed.
> - # if wait is 0.0, this means "no wait" and is also implicitly false.
> - if not self.__events and wait:
> - if isinstance(wait, float):
> - self.__sock.settimeout(wait)
> - try:
> - ret = self.__json_read(only_event=True)
> - except socket.timeout as err:
> - raise QMPTimeoutError("Timeout waiting for event") from err
> - except Exception as err:
> - msg = "Error while reading from socket"
> - raise QMPConnectError(msg) from err
> - finally:
> - self.__sock.settimeout(current_timeout)
> -
> - if ret is None:
> - raise QMPConnectError("Error while reading from socket")
> -
> - T = TypeVar('T')
> -
> - def __enter__(self: T) -> T:
> - # Implement context manager enter function.
> - return self
> -
> - def __exit__(self,
> - # pylint: disable=duplicate-code
> - # see https://github.com/PyCQA/pylint/issues/3619
> - exc_type: Optional[Type[BaseException]],
> - exc_val: Optional[BaseException],
> - exc_tb: Optional[TracebackType]) -> None:
> - # Implement context manager exit function.
> - self.close()
> -
> - def connect(self, negotiate: bool = True) -> Optional[QMPMessage]:
> - """
> - Connect to the QMP Monitor and perform capabilities negotiation.
> -
> - @return QMP greeting dict, or None if negotiate is false
> - @raise OSError on socket connection errors
> - @raise QMPConnectError if the greeting is not received
> - @raise QMPCapabilitiesError if fails to negotiate capabilities
> - """
> - self.__sock.connect(self.__address)
> - self.__sockfile = self.__sock.makefile(mode='r')
> - if negotiate:
> - return self.__negotiate_capabilities()
> - return None
> -
> - def accept(self, timeout: Optional[float] = 15.0) -> QMPMessage:
> - """
> - Await connection from QMP Monitor and perform capabilities
> negotiation.
> -
> - @param timeout: timeout in seconds (nonnegative float number, or
> - None). The value passed will set the behavior of the
> - underneath QMP socket as described in [1].
> - Default value is set to 15.0.
> -
> - @return QMP greeting dict
> - @raise OSError on socket connection errors
> - @raise QMPConnectError if the greeting is not received
> - @raise QMPCapabilitiesError if fails to negotiate capabilities
> -
> - [1]
> -
> https://docs.python.org/3/library/socket.html#socket.socket.settimeout
> - """
> - self.__sock.settimeout(timeout)
> - self.__sock, _ = self.__sock.accept()
> - self.__sockfile = self.__sock.makefile(mode='r')
> - return self.__negotiate_capabilities()
> -
> - def cmd_obj(self, qmp_cmd: QMPMessage) -> QMPMessage:
> - """
> - Send a QMP command to the QMP Monitor.
> -
> - @param qmp_cmd: QMP command to be sent as a Python dict
> - @return QMP response as a Python dict
> - """
> - self.logger.debug(">>> %s", qmp_cmd)
> - self.__sock.sendall(json.dumps(qmp_cmd).encode('utf-8'))
> - resp = self.__json_read()
> - if resp is None:
> - raise QMPConnectError("Unexpected empty reply from server")
> - self.logger.debug("<<< %s", resp)
> - return resp
> -
> - def cmd(self, name: str,
> - args: Optional[Dict[str, object]] = None,
> - cmd_id: Optional[object] = None) -> QMPMessage:
> - """
> - Build a QMP command and send it to the QMP Monitor.
> -
> - @param name: command name (string)
> - @param args: command arguments (dict)
> - @param cmd_id: command id (dict, list, string or int)
> - """
> - qmp_cmd: QMPMessage = {'execute': name}
> - if args:
> - qmp_cmd['arguments'] = args
> - if cmd_id:
> - qmp_cmd['id'] = cmd_id
> - return self.cmd_obj(qmp_cmd)
> -
> - def command(self, cmd: str, **kwds: object) -> QMPReturnValue:
> - """
> - Build and send a QMP command to the monitor, report errors if any
> - """
> - ret = self.cmd(cmd, kwds)
> - if 'error' in ret:
> - raise QMPResponseError(ret)
> - if 'return' not in ret:
> - raise QMPProtocolError(
> - "'return' key not found in QMP response
> '{}'".format(str(ret))
> - )
> - return cast(QMPReturnValue, ret['return'])
> -
> - def pull_event(self,
> - wait: Union[bool, float] = False) -> Optional[QMPMessage]:
> - """
> - Pulls a single event.
> -
> - @param wait (bool): block until an event is available.
> - @param wait (float): If wait is a float, treat it as a timeout value.
> -
> - @raise QMPTimeoutError: If a timeout float is provided and the
> timeout
> - period elapses.
> - @raise QMPConnectError: If wait is True but no events could be
> - retrieved or if some other error occurred.
> -
> - @return The first available QMP event, or None.
> - """
> - self.__get_events(wait)
> -
> - if self.__events:
> - return self.__events.pop(0)
> - return None
> -
> - def get_events(self, wait: bool = False) -> List[QMPMessage]:
> - """
> - Get a list of available QMP events and clear all pending events.
> -
> - @param wait (bool): block until an event is available.
> - @param wait (float): If wait is a float, treat it as a timeout value.
> -
> - @raise QMPTimeoutError: If a timeout float is provided and the
> timeout
> - period elapses.
> - @raise QMPConnectError: If wait is True but no events could be
> - retrieved or if some other error occurred.
> -
> - @return The list of available QMP events.
> - """
> - self.__get_events(wait)
> - events = self.__events
> - self.__events = []
> - return events
> -
> - def clear_events(self) -> None:
> - """
> - Clear current list of pending events.
> - """
> - self.__events = []
> -
> - def close(self) -> None:
> - """
> - Close the socket and socket file.
> - """
> - if self.__sock:
> - self.__sock.close()
> - if self.__sockfile:
> - self.__sockfile.close()
> -
> - def settimeout(self, timeout: Optional[float]) -> None:
> - """
> - Set the socket timeout.
> -
> - @param timeout (float): timeout in seconds (non-zero), or None.
> - @note This is a wrap around socket.settimeout
> -
> - @raise ValueError: if timeout was set to 0.
> - """
> - if timeout == 0:
> - msg = "timeout cannot be 0; this engages non-blocking mode."
> - msg += " Use 'None' instead to disable timeouts."
> - raise ValueError(msg)
> - self.__sock.settimeout(timeout)
> -
> - def send_fd_scm(self, fd: int) -> None:
> - """
> - Send a file descriptor to the remote via SCM_RIGHTS.
> - """
> - if self.__sock.family != socket.AF_UNIX:
> - raise RuntimeError("Can't use SCM_RIGHTS on non-AF_UNIX socket.")
> -
> - self.__sock.sendmsg(
> - [b' '],
> - [(socket.SOL_SOCKET, socket.SCM_RIGHTS, struct.pack('@i', fd))]
> - )
> diff --git a/python/qemu/qmp/py.typed b/python/qemu/qmp/py.typed
> deleted file mode 100644
> index e69de29bb2..0000000000
> diff --git a/python/setup.cfg b/python/setup.cfg
> index 510df23698..5140a5b322 100644
> --- a/python/setup.cfg
> +++ b/python/setup.cfg
> @@ -24,10 +24,9 @@ classifiers =
> [options]
> python_requires = >= 3.6
> packages =
> - qemu.qmp
> + qemu.aqmp
> qemu.machine
> qemu.utils
> - qemu.aqmp
>
> [options.package_data]
> * = py.typed
Reviewed-by: Beraldo Leal <bleal@redhat.com>
--
Beraldo
- [PATCH v2 25/25] python: rename qemu.aqmp to qemu.qmp, (continued)
[PATCH v2 24/25] python: re-enable pylint duplicate-code warnings, John Snow, 2021/12/15
[PATCH v2 23/25] python: remove the old QMP package, John Snow, 2021/12/15
[PATCH v2 22/25] python/aqmp: copy qmp docstrings to qemu.aqmp.legacy, John Snow, 2021/12/15
Re: [PATCH v2 00/25] Python: delete synchronous qemu.qmp package, Daniel P . Berrangé, 2021/12/16