From patchwork Fri Jun 26 20:41:18 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Snow X-Patchwork-Id: 279259 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.8 required=3.0 tests=DKIM_INVALID,DKIM_SIGNED, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id ECE3DC433E1 for ; Fri, 26 Jun 2020 20:43:06 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id BC47A204EC for ; Fri, 26 Jun 2020 20:43:06 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="ZEe/F850" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org BC47A204EC Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:60336 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jovBh-0000Az-W1 for qemu-devel@archiver.kernel.org; Fri, 26 Jun 2020 16:43:06 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:33722) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1jovAR-0006ek-5i for qemu-devel@nongnu.org; Fri, 26 Jun 2020 16:41:47 -0400 Received: from us-smtp-2.mimecast.com ([207.211.31.81]:42796 helo=us-smtp-delivery-1.mimecast.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.90_1) (envelope-from ) id 1jovAK-0005nf-Ll for qemu-devel@nongnu.org; Fri, 26 Jun 2020 16:41:46 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1593204099; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=sStIApWiVtCBKiexrQL7OTQwVFLoTBgkZqfp2zgj6T8=; b=ZEe/F850x2tROtX9E8/A1cYTW1NA8RWY8nmU288GHpW0NldTy0XUGSZh1ycE4JiKypGa9w rrrqnTKTu0MfxTMuVhnkE+pSCj296Wg8NebEc95mujaPaf7l+BP7q8tfrqVxOF3dCX2mje xT8EqKH/+1QmdhrPEly3MRXAon8HjoM= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-151-tiQYQdTEMMCL4YgZb1qRhw-1; Fri, 26 Jun 2020 16:41:37 -0400 X-MC-Unique: tiQYQdTEMMCL4YgZb1qRhw-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id CEAD7BFC1; Fri, 26 Jun 2020 20:41:36 +0000 (UTC) Received: from probe.redhat.com (ovpn-119-184.rdu2.redhat.com [10.10.119.184]) by smtp.corp.redhat.com (Postfix) with ESMTP id B9B6F19C58; Fri, 26 Jun 2020 20:41:35 +0000 (UTC) From: John Snow To: qemu-devel@nongnu.org Subject: [PATCH v4 01/16] python/qmp.py: Define common types Date: Fri, 26 Jun 2020 16:41:18 -0400 Message-Id: <20200626204133.14500-2-jsnow@redhat.com> In-Reply-To: <20200626204133.14500-1-jsnow@redhat.com> References: <20200626204133.14500-1-jsnow@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Received-SPF: pass client-ip=207.211.31.81; envelope-from=jsnow@redhat.com; helo=us-smtp-delivery-1.mimecast.com X-detected-operating-system: by eggs.gnu.org: First seen = 2020/06/26 03:23:21 X-ACL-Warn: Detected OS = Linux 2.2.x-3.x [generic] [fuzzy] X-Spam_score_int: -30 X-Spam_score: -3.1 X-Spam_bar: --- X-Spam_report: (-3.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-1, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, URIBL_BLOCKED=0.001 autolearn=_AUTOLEARN X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Kevin Wolf , Eduardo Habkost , qemu-block@nongnu.org, John Snow , Max Reitz , Cleber Rosa , philmd@redhat.com Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" Define some common types that we'll need to annotate a lot of other functions going forward. Signed-off-by: John Snow Reviewed-by: Kevin Wolf --- python/qemu/qmp.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/python/qemu/qmp.py b/python/qemu/qmp.py index e64b6b5faa..8388c7b603 100644 --- a/python/qemu/qmp.py +++ b/python/qemu/qmp.py @@ -12,13 +12,31 @@ import socket import logging from typing import ( + Any, + Dict, Optional, TextIO, Type, + Tuple, + Union, ) from types import TracebackType +# 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 = Dict[str, Any] +QMPReturnValue = Dict[str, Any] + +InternetAddrT = Tuple[str, str] +UnixAddrT = str +SocketAddrT = Union[InternetAddrT, UnixAddrT] + + class QMPError(Exception): """ QMP base exception From patchwork Fri Jun 26 20:41:20 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: John Snow X-Patchwork-Id: 279258 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.8 required=3.0 tests=DKIM_INVALID,DKIM_SIGNED, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 15954C433DF for ; Fri, 26 Jun 2020 20:45:10 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id D1F59204EC for ; Fri, 26 Jun 2020 20:45:09 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="HiNNA90B" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org D1F59204EC Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:40230 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jovDh-0003V5-29 for qemu-devel@archiver.kernel.org; Fri, 26 Jun 2020 16:45:09 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:33712) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1jovAQ-0006dX-J8 for qemu-devel@nongnu.org; Fri, 26 Jun 2020 16:41:46 -0400 Received: from us-smtp-2.mimecast.com ([207.211.31.81]:53094 helo=us-smtp-delivery-1.mimecast.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.90_1) (envelope-from ) id 1jovAO-0005oj-38 for qemu-devel@nongnu.org; Fri, 26 Jun 2020 16:41:46 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1593204103; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Ebpl/s2od5p2nkJdsUmlCGv8AXKiXwIj/vwcNQbJWCY=; b=HiNNA90BS1gnjfAaE1bj2Odarh42XofxZAUwSOfP961HCN5MXXVTRYOneLt2+DnCrImgW4 O0Vzdt+KFmXMUBrYhpQ8LpyHw/13pAX+MlVfqtGJHaVsjuqdnWSJ/FOqpYivNYwzsUKBOj ojdbsETRq0DRe8NiLMdiNAlVV6fluU4= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-119-pcwcTblwMNuiAQVqm6OCcQ-1; Fri, 26 Jun 2020 16:41:39 -0400 X-MC-Unique: pcwcTblwMNuiAQVqm6OCcQ-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id C932C107ACCA; Fri, 26 Jun 2020 20:41:38 +0000 (UTC) Received: from probe.redhat.com (ovpn-119-184.rdu2.redhat.com [10.10.119.184]) by smtp.corp.redhat.com (Postfix) with ESMTP id EF4E219C58; Fri, 26 Jun 2020 20:41:37 +0000 (UTC) From: John Snow To: qemu-devel@nongnu.org Subject: [PATCH v4 03/16] python/qmp.py: re-absorb MonitorResponseError Date: Fri, 26 Jun 2020 16:41:20 -0400 Message-Id: <20200626204133.14500-4-jsnow@redhat.com> In-Reply-To: <20200626204133.14500-1-jsnow@redhat.com> References: <20200626204133.14500-1-jsnow@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=jsnow@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Received-SPF: pass client-ip=207.211.31.81; envelope-from=jsnow@redhat.com; helo=us-smtp-delivery-1.mimecast.com X-detected-operating-system: by eggs.gnu.org: First seen = 2020/06/26 03:23:21 X-ACL-Warn: Detected OS = Linux 2.2.x-3.x [generic] [fuzzy] X-Spam_score_int: -30 X-Spam_score: -3.1 X-Spam_bar: --- X-Spam_report: (-3.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-1, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, URIBL_BLOCKED=0.001 autolearn=_AUTOLEARN X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Kevin Wolf , Eduardo Habkost , qemu-block@nongnu.org, John Snow , Max Reitz , Cleber Rosa , philmd@redhat.com Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" When I initially split this out, I considered this more of a machine error than a QMP protocol error, but I think that's misguided. Move this back to qmp.py and name it QMPResponseError. Convert qmp.command() to use this exception type. Signed-off-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Kevin Wolf --- python/qemu/machine.py | 15 +-------------- python/qemu/qmp.py | 17 +++++++++++++++-- scripts/render_block_graph.py | 7 +++++-- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/python/qemu/machine.py b/python/qemu/machine.py index 8c9050af5d..a5a579095b 100644 --- a/python/qemu/machine.py +++ b/python/qemu/machine.py @@ -49,19 +49,6 @@ class QEMUMachineAddDeviceError(QEMUMachineError): """ -class MonitorResponseError(qmp.QMPError): - """ - Represents erroneous QMP monitor reply - """ - def __init__(self, reply): - try: - desc = reply["error"]["desc"] - except KeyError: - desc = reply - super().__init__(desc) - self.reply = reply - - class QEMUMachine: """ A QEMU VM @@ -477,7 +464,7 @@ def command(self, cmd, conv_keys=True, **args): if reply is None: raise qmp.QMPError("Monitor is closed") if "error" in reply: - raise MonitorResponseError(reply) + raise qmp.QMPResponseError(reply) return reply["return"] def get_qmp_event(self, wait=False): diff --git a/python/qemu/qmp.py b/python/qemu/qmp.py index 8388c7b603..aa8a666b8a 100644 --- a/python/qemu/qmp.py +++ b/python/qemu/qmp.py @@ -61,6 +61,19 @@ class QMPTimeoutError(QMPError): """ +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 @@ -251,8 +264,8 @@ def command(self, cmd, **kwds): Build and send a QMP command to the monitor, report errors if any """ ret = self.cmd(cmd, kwds) - if "error" in ret: - raise Exception(ret['error']['desc']) + if 'error' in ret: + raise QMPResponseError(ret) return ret['return'] def pull_event(self, wait=False): diff --git a/scripts/render_block_graph.py b/scripts/render_block_graph.py index 409b4321f2..da6acf050d 100755 --- a/scripts/render_block_graph.py +++ b/scripts/render_block_graph.py @@ -25,7 +25,10 @@ from graphviz import Digraph sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'python')) -from qemu.machine import MonitorResponseError +from qemu.qmp import ( + QEMUMonitorProtocol, + QMPResponseError, +) def perm(arr): @@ -102,7 +105,7 @@ def command(self, cmd): reply = json.loads(subprocess.check_output(ar)) if 'error' in reply: - raise MonitorResponseError(reply) + raise QMPResponseError(reply) return reply['return'] From patchwork Fri Jun 26 20:41:21 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Snow X-Patchwork-Id: 279260 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.8 required=3.0 tests=DKIM_INVALID,DKIM_SIGNED, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6FE6DC433DF for ; Fri, 26 Jun 2020 20:42:57 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 3B4BA204EC for ; Fri, 26 Jun 2020 20:42:57 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="U1eM6dGM" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 3B4BA204EC Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:59940 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jovBY-0008NL-DR for qemu-devel@archiver.kernel.org; Fri, 26 Jun 2020 16:42:56 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:33698) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1jovAQ-0006cm-6N for qemu-devel@nongnu.org; Fri, 26 Jun 2020 16:41:46 -0400 Received: from us-smtp-delivery-1.mimecast.com ([207.211.31.120]:54378 helo=us-smtp-1.mimecast.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.90_1) (envelope-from ) id 1jovAN-0005oB-9S for qemu-devel@nongnu.org; Fri, 26 Jun 2020 16:41:45 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1593204102; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=oNhPhaxByI3BTfZupYYGTKC0C0PPGE+d9nTXAs0uWH4=; b=U1eM6dGMYAjSKl7l0fJzVV69NTWl1XZZKAxNVKhCu1hkVotTomRbal4qJJBL1b0otXCOfM 5MleoDFTzk7PJJNDcvDUt+nqg7FtI/gPsPQ81UUGGDRohsAane/dGn774e0/1GP6m0dtWg UxQU2ff/TNh04TNv4I0TgDp7vHtFC2I= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-159-P_zF_z9WOnyt823INcb2DA-1; Fri, 26 Jun 2020 16:41:40 -0400 X-MC-Unique: P_zF_z9WOnyt823INcb2DA-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id C0149BFC1; Fri, 26 Jun 2020 20:41:39 +0000 (UTC) Received: from probe.redhat.com (ovpn-119-184.rdu2.redhat.com [10.10.119.184]) by smtp.corp.redhat.com (Postfix) with ESMTP id EC26F19C58; Fri, 26 Jun 2020 20:41:38 +0000 (UTC) From: John Snow To: qemu-devel@nongnu.org Subject: [PATCH v4 04/16] python/qmp.py: Do not return None from cmd_obj Date: Fri, 26 Jun 2020 16:41:21 -0400 Message-Id: <20200626204133.14500-5-jsnow@redhat.com> In-Reply-To: <20200626204133.14500-1-jsnow@redhat.com> References: <20200626204133.14500-1-jsnow@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Received-SPF: pass client-ip=207.211.31.120; envelope-from=jsnow@redhat.com; helo=us-smtp-1.mimecast.com X-detected-operating-system: by eggs.gnu.org: First seen = 2020/06/26 15:42:27 X-ACL-Warn: Detected OS = Linux 2.2.x-3.x [generic] [fuzzy] X-Spam_score_int: -30 X-Spam_score: -3.1 X-Spam_bar: --- X-Spam_report: (-3.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-1, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, URIBL_BLOCKED=0.001 autolearn=_AUTOLEARN X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Kevin Wolf , Eduardo Habkost , qemu-block@nongnu.org, John Snow , Max Reitz , Cleber Rosa , philmd@redhat.com Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" This makes typing the qmp library difficult, as it necessitates wrapping Optional[] around the type for every return type up the stack. At some point, it becomes difficult to discern or remember why it's None instead of the expected object. Use the python exception system to tell us exactly why we didn't get an object. Remove this special-cased return. Signed-off-by: John Snow Reviewed-by: Kevin Wolf --- python/qemu/qmp.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/python/qemu/qmp.py b/python/qemu/qmp.py index aa8a666b8a..ef3c919b76 100644 --- a/python/qemu/qmp.py +++ b/python/qemu/qmp.py @@ -225,22 +225,18 @@ def accept(self, timeout=15.0): self.__sockfile = self.__sock.makefile(mode='r') return self.__negotiate_capabilities() - def cmd_obj(self, qmp_cmd): + 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 or None if the connection has - been closed + @return QMP response as a Python dict """ self.logger.debug(">>> %s", qmp_cmd) - try: - self.__sock.sendall(json.dumps(qmp_cmd).encode('utf-8')) - except OSError as err: - if err.errno == errno.EPIPE: - return None - raise err + 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 From patchwork Fri Jun 26 20:41:22 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Snow X-Patchwork-Id: 279256 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.8 required=3.0 tests=DKIM_INVALID,DKIM_SIGNED, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9D7E1C433E0 for ; Fri, 26 Jun 2020 20:48:02 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 685BC20675 for ; Fri, 26 Jun 2020 20:48:02 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="jG5uf58X" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 685BC20675 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:48434 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jovGT-00072B-G7 for qemu-devel@archiver.kernel.org; Fri, 26 Jun 2020 16:48:01 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:33764) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1jovAX-0006sz-EF for qemu-devel@nongnu.org; Fri, 26 Jun 2020 16:41:53 -0400 Received: from us-smtp-1.mimecast.com ([207.211.31.81]:49342 helo=us-smtp-delivery-1.mimecast.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.90_1) (envelope-from ) id 1jovAV-0005po-Jo for qemu-devel@nongnu.org; Fri, 26 Jun 2020 16:41:53 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1593204111; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=gqk7ovbcZenmmjkGeGLVpvHvzhizoBYcL+O5A5dVKHI=; b=jG5uf58XoX8arOzwk/gVEOWfNOrrT62LWQk8tVVhsJFWiqcgn4caoHX5lLCd5MTkknPIXd YOSGnNBxyedEUQGkGgRCcRB9vmFaEn3wYgwipCV/FF7bCs/tw+9LdZtg2IIMnutRGz2v3Y +NbNAuoVgSFpFEveo3D8TzV2DFa3Oqg= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-311-tUvaUqc-OSm0OFb4iS3uag-1; Fri, 26 Jun 2020 16:41:49 -0400 X-MC-Unique: tUvaUqc-OSm0OFb4iS3uag-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 4BA6918585BA; Fri, 26 Jun 2020 20:41:48 +0000 (UTC) Received: from probe.redhat.com (ovpn-119-184.rdu2.redhat.com [10.10.119.184]) by smtp.corp.redhat.com (Postfix) with ESMTP id E1C8319C58; Fri, 26 Jun 2020 20:41:39 +0000 (UTC) From: John Snow To: qemu-devel@nongnu.org Subject: [PATCH v4 05/16] python/qmp.py: add casts to JSON deserialization Date: Fri, 26 Jun 2020 16:41:22 -0400 Message-Id: <20200626204133.14500-6-jsnow@redhat.com> In-Reply-To: <20200626204133.14500-1-jsnow@redhat.com> References: <20200626204133.14500-1-jsnow@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Received-SPF: pass client-ip=207.211.31.81; envelope-from=jsnow@redhat.com; helo=us-smtp-delivery-1.mimecast.com X-detected-operating-system: by eggs.gnu.org: First seen = 2020/06/26 03:23:21 X-ACL-Warn: Detected OS = Linux 2.2.x-3.x [generic] [fuzzy] X-Spam_score_int: -30 X-Spam_score: -3.1 X-Spam_bar: --- X-Spam_report: (-3.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-1, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, URIBL_BLOCKED=0.001 autolearn=_AUTOLEARN X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Kevin Wolf , Eduardo Habkost , qemu-block@nongnu.org, John Snow , Max Reitz , Cleber Rosa , philmd@redhat.com Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" mypy and python type hints are not powerful enough to properly describe JSON messages in Python 3.6. The best we can do, generally, is describe them as Dict[str, Any]. Add casts to coerce this type for static analysis; but do NOT enforce this type at runtime in any way. Note: Python 3.8 adds a TypedDict construct which allows for the description of more arbitrary Dictionary shapes. There is a third-party module, "Pydantic", which is compatible with 3.6 that can be used instead of the JSON library that parses JSON messages to fully-typed Python objects, and may be preferable in some cases. (That is well beyond the scope of this commit or series.) Signed-off-by: John Snow Reviewed-by: Kevin Wolf --- python/qemu/qmp.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/python/qemu/qmp.py b/python/qemu/qmp.py index ef3c919b76..1ae36050a4 100644 --- a/python/qemu/qmp.py +++ b/python/qemu/qmp.py @@ -13,6 +13,7 @@ import logging from typing import ( Any, + cast, Dict, Optional, TextIO, @@ -130,7 +131,10 @@ def __json_read(self, only_event=False): data = self.__sockfile.readline() if not data: return None - resp = json.loads(data) + # 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) @@ -262,7 +266,7 @@ def command(self, cmd, **kwds): ret = self.cmd(cmd, kwds) if 'error' in ret: raise QMPResponseError(ret) - return ret['return'] + return cast(QMPReturnValue, ret['return']) def pull_event(self, wait=False): """ From patchwork Fri Jun 26 20:41:25 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: John Snow X-Patchwork-Id: 279257 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.8 required=3.0 tests=DKIM_INVALID,DKIM_SIGNED, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 72CBEC433E1 for ; Fri, 26 Jun 2020 20:45:41 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 427D320675 for ; Fri, 26 Jun 2020 20:45:41 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="STSFHzJA" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 427D320675 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:41376 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jovEC-0003xl-Go for qemu-devel@archiver.kernel.org; Fri, 26 Jun 2020 16:45:40 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:33864) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1jovAb-00070b-4N for qemu-devel@nongnu.org; Fri, 26 Jun 2020 16:41:57 -0400 Received: from us-smtp-delivery-1.mimecast.com ([207.211.31.120]:37799 helo=us-smtp-1.mimecast.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.90_1) (envelope-from ) id 1jovAY-0005qa-Py for qemu-devel@nongnu.org; Fri, 26 Jun 2020 16:41:56 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1593204114; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=m5FtFcNMJsPCBRXp0TnZIAiThT8ctXMzCke7CUDFGYk=; b=STSFHzJA9pLBwfopfBhazgGv+nzAuoY15HmhDce+vOwURTZGdFDBrtamH1wqRuBsekuv2z ZB9N/G/0gydN3ZxIO7gaM2zR1Bl8om7cRfdt27Dp2BqX/Ju6N0HmUA2s569Vt7obvD86Pt Yq1H4uTcDISJZI/KWx1TctbjnB82SOw= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-215-zrBls-LeM9KQnFwsO5Bvkg-1; Fri, 26 Jun 2020 16:41:52 -0400 X-MC-Unique: zrBls-LeM9KQnFwsO5Bvkg-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 3C64F800FEC; Fri, 26 Jun 2020 20:41:51 +0000 (UTC) Received: from probe.redhat.com (ovpn-119-184.rdu2.redhat.com [10.10.119.184]) by smtp.corp.redhat.com (Postfix) with ESMTP id 691E419C58; Fri, 26 Jun 2020 20:41:50 +0000 (UTC) From: John Snow To: qemu-devel@nongnu.org Subject: [PATCH v4 08/16] python/machine.py: reorder __init__ Date: Fri, 26 Jun 2020 16:41:25 -0400 Message-Id: <20200626204133.14500-9-jsnow@redhat.com> In-Reply-To: <20200626204133.14500-1-jsnow@redhat.com> References: <20200626204133.14500-1-jsnow@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Received-SPF: pass client-ip=207.211.31.120; envelope-from=jsnow@redhat.com; helo=us-smtp-1.mimecast.com X-detected-operating-system: by eggs.gnu.org: First seen = 2020/06/26 15:42:27 X-ACL-Warn: Detected OS = Linux 2.2.x-3.x [generic] [fuzzy] X-Spam_score_int: -30 X-Spam_score: -3.1 X-Spam_bar: --- X-Spam_report: (-3.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-1, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, URIBL_BLOCKED=0.001 autolearn=_AUTOLEARN X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Kevin Wolf , Eduardo Habkost , qemu-block@nongnu.org, John Snow , Max Reitz , Cleber Rosa , philmd@redhat.com Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" Put the init arg handling all at the top, and mostly in order (deviating when one is dependent on another), and put what is effectively runtime state declaration at the bottom. Signed-off-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Kevin Wolf --- python/qemu/machine.py | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/python/qemu/machine.py b/python/qemu/machine.py index 6d38147cd5..36812bd2be 100644 --- a/python/qemu/machine.py +++ b/python/qemu/machine.py @@ -81,38 +81,43 @@ def __init__(self, binary, args=None, wrapper=None, name=None, @param socket_scm_helper: helper program, required for send_fd_scm() @note: Qemu process is not started until launch() is used. ''' + # Direct user configuration + + self._binary = binary + if args is None: args = [] + # Copy mutable input: we will be modifying our copy + self._args = list(args) + if wrapper is None: wrapper = [] - if name is None: - name = "qemu-%d" % os.getpid() - if sock_dir is None: - sock_dir = test_dir - self._name = name + self._wrapper = wrapper + + self._name = name or "qemu-%d" % os.getpid() + self._test_dir = test_dir + self._sock_dir = sock_dir or self._test_dir + self._socket_scm_helper = socket_scm_helper + if monitor_address is not None: self._monitor_address = monitor_address self._remove_monitor_sockfile = False else: self._monitor_address = os.path.join( - sock_dir, f"{name}-monitor.sock" + self._sock_dir, f"{self._name}-monitor.sock" ) self._remove_monitor_sockfile = True + + # Runstate self._qemu_log_path = None self._qemu_log_file = None self._popen = None - self._binary = binary - self._args = list(args) # Force copy args in case we modify them - self._wrapper = wrapper self._events = [] self._iolog = None - self._socket_scm_helper = socket_scm_helper self._qmp_set = True # Enable QMP monitor by default. self._qmp = None self._qemu_full_args = None - self._test_dir = test_dir self._temp_dir = None - self._sock_dir = sock_dir self._launched = False self._machine = None self._console_index = 0 From patchwork Fri Jun 26 20:41:27 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Snow X-Patchwork-Id: 279255 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.8 required=3.0 tests=DKIM_INVALID,DKIM_SIGNED, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 50C92C433DF for ; Fri, 26 Jun 2020 20:48:51 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 1EDCB2075D for ; Fri, 26 Jun 2020 20:48:51 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="Cv5qGzMk" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 1EDCB2075D Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:52320 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jovHG-00009J-D7 for qemu-devel@archiver.kernel.org; Fri, 26 Jun 2020 16:48:50 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:33928) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1jovAf-00078G-3M for qemu-devel@nongnu.org; Fri, 26 Jun 2020 16:42:01 -0400 Received: from us-smtp-2.mimecast.com ([207.211.31.81]:22033 helo=us-smtp-delivery-1.mimecast.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.90_1) (envelope-from ) id 1jovAc-0005rm-0T for qemu-devel@nongnu.org; Fri, 26 Jun 2020 16:42:00 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1593204117; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=VGhfiTXbiXd+kEnKFvL/DKq9TUWbI6mf0WxW/E8MJ8E=; b=Cv5qGzMkQvRNKXBRDYQGvLzQvnA2na2pCqFmPjoLjY/ofaVAycsdDivxuD2N6Z+nXz5rey FxVC4plzFTr4OmtmT1dGoYfZf4zkQjiPxCGzPqWeAAKjbn/JwpoPfBXUNTvuG7N01Mwro/ LTn543pVP7I8YVC1Sl25efaN5+ytk/w= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-223-UiOS-Z6LNuSRkgzxosSuCQ-1; Fri, 26 Jun 2020 16:41:55 -0400 X-MC-Unique: UiOS-Z6LNuSRkgzxosSuCQ-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 91559107ACCA; Fri, 26 Jun 2020 20:41:54 +0000 (UTC) Received: from probe.redhat.com (ovpn-119-184.rdu2.redhat.com [10.10.119.184]) by smtp.corp.redhat.com (Postfix) with ESMTP id 9C5F219C58; Fri, 26 Jun 2020 20:41:53 +0000 (UTC) From: John Snow To: qemu-devel@nongnu.org Subject: [PATCH v4 10/16] python/machine.py: Handle None events in events_wait Date: Fri, 26 Jun 2020 16:41:27 -0400 Message-Id: <20200626204133.14500-11-jsnow@redhat.com> In-Reply-To: <20200626204133.14500-1-jsnow@redhat.com> References: <20200626204133.14500-1-jsnow@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Received-SPF: pass client-ip=207.211.31.81; envelope-from=jsnow@redhat.com; helo=us-smtp-delivery-1.mimecast.com X-detected-operating-system: by eggs.gnu.org: First seen = 2020/06/26 03:23:21 X-ACL-Warn: Detected OS = Linux 2.2.x-3.x [generic] [fuzzy] X-Spam_score_int: -30 X-Spam_score: -3.1 X-Spam_bar: --- X-Spam_report: (-3.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-1, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, URIBL_BLOCKED=0.001 autolearn=_AUTOLEARN X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Kevin Wolf , Eduardo Habkost , qemu-block@nongnu.org, John Snow , Max Reitz , Cleber Rosa , philmd@redhat.com Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" If the timeout is 0, we can get None back. Handle this explicitly. Signed-off-by: John Snow Reviewed-by: Kevin Wolf --- python/qemu/machine.py | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/python/qemu/machine.py b/python/qemu/machine.py index 8eb10a4bc1..f65bd18c1a 100644 --- a/python/qemu/machine.py +++ b/python/qemu/machine.py @@ -33,7 +33,7 @@ from types import TracebackType from . import qmp -from .qmp import SocketAddrT +from .qmp import SocketAddrT, QMPMessage LOG = logging.getLogger(__name__) @@ -552,13 +552,20 @@ def event_wait(self, name, timeout=60.0, match=None): def events_wait(self, events, timeout=60.0): """ - events_wait waits for and returns a named event - from QMP with a timeout. + events_wait waits for and returns a single named event from QMP. + In the case of multiple qualifying events, this function returns the + first one. - events: a sequence of (name, match_criteria) tuples. - The match criteria are optional and may be None. - See event_match for details. - timeout: QEMUMonitorProtocol.pull_event timeout parameter. + :param events: A sequence of (name, match_criteria) tuples. + The match criteria are optional and may be None. + See event_match for details. + :param timeout: Optional timeout, in seconds. + See QEMUMonitorProtocol.pull_event. + + :raise QMPTimeoutError: If timeout was non-zero and no matching events + were found. + :return: A QMP event matching the filter criteria. + If timeout was 0 and no event matched, None. """ def _match(event): for name, match in events: @@ -566,6 +573,8 @@ def _match(event): return True return False + event: Optional[QMPMessage] + # Search cached events for event in self._events: if _match(event): @@ -575,6 +584,10 @@ def _match(event): # Poll for new events while True: event = self._qmp.pull_event(wait=timeout) + if event is None: + # NB: None is only returned when timeout is false-ish. + # Timeouts raise QMPTimeoutError instead! + break if _match(event): return event self._events.append(event) From patchwork Fri Jun 26 20:41:29 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Snow X-Patchwork-Id: 279253 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.8 required=3.0 tests=DKIM_INVALID,DKIM_SIGNED, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 45FBBC433E0 for ; Fri, 26 Jun 2020 20:51:47 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 1064720720 for ; Fri, 26 Jun 2020 20:51:47 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="U+Gs9oGA" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 1064720720 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:34042 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jovK6-0004Qz-A5 for qemu-devel@archiver.kernel.org; Fri, 26 Jun 2020 16:51:46 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:34084) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1jovAq-0007GA-KT for qemu-devel@nongnu.org; Fri, 26 Jun 2020 16:42:13 -0400 Received: from us-smtp-delivery-1.mimecast.com ([205.139.110.120]:52852 helo=us-smtp-1.mimecast.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.90_1) (envelope-from ) id 1jovAf-0005t0-MI for qemu-devel@nongnu.org; Fri, 26 Jun 2020 16:42:12 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1593204120; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=HqqNxG5KpI0efHrqCewjCVxiWjh+EBq81VttRszc3tE=; b=U+Gs9oGAjIoKhqbO0a3/iI5HJJNmrjq1YZIq9n+ZqXqOi+9NAgw2vbY7ZBzm4yUZnSMBk0 1+3ZdruNOIpzLCyALQco0R2qDvQe5SSKjEtI2TipQ66MBz073Ie6uFyCrfqIsHROIZkWFK CFgcvh2nQzN6k5H7EjWCF+mdIMk02DI= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-26-02R0kKfAMTyZ1Ma_FDn7ug-1; Fri, 26 Jun 2020 16:41:57 -0400 X-MC-Unique: 02R0kKfAMTyZ1Ma_FDn7ug-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 8080B107ACCD; Fri, 26 Jun 2020 20:41:56 +0000 (UTC) Received: from probe.redhat.com (ovpn-119-184.rdu2.redhat.com [10.10.119.184]) by smtp.corp.redhat.com (Postfix) with ESMTP id A690B19C58; Fri, 26 Jun 2020 20:41:55 +0000 (UTC) From: John Snow To: qemu-devel@nongnu.org Subject: [PATCH v4 12/16] python/machine.py: Add _qmp access shim Date: Fri, 26 Jun 2020 16:41:29 -0400 Message-Id: <20200626204133.14500-13-jsnow@redhat.com> In-Reply-To: <20200626204133.14500-1-jsnow@redhat.com> References: <20200626204133.14500-1-jsnow@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=jsnow@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Received-SPF: pass client-ip=205.139.110.120; envelope-from=jsnow@redhat.com; helo=us-smtp-1.mimecast.com X-detected-operating-system: by eggs.gnu.org: First seen = 2020/06/26 01:55:55 X-ACL-Warn: Detected OS = Linux 2.2.x-3.x [generic] [fuzzy] X-Spam_score_int: -30 X-Spam_score: -3.1 X-Spam_bar: --- X-Spam_report: (-3.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-1, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, URIBL_BLOCKED=0.001 autolearn=_AUTOLEARN X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Kevin Wolf , Eduardo Habkost , qemu-block@nongnu.org, John Snow , Max Reitz , Cleber Rosa , philmd@redhat.com Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" Like many other Optional[] types, it's not always a given that this object will be set. Wrap it in a type-shim that raises a meaningful error and will always return a concrete type. Signed-off-by: John Snow --- python/qemu/machine.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/python/qemu/machine.py b/python/qemu/machine.py index 9bb5d26407..cb3db1f66a 100644 --- a/python/qemu/machine.py +++ b/python/qemu/machine.py @@ -118,7 +118,7 @@ def __init__(self, binary, args=None, wrapper=None, name=None, self._events = [] self._iolog = None self._qmp_set = True # Enable QMP monitor by default. - self._qmp = None + self._qmp_connection: Optional[qmp.QEMUMonitorProtocol] = None self._qemu_full_args = None self._temp_dir = None self._launched = False @@ -285,14 +285,14 @@ def _pre_launch(self): if self._remove_monitor_sockfile: assert isinstance(self._monitor_address, str) self._remove_files.append(self._monitor_address) - self._qmp = qmp.QEMUMonitorProtocol( + self._qmp_connection = qmp.QEMUMonitorProtocol( self._monitor_address, server=True, nickname=self._name ) def _post_launch(self): - if self._qmp: + if self._qmp_connection: self._qmp.accept() def _post_shutdown(self) -> None: @@ -370,7 +370,7 @@ def wait(self): Wait for the VM to power off """ self._popen.wait() - if self._qmp: + if self._qmp_connection: self._qmp.close() self._post_shutdown() @@ -459,11 +459,13 @@ def set_qmp_monitor(self, enabled=True): line. Default is True. @note: call this function before launch(). """ - if enabled: - self._qmp_set = True - else: - self._qmp_set = False - self._qmp = None + self._qmp_set = enabled + + @property + def _qmp(self) -> qmp.QEMUMonitorProtocol: + if self._qmp_connection is None: + raise QEMUMachineError("Attempt to access QMP with no connection") + return self._qmp_connection @classmethod def _qmp_args(cls, _conv_keys: bool = True, **args: Any) -> Dict[str, Any]: From patchwork Fri Jun 26 20:41:32 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Snow X-Patchwork-Id: 279254 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.8 required=3.0 tests=DKIM_INVALID,DKIM_SIGNED, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5E4FBC433E0 for ; Fri, 26 Jun 2020 20:50:25 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 364D620720 for ; Fri, 26 Jun 2020 20:50:22 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="a/IPFDoF" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 364D620720 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:57112 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jovIj-00029x-FU for qemu-devel@archiver.kernel.org; Fri, 26 Jun 2020 16:50:21 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:34078) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1jovAq-0007G0-Gx for qemu-devel@nongnu.org; Fri, 26 Jun 2020 16:42:13 -0400 Received: from us-smtp-delivery-1.mimecast.com ([207.211.31.120]:36934 helo=us-smtp-1.mimecast.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.90_1) (envelope-from ) id 1jovAl-0005u4-1s for qemu-devel@nongnu.org; Fri, 26 Jun 2020 16:42:12 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1593204124; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=3XuqgNBoHiB1EqTrAtUfEk1aG8bC1rg5A/meSP+YByQ=; b=a/IPFDoFwuAiLeDpoUmWuMlgMEemln165Q5PekNeaatOtlH+PwlmwBIK7m0f0elrMU6Q0/ FxEYirUflujB9/BBRXvX0dBr1WfSM/6cYNdwhBgrH92wSvfrxgvp20OSNyQNKcTg2ViNSw sX3jyagH49JVqgyJ8kCLX5owGvzmKoA= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-373-kD8QHXdPMLyOIYQnyNNsHw-1; Fri, 26 Jun 2020 16:42:00 -0400 X-MC-Unique: kD8QHXdPMLyOIYQnyNNsHw-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 6D02D800D5C; Fri, 26 Jun 2020 20:41:59 +0000 (UTC) Received: from probe.redhat.com (ovpn-119-184.rdu2.redhat.com [10.10.119.184]) by smtp.corp.redhat.com (Postfix) with ESMTP id 966D219C58; Fri, 26 Jun 2020 20:41:58 +0000 (UTC) From: John Snow To: qemu-devel@nongnu.org Subject: [PATCH v4 15/16] iotests.py: Adjust HMP kwargs typing Date: Fri, 26 Jun 2020 16:41:32 -0400 Message-Id: <20200626204133.14500-16-jsnow@redhat.com> In-Reply-To: <20200626204133.14500-1-jsnow@redhat.com> References: <20200626204133.14500-1-jsnow@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Received-SPF: pass client-ip=207.211.31.120; envelope-from=jsnow@redhat.com; helo=us-smtp-1.mimecast.com X-detected-operating-system: by eggs.gnu.org: First seen = 2020/06/26 15:42:27 X-ACL-Warn: Detected OS = Linux 2.2.x-3.x [generic] [fuzzy] X-Spam_score_int: -30 X-Spam_score: -3.1 X-Spam_bar: --- X-Spam_report: (-3.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-1, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, URIBL_BLOCKED=0.001 autolearn=_AUTOLEARN X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Kevin Wolf , Eduardo Habkost , qemu-block@nongnu.org, John Snow , Max Reitz , Cleber Rosa , philmd@redhat.com Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" mypy wants to ensure there's consistency between the kwargs arguments types and any unspecified keyword arguments. In this case, conv_keys is a bool, but the remaining keys are Any type. Mypy (correctly) infers the **kwargs type to be **Dict[str, str], which is not compatible with conv_keys: bool. Because QMP typing is a little fraught right now anyway, re-type kwargs to Dict[str, Any] which has the benefit of silencing this check right now. A future re-design might type these more aggressively, but this will give us a baseline to work from with minimal disruption. (Thanks Kevin Wolf for the debugging assist here) Signed-off-by: John Snow Reviewed-by: Kevin Wolf --- tests/qemu-iotests/iotests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index a8576f811b..9040a671f9 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -553,7 +553,7 @@ def add_incoming(self, addr): def hmp(self, command_line: str, use_log: bool = False) -> QMPMessage: cmd = 'human-monitor-command' - kwargs = {'command-line': command_line} + kwargs: Dict[str, Any] = {'command-line': command_line} if use_log: return self.qmp_log(cmd, **kwargs) else: From patchwork Fri Jun 26 20:41:33 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Snow X-Patchwork-Id: 279252 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.8 required=3.0 tests=DKIM_INVALID,DKIM_SIGNED, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id D1206C433E0 for ; Fri, 26 Jun 2020 20:53:20 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 8E3C420720 for ; Fri, 26 Jun 2020 20:53:20 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="hg7rckK9" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 8E3C420720 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:37922 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jovLb-00065R-TW for qemu-devel@archiver.kernel.org; Fri, 26 Jun 2020 16:53:19 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:34082) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1jovAq-0007G6-JD for qemu-devel@nongnu.org; Fri, 26 Jun 2020 16:42:13 -0400 Received: from us-smtp-2.mimecast.com ([207.211.31.81]:36886 helo=us-smtp-delivery-1.mimecast.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.90_1) (envelope-from ) id 1jovAj-0005tu-Iv for qemu-devel@nongnu.org; Fri, 26 Jun 2020 16:42:12 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1593204123; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=f1wpgVAPE4U4/ZFi+obNRppVSKp5dvn5ZK9lvilHuwM=; b=hg7rckK9YUSPsGqfedNa7ocTt6q4JcMtLvEatUCL2JfTmBK5nMmK75uZjsw+e45HaWBR0A +6Kw/eFvkiNTrkEmsRdSS/NB3m/28zmv6ZyxHTbernI4Iapg7M3D3F4oilvIo6/IE6zMJf pgjaLpEomUEMKTcWWiouCBafvkrFPMY= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-433-uOTpvyaiMMWPRnAsiwcJYQ-1; Fri, 26 Jun 2020 16:42:01 -0400 X-MC-Unique: uOTpvyaiMMWPRnAsiwcJYQ-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 045B5800597; Fri, 26 Jun 2020 20:42:01 +0000 (UTC) Received: from probe.redhat.com (ovpn-119-184.rdu2.redhat.com [10.10.119.184]) by smtp.corp.redhat.com (Postfix) with ESMTP id BE15919C58; Fri, 26 Jun 2020 20:41:59 +0000 (UTC) From: John Snow To: qemu-devel@nongnu.org Subject: [PATCH v4 16/16] python/qemu: Add mypy type annotations Date: Fri, 26 Jun 2020 16:41:33 -0400 Message-Id: <20200626204133.14500-17-jsnow@redhat.com> In-Reply-To: <20200626204133.14500-1-jsnow@redhat.com> References: <20200626204133.14500-1-jsnow@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Received-SPF: pass client-ip=207.211.31.81; envelope-from=jsnow@redhat.com; helo=us-smtp-delivery-1.mimecast.com X-detected-operating-system: by eggs.gnu.org: First seen = 2020/06/26 03:23:21 X-ACL-Warn: Detected OS = Linux 2.2.x-3.x [generic] [fuzzy] X-Spam_score_int: -30 X-Spam_score: -3.1 X-Spam_bar: --- X-Spam_report: (-3.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-1, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, URIBL_BLOCKED=0.001 autolearn=_AUTOLEARN X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Kevin Wolf , Eduardo Habkost , qemu-block@nongnu.org, John Snow , Max Reitz , Cleber Rosa , philmd@redhat.com Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" These should all be purely annotations with no changes in behavior at all. You need to be in the python folder, but you should be able to confirm that these annotations are correct (or at least self-consistent) by running `mypy --strict qemu`. Signed-off-by: John Snow Reviewed-by: Kevin Wolf --- python/qemu/accel.py | 8 ++-- python/qemu/machine.py | 94 ++++++++++++++++++++++++------------------ python/qemu/qmp.py | 44 +++++++++++--------- python/qemu/qtest.py | 26 +++++++----- 4 files changed, 98 insertions(+), 74 deletions(-) diff --git a/python/qemu/accel.py b/python/qemu/accel.py index 7fabe62920..4325114e51 100644 --- a/python/qemu/accel.py +++ b/python/qemu/accel.py @@ -17,6 +17,7 @@ import logging import os import subprocess +from typing import List, Optional LOG = logging.getLogger(__name__) @@ -29,7 +30,7 @@ } -def list_accel(qemu_bin): +def list_accel(qemu_bin: str) -> List[str]: """ List accelerators enabled in the QEMU binary. @@ -49,7 +50,8 @@ def list_accel(qemu_bin): return [acc.strip() for acc in out.splitlines()[1:]] -def kvm_available(target_arch=None, qemu_bin=None): +def kvm_available(target_arch: Optional[str] = None, + qemu_bin: Optional[str] = None) -> bool: """ Check if KVM is available using the following heuristic: - Kernel module is present in the host; @@ -72,7 +74,7 @@ def kvm_available(target_arch=None, qemu_bin=None): return True -def tcg_available(qemu_bin): +def tcg_available(qemu_bin: str) -> bool: """ Check if TCG is available. diff --git a/python/qemu/machine.py b/python/qemu/machine.py index 15e854e04f..9963f76741 100644 --- a/python/qemu/machine.py +++ b/python/qemu/machine.py @@ -28,6 +28,7 @@ import tempfile from typing import ( Any, + BinaryIO, Dict, List, Optional, @@ -38,7 +39,7 @@ from types import TracebackType from . import qmp -from .qmp import SocketAddrT, QMPMessage +from .qmp import QMPMessage, QMPReturnValue, SocketAddrT LOG = logging.getLogger(__name__) @@ -61,7 +62,7 @@ class QEMUMachineAddDeviceError(QEMUMachineError): class QEMUMachine: """ - A QEMU VM + A QEMU VM. Use this object as a context manager to ensure the QEMU process terminates:: @@ -78,7 +79,8 @@ def __init__(self, name: Optional[str] = None, test_dir: str = "/var/tmp", monitor_address: Optional[SocketAddrT] = None, - socket_scm_helper=None, sock_dir=None): + socket_scm_helper: Optional[str] = None, + sock_dir: Optional[str] = None): ''' Initialize a QEMUMachine @@ -112,28 +114,28 @@ def __init__(self, self._remove_monitor_sockfile = True # Runstate - self._qemu_log_path = None - self._qemu_log_file = None + self._qemu_log_path: Optional[str] = None + self._qemu_log_file: Optional[BinaryIO] = None self._popen: Optional['subprocess.Popen[bytes]'] = None - self._events = [] - self._iolog = None + self._events: List[QMPMessage] = [] + self._iolog: Optional[str] = None self._qmp_set = True # Enable QMP monitor by default. self._qmp_connection: Optional[qmp.QEMUMonitorProtocol] = None self._qemu_full_args: Tuple[str, ...] = () - self._temp_dir = None + self._temp_dir: Optional[str] = None self._launched = False - self._machine = None + self._machine: Optional[str] = None self._console_index = 0 self._console_set = False - self._console_device_type = None + self._console_device_type: Optional[str] = None self._console_address = os.path.join( self._sock_dir, f"{self._name}-console.sock" ) - self._console_socket = None - self._remove_files = [] + self._console_socket: Optional[socket.socket] = None + self._remove_files: List[str] = [] self._killed = False - def __enter__(self): + def __enter__(self) -> 'QEMUMachine': return self def __exit__(self, @@ -142,14 +144,15 @@ def __exit__(self, exc_tb: Optional[TracebackType]) -> None: self.shutdown() - def add_monitor_null(self): + def add_monitor_null(self) -> None: """ This can be used to add an unused monitor instance. """ self._args.append('-monitor') self._args.append('null') - def add_fd(self, fd, fdset, opaque, opts=''): + def add_fd(self, fd: int, fdset: int, + opaque: str, opts: str = '') -> 'QEMUMachine': """ Pass a file descriptor to the VM """ @@ -168,7 +171,8 @@ def add_fd(self, fd, fdset, opaque, opts=''): self._args.append(','.join(options)) return self - def send_fd_scm(self, fd=None, file_path=None): + def send_fd_scm(self, fd: Optional[int] = None, + file_path: Optional[str] = None) -> int: """ Send an fd or file_path to socket_scm_helper. @@ -212,7 +216,7 @@ def send_fd_scm(self, fd=None, file_path=None): return proc.returncode @staticmethod - def _remove_if_exists(path): + def _remove_if_exists(path: str) -> None: """ Remove file object at path if it exists """ @@ -223,7 +227,7 @@ def _remove_if_exists(path): return raise - def is_running(self): + def is_running(self) -> bool: """Returns true if the VM is running.""" return self._popen is not None and self._popen.poll() is None @@ -233,19 +237,19 @@ def _subp(self) -> 'subprocess.Popen[bytes]': raise QEMUMachineError('Subprocess pipe not present') return self._popen - def exitcode(self): + def exitcode(self) -> Optional[int]: """Returns the exit code if possible, or None.""" if self._popen is None: return None return self._popen.poll() - def get_pid(self): + def get_pid(self) -> Optional[int]: """Returns the PID of the running process, or None.""" if not self.is_running(): return None return self._subp.pid - def _load_io_log(self): + def _load_io_log(self) -> None: if self._qemu_log_path is not None: with open(self._qemu_log_path, "r") as iolog: self._iolog = iolog.read() @@ -279,7 +283,7 @@ def _base_args(self) -> List[str]: args.extend(['-device', device]) return args - def _pre_launch(self): + def _pre_launch(self) -> None: self._temp_dir = tempfile.mkdtemp(dir=self._test_dir) self._qemu_log_path = os.path.join(self._temp_dir, self._name + ".log") self._qemu_log_file = open(self._qemu_log_path, 'wb') @@ -297,7 +301,7 @@ def _pre_launch(self): nickname=self._name ) - def _post_launch(self): + def _post_launch(self) -> None: if self._qmp_connection: self._qmp.accept() @@ -330,7 +334,7 @@ def _post_shutdown(self) -> None: self._killed = False self._launched = False - def launch(self): + def launch(self) -> None: """ Launch the VM and make sure we cleanup and expose the command line/output in case of exception @@ -354,7 +358,7 @@ def launch(self): LOG.debug('Output: %r', self._iolog) raise - def _launch(self): + def _launch(self) -> None: """ Launch the VM and establish a QMP connection """ @@ -375,7 +379,7 @@ def _launch(self): close_fds=False) self._post_launch() - def wait(self): + def wait(self) -> None: """ Wait for the VM to power off """ @@ -454,13 +458,13 @@ def shutdown(self, has_quit: bool = False, hard: bool = False) -> None: finally: self._post_shutdown() - def kill(self): + def kill(self) -> None: """ Terminate the VM forcefully and perform cleanup. """ self.shutdown(hard=True) - def set_qmp_monitor(self, enabled=True): + def set_qmp_monitor(self, enabled: bool = True) -> None: """ Set the QMP monitor. @@ -496,7 +500,9 @@ def qmp(self, cmd: str, qmp_args = self._qmp_args(conv_keys, **args) return self._qmp.cmd(cmd, args=qmp_args) - def command(self, cmd, conv_keys=True, **args): + def command(self, cmd: str, + conv_keys: bool = True, + **args: Any) -> QMPReturnValue: """ Invoke a QMP command. On success return the response dict. @@ -505,7 +511,7 @@ def command(self, cmd, conv_keys=True, **args): qmp_args = self._qmp_args(conv_keys, **args) return self._qmp.command(cmd, **qmp_args) - def get_qmp_event(self, wait=False): + def get_qmp_event(self, wait: bool = False) -> Optional[QMPMessage]: """ Poll for one queued QMP events and return it """ @@ -513,7 +519,7 @@ def get_qmp_event(self, wait=False): return self._events.pop(0) return self._qmp.pull_event(wait=wait) - def get_qmp_events(self, wait=False): + def get_qmp_events(self, wait: bool = False) -> List[QMPMessage]: """ Poll for queued QMP events and return a list of dicts """ @@ -524,7 +530,7 @@ def get_qmp_events(self, wait=False): return events @staticmethod - def event_match(event, match=None): + def event_match(event: Any, match: Optional[Any]) -> bool: """ Check if an event matches optional match criteria. @@ -554,9 +560,11 @@ def event_match(event, match=None): return True except TypeError: # either match or event wasn't iterable (not a dict) - return match == event + return bool(match == event) - def event_wait(self, name, timeout=60.0, match=None): + def event_wait(self, name: str, + timeout: float = 60.0, + match: Optional[QMPMessage] = None) -> Optional[QMPMessage]: """ event_wait waits for and returns a named event from QMP with a timeout. @@ -566,7 +574,9 @@ def event_wait(self, name, timeout=60.0, match=None): """ return self.events_wait([(name, match)], timeout) - def events_wait(self, events, timeout=60.0): + def events_wait(self, + events: Sequence[Tuple[str, Any]], + timeout: float = 60.0) -> Optional[QMPMessage]: """ events_wait waits for and returns a single named event from QMP. In the case of multiple qualifying events, this function returns the @@ -583,7 +593,7 @@ def events_wait(self, events, timeout=60.0): :return: A QMP event matching the filter criteria. If timeout was 0 and no event matched, None. """ - def _match(event): + def _match(event: QMPMessage) -> bool: for name, match in events: if event['event'] == name and self.event_match(event, match): return True @@ -610,20 +620,20 @@ def _match(event): return None - def get_log(self): + def get_log(self) -> Optional[str]: """ After self.shutdown or failed qemu execution, this returns the output of the qemu process. """ return self._iolog - def add_args(self, *args): + def add_args(self, *args: str) -> None: """ Adds to the list of extra arguments to be given to the QEMU binary """ self._args.extend(args) - def set_machine(self, machine_type): + def set_machine(self, machine_type: str) -> None: """ Sets the machine type @@ -632,7 +642,9 @@ def set_machine(self, machine_type): """ self._machine = machine_type - def set_console(self, device_type=None, console_index=0): + def set_console(self, + device_type: Optional[str] = None, + console_index: int = 0) -> None: """ Sets the device type for a console device @@ -663,7 +675,7 @@ def set_console(self, device_type=None, console_index=0): self._console_index = console_index @property - def console_socket(self): + def console_socket(self) -> socket.socket: """ Returns a socket connected to the console """ diff --git a/python/qemu/qmp.py b/python/qemu/qmp.py index 7935dababb..303e82ee6b 100644 --- a/python/qemu/qmp.py +++ b/python/qemu/qmp.py @@ -15,6 +15,7 @@ Any, cast, Dict, + List, Optional, TextIO, Type, @@ -90,7 +91,9 @@ class QEMUMonitorProtocol: #: Logger object for debugging messages logger = logging.getLogger('QMP') - def __init__(self, address, server=False, nickname=None): + def __init__(self, address: SocketAddrT, + server: bool = False, + nickname: Optional[str] = None): """ Create a QEMUMonitorProtocol class. @@ -102,7 +105,7 @@ def __init__(self, address, server=False, nickname=None): @note No connection is established, this is done by the connect() or accept() methods """ - self.__events = [] + self.__events: List[QMPMessage] = [] self.__address = address self.__sock = self.__get_sock() self.__sockfile: Optional[TextIO] = None @@ -114,14 +117,14 @@ def __init__(self, address, server=False, nickname=None): self.__sock.bind(self.__address) self.__sock.listen(1) - def __get_sock(self): + 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): + def __negotiate_capabilities(self) -> QMPMessage: greeting = self.__json_read() if greeting is None or "QMP" not in greeting: raise QMPConnectError @@ -131,7 +134,7 @@ def __negotiate_capabilities(self): return greeting raise QMPCapabilitiesError - def __json_read(self, only_event=False): + def __json_read(self, only_event: bool = False) -> Optional[QMPMessage]: assert self.__sockfile is not None while True: data = self.__sockfile.readline() @@ -148,7 +151,7 @@ def __json_read(self, only_event=False): continue return resp - def __get_events(self, wait=False): + def __get_events(self, wait: Union[bool, float] = False) -> None: """ Check for new events in the stream and cache them in __events. @@ -186,7 +189,7 @@ def __get_events(self, wait=False): raise QMPConnectError("Error while reading from socket") self.__sock.settimeout(None) - def __enter__(self): + def __enter__(self) -> 'QEMUMonitorProtocol': # Implement context manager enter function. return self @@ -199,7 +202,7 @@ def __exit__(self, # Implement context manager exit function. self.close() - def connect(self, negotiate=True): + def connect(self, negotiate: bool = True) -> Optional[QMPMessage]: """ Connect to the QMP Monitor and perform capabilities negotiation. @@ -214,7 +217,7 @@ def connect(self, negotiate=True): return self.__negotiate_capabilities() return None - def accept(self, timeout=15.0): + def accept(self, timeout: float = 15.0) -> QMPMessage: """ Await connection from QMP Monitor and perform capabilities negotiation. @@ -250,7 +253,9 @@ def cmd_obj(self, qmp_cmd: QMPMessage) -> QMPMessage: self.logger.debug("<<< %s", resp) return resp - def cmd(self, name, args=None, cmd_id=None): + def cmd(self, name: str, + args: Optional[Dict[str, Any]] = None, + cmd_id: Optional[Any] = None) -> QMPMessage: """ Build a QMP command and send it to the QMP Monitor. @@ -258,14 +263,14 @@ def cmd(self, name, args=None, cmd_id=None): @param args: command arguments (dict) @param cmd_id: command id (dict, list, string or int) """ - qmp_cmd = {'execute': name} + 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, **kwds): + def command(self, cmd: str, **kwds: Any) -> QMPReturnValue: """ Build and send a QMP command to the monitor, report errors if any """ @@ -278,7 +283,8 @@ def command(self, cmd, **kwds): ) return cast(QMPReturnValue, ret['return']) - def pull_event(self, wait=False): + def pull_event(self, + wait: Union[bool, float] = False) -> Optional[QMPMessage]: """ Pulls a single event. @@ -298,7 +304,7 @@ def pull_event(self, wait=False): return self.__events.pop(0) return None - def get_events(self, wait=False): + def get_events(self, wait: bool = False) -> List[QMPMessage]: """ Get a list of available QMP events. @@ -315,13 +321,13 @@ def get_events(self, wait=False): self.__get_events(wait) return self.__events - def clear_events(self): + def clear_events(self) -> None: """ Clear current list of pending events. """ self.__events = [] - def close(self): + def close(self) -> None: """ Close the socket and socket file. """ @@ -330,7 +336,7 @@ def close(self): if self.__sockfile: self.__sockfile.close() - def settimeout(self, timeout): + def settimeout(self, timeout: float) -> None: """ Set the socket timeout. @@ -339,7 +345,7 @@ def settimeout(self, timeout): """ self.__sock.settimeout(timeout) - def get_sock_fd(self): + def get_sock_fd(self) -> int: """ Get the socket file descriptor. @@ -347,7 +353,7 @@ def get_sock_fd(self): """ return self.__sock.fileno() - def is_scm_available(self): + def is_scm_available(self) -> bool: """ Check if the socket allows for SCM_RIGHTS. diff --git a/python/qemu/qtest.py b/python/qemu/qtest.py index ae4661d4d3..49c04bc98d 100644 --- a/python/qemu/qtest.py +++ b/python/qemu/qtest.py @@ -27,6 +27,7 @@ ) from .machine import QEMUMachine +from .qmp import SocketAddrT class QEMUQtestProtocol: @@ -43,7 +44,8 @@ class QEMUQtestProtocol: No conection is estabalished by __init__(), this is done by the connect() or accept() methods. """ - def __init__(self, address, server=False): + def __init__(self, address: SocketAddrT, + server: bool = False): self._address = address self._sock = self._get_sock() self._sockfile: Optional[TextIO] = None @@ -51,14 +53,14 @@ def __init__(self, address, server=False): self._sock.bind(self._address) self._sock.listen(1) - def _get_sock(self): + 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 connect(self): + def connect(self) -> None: """ Connect to the qtest socket. @@ -67,7 +69,7 @@ def connect(self): self._sock.connect(self._address) self._sockfile = self._sock.makefile(mode='r') - def accept(self): + def accept(self) -> None: """ Await connection from QEMU. @@ -76,7 +78,7 @@ def accept(self): self._sock, _ = self._sock.accept() self._sockfile = self._sock.makefile(mode='r') - def cmd(self, qtest_cmd): + def cmd(self, qtest_cmd: str) -> str: """ Send a qtest command on the wire. @@ -87,14 +89,16 @@ def cmd(self, qtest_cmd): resp = self._sockfile.readline() return resp - def close(self): - """Close this socket.""" + def close(self) -> None: + """ + Close this socket. + """ self._sock.close() if self._sockfile: self._sockfile.close() self._sockfile = None - def settimeout(self, timeout): + def settimeout(self, timeout: Optional[float]) -> None: """Set a timeout, in seconds.""" self._sock.settimeout(timeout) @@ -118,7 +122,7 @@ def __init__(self, super().__init__(binary, args, name=name, test_dir=test_dir, socket_scm_helper=socket_scm_helper, sock_dir=sock_dir) - self._qtest = None + self._qtest: Optional[QEMUQtestProtocol] = None self._qtest_path = os.path.join(sock_dir, name + "-qtest.sock") @property @@ -130,7 +134,7 @@ def _base_args(self) -> List[str]: ]) return args - def _pre_launch(self): + def _pre_launch(self) -> None: super()._pre_launch() self._qtest = QEMUQtestProtocol(self._qtest_path, server=True) @@ -139,7 +143,7 @@ def _post_launch(self) -> None: super()._post_launch() self._qtest.accept() - def _post_shutdown(self): + def _post_shutdown(self) -> None: super()._post_shutdown() self._remove_if_exists(self._qtest_path)