From patchwork Thu Nov 16 01:43:32 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Snow X-Patchwork-Id: 13457502 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org 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 smtp.lore.kernel.org (Postfix) with ESMTPS id 0FBDBC5ACB3 for ; Thu, 16 Nov 2023 01:48:22 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1r3RQL-0005Im-Bg; Wed, 15 Nov 2023 20:44:05 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3RQJ-0005GI-KL for qemu-devel@nongnu.org; Wed, 15 Nov 2023 20:44:03 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3RQE-0001zq-4V for qemu-devel@nongnu.org; Wed, 15 Nov 2023 20:44:03 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1700099037; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Gb8CAZVMsVsHjLgjk1Zt5AMrISOZgCh7rMmJiIH9KEM=; b=hJ1ayTXn2Y+5RqsBI1LsGiHDU7vVP1IVcNFwx58sfnfLyyMlWkwyek5OPCqLMat55EMkWc YXGqV4ed73dutJXhBrbaxm2fhzYSG08ys6XoAyCUJi/kYFifyp9XABlHdv4W9MWjhN/ZAR JDd1ev1Ge+C6zjvMZES3eNYamLhf9+c= Received: from mimecast-mx02.redhat.com (mx-ext.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-381-_xUr2OrcP36ttdrqveUZKg-1; Wed, 15 Nov 2023 20:43:51 -0500 X-MC-Unique: _xUr2OrcP36ttdrqveUZKg-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.rdu2.redhat.com [10.11.54.8]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 99D3E3C11CC1; Thu, 16 Nov 2023 01:43:51 +0000 (UTC) Received: from scv.localdomain (unknown [10.22.32.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 5615CC15881; Thu, 16 Nov 2023 01:43:51 +0000 (UTC) From: John Snow To: qemu-devel@nongnu.org Cc: Peter Maydell , Michael Roth , Markus Armbruster , John Snow Subject: [PATCH 01/19] qapi/schema: fix QAPISchemaEntity.__repr__() Date: Wed, 15 Nov 2023 20:43:32 -0500 Message-ID: <20231116014350.653792-2-jsnow@redhat.com> In-Reply-To: <20231116014350.653792-1-jsnow@redhat.com> References: <20231116014350.653792-1-jsnow@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.8 Received-SPF: pass client-ip=170.10.129.124; envelope-from=jsnow@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -21 X-Spam_score: -2.2 X-Spam_bar: -- X-Spam_report: (-2.2 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.099, 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_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org This needs parentheses to work how you want it to: >>> "%s-%s-%s" % 'a', 'b', 'c' Traceback (most recent call last): File "", line 1, in TypeError: not enough arguments for format string >>> "%s-%s-%s" % ('a', 'b', 'c') 'a-b-c' Signed-off-by: John Snow --- scripts/qapi/schema.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index d739e558e9e..c79747b2a15 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -76,7 +76,7 @@ def __init__(self, name: str, info, doc, ifcond=None, features=None): def __repr__(self): if self.name is None: return "<%s at 0x%x>" % (type(self).__name__, id(self)) - return "<%s:%s at 0x%x>" % type(self).__name__, self.name, id(self) + return "<%s:%s at 0x%x>" % (type(self).__name__, self.name, id(self)) def c_name(self): return c_name(self.name) From patchwork Thu Nov 16 01:43:33 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Snow X-Patchwork-Id: 13457495 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org 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 smtp.lore.kernel.org (Postfix) with ESMTPS id 1CF4FC5ACB3 for ; Thu, 16 Nov 2023 01:46:11 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1r3RQJ-0005GD-JK; Wed, 15 Nov 2023 20:44:03 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3RQH-0005Eo-S8 for qemu-devel@nongnu.org; Wed, 15 Nov 2023 20:44:01 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3RQC-0001zP-EL for qemu-devel@nongnu.org; Wed, 15 Nov 2023 20:44:01 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1700099035; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=08it7JyTmlqZ2qYMjBMaLdaRQndHwtSJh7g5239FHgk=; b=JhmnLFXY9Kn8PsH5BTcQbnEVheP+vf/r0l2+cCZt/UHKtklw8uOo64C2HTSB49oIY4b/Pu RoeGGUx6C3DpCz15idzHfxtEPHfGkOEJe8ikzvo2ytTkP/bTxy2q+qTcYdQJ9D9njBGv14 GnvaQzNZeflj+g2GCqUTr7kI9FWK/FE= Received: from mimecast-mx02.redhat.com (mx-ext.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-477-4VQTjWjoPiWX20yeV7hLDw-1; Wed, 15 Nov 2023 20:43:52 -0500 X-MC-Unique: 4VQTjWjoPiWX20yeV7hLDw-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.rdu2.redhat.com [10.11.54.8]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id EC51C1C05EC3; Thu, 16 Nov 2023 01:43:51 +0000 (UTC) Received: from scv.localdomain (unknown [10.22.32.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id A946CC15881; Thu, 16 Nov 2023 01:43:51 +0000 (UTC) From: John Snow To: qemu-devel@nongnu.org Cc: Peter Maydell , Michael Roth , Markus Armbruster , John Snow Subject: [PATCH 02/19] qapi/schema: add pylint suppressions Date: Wed, 15 Nov 2023 20:43:33 -0500 Message-ID: <20231116014350.653792-3-jsnow@redhat.com> In-Reply-To: <20231116014350.653792-1-jsnow@redhat.com> References: <20231116014350.653792-1-jsnow@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.8 Received-SPF: pass client-ip=170.10.129.124; envelope-from=jsnow@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -21 X-Spam_score: -2.2 X-Spam_bar: -- X-Spam_report: (-2.2 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.099, 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_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org With this, pylint is happy with the file, so enable it in the configuration. Signed-off-by: John Snow Reviewed-by: Markus Armbruster --- scripts/qapi/pylintrc | 5 ----- scripts/qapi/schema.py | 4 ++++ 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/scripts/qapi/pylintrc b/scripts/qapi/pylintrc index 90546df5345..aafddd3d8d0 100644 --- a/scripts/qapi/pylintrc +++ b/scripts/qapi/pylintrc @@ -1,10 +1,5 @@ [MASTER] -# Add files or directories matching the regex patterns to the ignore list. -# The regex matches against base names, not paths. -ignore-patterns=schema.py, - - [MESSAGES CONTROL] # Disable the message, report, category or checker with the given id(s). You diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index c79747b2a15..153e703e0ef 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -13,6 +13,7 @@ # See the COPYING file in the top-level directory. # TODO catching name collisions in generated code would be nice +# pylint: disable=too-many-lines from collections import OrderedDict import os @@ -82,6 +83,7 @@ def c_name(self): return c_name(self.name) def check(self, schema): + # pylint: disable=unused-argument assert not self._checked seen = {} for f in self.features: @@ -116,6 +118,7 @@ def is_implicit(self): return not self.info def visit(self, visitor): + # pylint: disable=unused-argument assert self._checked def describe(self): @@ -134,6 +137,7 @@ def visit_module(self, name): pass def visit_needed(self, entity): + # pylint: disable=unused-argument # Default to visiting everything return True From patchwork Thu Nov 16 01:43:34 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Snow X-Patchwork-Id: 13457500 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org 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 smtp.lore.kernel.org (Postfix) with ESMTPS id 92226C2BB3F for ; Thu, 16 Nov 2023 01:48:21 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1r3RQJ-0005Gv-VW; Wed, 15 Nov 2023 20:44:04 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3RQI-0005Ew-6k for qemu-devel@nongnu.org; Wed, 15 Nov 2023 20:44:02 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3RQD-0001zc-RI for qemu-devel@nongnu.org; Wed, 15 Nov 2023 20:44:01 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1700099036; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=pizuQG2VKmmCYLJ6GmUnliG1sq7KBbVuRhIS3gCZAzg=; b=BzR1Ov3+mHHR3hCsl0FDCGPhaWR1UdLz6RWxdFurZi0AOLlgl/+kZe6ENPZXMH6HX9A7zD vBRentjjBV5Dt3Gh1XUxUL9qPjgR1hq8KOxaFJ4zL9weRlF4alCS805nF8z731GXkL1ZMg Q8chcpfGaEcaBnwmyi+z45QaYALk4QQ= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-32-7GvnHjE4N3KMuACPqNLOLw-1; Wed, 15 Nov 2023 20:43:52 -0500 X-MC-Unique: 7GvnHjE4N3KMuACPqNLOLw-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.rdu2.redhat.com [10.11.54.8]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 4DCFF185A782; Thu, 16 Nov 2023 01:43:52 +0000 (UTC) Received: from scv.localdomain (unknown [10.22.32.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 09159C15881; Thu, 16 Nov 2023 01:43:51 +0000 (UTC) From: John Snow To: qemu-devel@nongnu.org Cc: Peter Maydell , Michael Roth , Markus Armbruster , John Snow Subject: [PATCH 03/19] qapi/schema: name QAPISchemaInclude entities Date: Wed, 15 Nov 2023 20:43:34 -0500 Message-ID: <20231116014350.653792-4-jsnow@redhat.com> In-Reply-To: <20231116014350.653792-1-jsnow@redhat.com> References: <20231116014350.653792-1-jsnow@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.8 Received-SPF: pass client-ip=170.10.133.124; envelope-from=jsnow@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -21 X-Spam_score: -2.2 X-Spam_bar: -- X-Spam_report: (-2.2 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.099, 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.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org It simplifies typing to mandate that entities will always have a name; to achieve this we can occasionally assign an internal name. This alleviates errors such as: qapi/schema.py:287: error: Argument 1 to "__init__" of "QAPISchemaEntity" has incompatible type "None"; expected "str" [arg-type] Trying to fix it the other way by allowing entities to only have optional names opens up a nightmare portal of whackamole to try and audit that every other pathway doesn't actually pass a None name when we expect it to; this is the simpler direction of consitifying the typing. Signed-off-by: John Snow --- scripts/qapi/schema.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 153e703e0ef..0fb44452dd5 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -220,7 +220,9 @@ def visit(self, visitor): class QAPISchemaInclude(QAPISchemaEntity): def __init__(self, sub_module, info): - super().__init__(None, info, None) + # Includes are internal entity objects; and may occur multiple times + name = f"q_include_{info.fname}:{info.line}" + super().__init__(name, info, None) self._sub_module = sub_module def visit(self, visitor): From patchwork Thu Nov 16 01:43:35 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Snow X-Patchwork-Id: 13457489 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org 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 smtp.lore.kernel.org (Postfix) with ESMTPS id EC87AC2BB3F for ; Thu, 16 Nov 2023 01:44:52 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1r3RQI-0005GB-Sc; Wed, 15 Nov 2023 20:44:02 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3RQG-0005ET-LO for qemu-devel@nongnu.org; Wed, 15 Nov 2023 20:44:00 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3RQB-0001zJ-Dq for qemu-devel@nongnu.org; Wed, 15 Nov 2023 20:44:00 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1700099034; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Kg3m98RICCt6Ri/bKbY7xmy4OwxNVxaMaqze8HLBBIw=; b=NM6XqBF+Jp9Sa1fnUg8YRfiqTm2PbTwOgskeVryuFToVh+d/W4geSdjhfjWL7uacMJq46J c2eyUqRK/4v8o1FhyqTxP8CrnSxHHqQB42pQ3Cf4O5tgQmBeNCJK8yLtSgV6L2TxpUfVJJ f4bdBo/LW1zj9L8WjTFuzRDY/w6YsYQ= Received: from mimecast-mx02.redhat.com (mx-ext.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-466-Lsh9LAnTNrGRG4fXX0Ea0g-1; Wed, 15 Nov 2023 20:43:53 -0500 X-MC-Unique: Lsh9LAnTNrGRG4fXX0Ea0g-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.rdu2.redhat.com [10.11.54.8]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 9C7C33C11CC0; Thu, 16 Nov 2023 01:43:52 +0000 (UTC) Received: from scv.localdomain (unknown [10.22.32.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 5BE7FC15881; Thu, 16 Nov 2023 01:43:52 +0000 (UTC) From: John Snow To: qemu-devel@nongnu.org Cc: Peter Maydell , Michael Roth , Markus Armbruster , John Snow Subject: [PATCH 04/19] qapi/schema: declare type for QAPISchemaObjectTypeMember.type Date: Wed, 15 Nov 2023 20:43:35 -0500 Message-ID: <20231116014350.653792-5-jsnow@redhat.com> In-Reply-To: <20231116014350.653792-1-jsnow@redhat.com> References: <20231116014350.653792-1-jsnow@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.8 Received-SPF: pass client-ip=170.10.133.124; envelope-from=jsnow@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -21 X-Spam_score: -2.2 X-Spam_bar: -- X-Spam_report: (-2.2 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.099, 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.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org declare, but don't initialize the type of "type" to be QAPISchemaType - and allow the value to be initialized during check(). This avoids the need for several "assert type is not None" statements littered throughout the code by asserting it "will always be set." It's a little hokey, but it works -- at the expense of slightly incorrect type information before check() is called, anyway. If this field is accessed before it is initialized, you'll be treated to an AttributeError exception. Fixes stuff like this: qapi/schema.py:657: error: "None" has no attribute "alternate_qtype" [attr-defined] qapi/schema.py:662: error: "None" has no attribute "describe" [attr-defined] Signed-off-by: John Snow --- scripts/qapi/schema.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 0fb44452dd5..c5fdd625452 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -771,7 +771,7 @@ def __init__(self, name, info, typ, optional, ifcond=None, features=None): assert isinstance(f, QAPISchemaFeature) f.set_defined_in(name) self._type_name = typ - self.type = None + self.type: QAPISchemaType # set during check(). Kind of hokey. self.optional = optional self.features = features or [] From patchwork Thu Nov 16 01:43:36 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: John Snow X-Patchwork-Id: 13457493 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org 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 smtp.lore.kernel.org (Postfix) with ESMTPS id CB98EC072A2 for ; Thu, 16 Nov 2023 01:46:08 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1r3RQQ-0005LE-OX; Wed, 15 Nov 2023 20:44:10 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3RQJ-0005GJ-Kf for qemu-devel@nongnu.org; Wed, 15 Nov 2023 20:44:03 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3RQD-0001zf-SM for qemu-devel@nongnu.org; Wed, 15 Nov 2023 20:44:03 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1700099037; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=LUNYTwKFIURIMs3zNQb1pL4DzA4DI1sItK09tnGDlGw=; b=hClPuCE7FahmpbYZyBjgPrB0WoE4fX3GiK1F4eLP7X7XrDqEPETUnpYyPe1IeOBaJfepAW d/z4E+Dr6Eqz1DQFcaLKS+RueSDYxHsA13+2q4t5LgtAAJLJXIk1a3jdyNVSyCBWuPMdeM SxDUpvoBdkjyLZ21UZMFN2mHbxXO5+0= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-649-7-V9uUfdNq--p_dRs5O1Zg-1; Wed, 15 Nov 2023 20:43:53 -0500 X-MC-Unique: 7-V9uUfdNq--p_dRs5O1Zg-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.rdu2.redhat.com [10.11.54.8]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id EBED3828B20; Thu, 16 Nov 2023 01:43:52 +0000 (UTC) Received: from scv.localdomain (unknown [10.22.32.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id ACA21C15881; Thu, 16 Nov 2023 01:43:52 +0000 (UTC) From: John Snow To: qemu-devel@nongnu.org Cc: Peter Maydell , Michael Roth , Markus Armbruster , John Snow Subject: [PATCH 05/19] qapi/schema: make c_type() and json_type() abstract methods Date: Wed, 15 Nov 2023 20:43:36 -0500 Message-ID: <20231116014350.653792-6-jsnow@redhat.com> In-Reply-To: <20231116014350.653792-1-jsnow@redhat.com> References: <20231116014350.653792-1-jsnow@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.8 Received-SPF: pass client-ip=170.10.129.124; envelope-from=jsnow@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -21 X-Spam_score: -2.2 X-Spam_bar: -- X-Spam_report: (-2.2 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.099, 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_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org These methods should always return a str, it's only the default abstract implementation that doesn't. They can be marked "abstract" by raising NotImplementedError(), which requires subclasses to override the method with the proper return type. Signed-off-by: John Snow Reviewed-by: Philippe Mathieu-Daudé --- scripts/qapi/schema.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index c5fdd625452..4600a566005 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -233,8 +233,8 @@ def visit(self, visitor): class QAPISchemaType(QAPISchemaEntity): # Return the C type for common use. # For the types we commonly box, this is a pointer type. - def c_type(self): - pass + def c_type(self) -> str: + raise NotImplementedError() # Return the C type to be used in a parameter list. def c_param_type(self): @@ -244,8 +244,8 @@ def c_param_type(self): def c_unboxed_type(self): return self.c_type() - def json_type(self): - pass + def json_type(self) -> str: + raise NotImplementedError() def alternate_qtype(self): json2qtype = { From patchwork Thu Nov 16 01:43:37 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: John Snow X-Patchwork-Id: 13457501 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org 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 smtp.lore.kernel.org (Postfix) with ESMTPS id D0B8CC5ACB2 for ; Thu, 16 Nov 2023 01:48:21 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1r3RQY-0005M8-MK; Wed, 15 Nov 2023 20:44:18 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3RQJ-0005GP-N5 for qemu-devel@nongnu.org; Wed, 15 Nov 2023 20:44:03 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3RQE-0001zw-2D for qemu-devel@nongnu.org; Wed, 15 Nov 2023 20:44:03 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1700099037; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=AbiChjSL/2Si4PQ35HQcRARm4HUx3rnT4JAXvzmVC2A=; b=LMzcP+xbLjGQU4lJ9Vo9wxgWNTcGSaF51/XLXUZrkveez/uLdcN4wBZPfGMFCfCgHXRxiJ w1pqxs+f9jQ9T528oZKxM2T8ji+siVcWHysb4iGfzxn1TSHY8ILvchBin4WAwjPm28SFgA qV83lCHQVqGmz2ugNq32Xux9KLFTHOU= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-450-_LK-HtIcNYyX7P_NWkR8kQ-1; Wed, 15 Nov 2023 20:43:53 -0500 X-MC-Unique: _LK-HtIcNYyX7P_NWkR8kQ-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.rdu2.redhat.com [10.11.54.8]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 504D7101A529; Thu, 16 Nov 2023 01:43:53 +0000 (UTC) Received: from scv.localdomain (unknown [10.22.32.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 0EA8DC15881; Thu, 16 Nov 2023 01:43:53 +0000 (UTC) From: John Snow To: qemu-devel@nongnu.org Cc: Peter Maydell , Michael Roth , Markus Armbruster , John Snow Subject: [PATCH 06/19] qapi/schema: adjust type narrowing for mypy's benefit Date: Wed, 15 Nov 2023 20:43:37 -0500 Message-ID: <20231116014350.653792-7-jsnow@redhat.com> In-Reply-To: <20231116014350.653792-1-jsnow@redhat.com> References: <20231116014350.653792-1-jsnow@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.8 Received-SPF: pass client-ip=170.10.133.124; envelope-from=jsnow@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -21 X-Spam_score: -2.2 X-Spam_bar: -- X-Spam_report: (-2.2 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.099, 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.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org We already take care to perform some type narrowing for arg_type and ret_type, but not in a way where mypy can utilize the result. A simple change to use a temporary variable helps the medicine go down. Signed-off-by: John Snow Reviewed-by: Philippe Mathieu-Daudé --- scripts/qapi/schema.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 4600a566005..a1094283828 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -825,13 +825,14 @@ def __init__(self, name, info, doc, ifcond, features, def check(self, schema): super().check(schema) if self._arg_type_name: - self.arg_type = schema.resolve_type( + arg_type = schema.resolve_type( self._arg_type_name, self.info, "command's 'data'") - if not isinstance(self.arg_type, QAPISchemaObjectType): + if not isinstance(arg_type, QAPISchemaObjectType): raise QAPISemError( self.info, "command's 'data' cannot take %s" - % self.arg_type.describe()) + % arg_type.describe()) + self.arg_type = arg_type if self.arg_type.variants and not self.boxed: raise QAPISemError( self.info, @@ -848,8 +849,7 @@ def check(self, schema): if self.name not in self.info.pragma.command_returns_exceptions: typ = self.ret_type if isinstance(typ, QAPISchemaArrayType): - typ = self.ret_type.element_type - assert typ + typ = typ.element_type if not isinstance(typ, QAPISchemaObjectType): raise QAPISemError( self.info, @@ -885,13 +885,14 @@ def __init__(self, name, info, doc, ifcond, features, arg_type, boxed): def check(self, schema): super().check(schema) if self._arg_type_name: - self.arg_type = schema.resolve_type( + typ = schema.resolve_type( self._arg_type_name, self.info, "event's 'data'") - if not isinstance(self.arg_type, QAPISchemaObjectType): + if not isinstance(typ, QAPISchemaObjectType): raise QAPISemError( self.info, "event's 'data' cannot take %s" - % self.arg_type.describe()) + % typ.describe()) + self.arg_type = typ if self.arg_type.variants and not self.boxed: raise QAPISemError( self.info, From patchwork Thu Nov 16 01:43:38 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Snow X-Patchwork-Id: 13457488 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org 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 smtp.lore.kernel.org (Postfix) with ESMTPS id B58EFC072A2 for ; Thu, 16 Nov 2023 01:44:52 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1r3RQL-0005It-Gx; Wed, 15 Nov 2023 20:44:05 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3RQI-0005Eq-3x for qemu-devel@nongnu.org; Wed, 15 Nov 2023 20:44:02 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3RQD-0001zZ-P9 for qemu-devel@nongnu.org; Wed, 15 Nov 2023 20:44:01 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1700099036; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=DEsxYWL/rvlUKgzze2QNRGRzTzXyfu0i4Gw01lfJ6SI=; b=PFBRti6wz4m0gzQAuAJX77ReKO8cidd19RwtQ1TdtVkzn0HoynzWxxhF9cLVAbT2FFpW/O j+ezATN8fxCbxMhpHdUEcxWb2y7puILrac6E/rnU/uqt8Z8TGvfrZqgN1qRf9DJMWhkKkc gLTD2Move7ovah1M/ujXQsGAkv7OaSs= Received: from mimecast-mx02.redhat.com (mx-ext.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-292-F_tLtw9RN768TLHrAPfXtQ-1; Wed, 15 Nov 2023 20:43:54 -0500 X-MC-Unique: F_tLtw9RN768TLHrAPfXtQ-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.rdu2.redhat.com [10.11.54.8]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id A43773813F24; Thu, 16 Nov 2023 01:43:53 +0000 (UTC) Received: from scv.localdomain (unknown [10.22.32.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 5FDB3C15881; Thu, 16 Nov 2023 01:43:53 +0000 (UTC) From: John Snow To: qemu-devel@nongnu.org Cc: Peter Maydell , Michael Roth , Markus Armbruster , John Snow Subject: [PATCH 07/19] qapi/introspect: assert schema.lookup_type did not fail Date: Wed, 15 Nov 2023 20:43:38 -0500 Message-ID: <20231116014350.653792-8-jsnow@redhat.com> In-Reply-To: <20231116014350.653792-1-jsnow@redhat.com> References: <20231116014350.653792-1-jsnow@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.8 Received-SPF: pass client-ip=170.10.133.124; envelope-from=jsnow@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -21 X-Spam_score: -2.2 X-Spam_bar: -- X-Spam_report: (-2.2 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.099, 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.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org lookup_type() is capable of returning None, but introspect.py isn't prepared for that. (And rightly so, if these built-in types are absent, something has gone hugely wrong.) RFC: This is slightly cumbersome as-is, but a patch at the end of this series tries to address it with some slightly slicker lookup functions that don't need as much hand-holding. Signed-off-by: John Snow --- scripts/qapi/introspect.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index 67c7d89aae0..42981bce163 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -227,10 +227,14 @@ def _use_type(self, typ: QAPISchemaType) -> str: # Map the various integer types to plain int if typ.json_type() == 'int': - typ = self._schema.lookup_type('int') + tmp = self._schema.lookup_type('int') + assert tmp is not None + typ = tmp elif (isinstance(typ, QAPISchemaArrayType) and typ.element_type.json_type() == 'int'): - typ = self._schema.lookup_type('intList') + tmp = self._schema.lookup_type('intList') + assert tmp is not None + typ = tmp # Add type to work queue if new if typ not in self._used_types: self._used_types.append(typ) From patchwork Thu Nov 16 01:43:39 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Snow X-Patchwork-Id: 13457498 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org 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 smtp.lore.kernel.org (Postfix) with ESMTPS id 342C7C2BB3F for ; Thu, 16 Nov 2023 01:48:19 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1r3RQJ-0005Gc-Ta; Wed, 15 Nov 2023 20:44:03 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3RQI-0005Ep-3J for qemu-devel@nongnu.org; Wed, 15 Nov 2023 20:44:02 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3RQC-0001zV-Sn for qemu-devel@nongnu.org; Wed, 15 Nov 2023 20:44:01 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1700099036; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=HYLkqBBvaIhpzJOuU0P3RkHpLlo+uQ+r8gLb/DfUIYQ=; b=QbcyyOkXeWfc/0HRttLGxQZ/eEsbSSd1SlK7y4fNfCpiL2NvCO56Ue9g5Ew0aOm+k1z2tF vTmD084M5kfffOlkIaRqGEQTI/bIKXE2OstJXmzp2ui05Hr9QJG8CxpqUlI5ZWAP1/oETW fEuqZybopxyBabCZghKQl1589jfAsoM= Received: from mimecast-mx02.redhat.com (mx-ext.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-50-d-EWvkQQNhOZ5xqTmAOxwA-1; Wed, 15 Nov 2023 20:43:54 -0500 X-MC-Unique: d-EWvkQQNhOZ5xqTmAOxwA-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.rdu2.redhat.com [10.11.54.8]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 040ED1C05EC9; Thu, 16 Nov 2023 01:43:54 +0000 (UTC) Received: from scv.localdomain (unknown [10.22.32.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id B58F7C15881; Thu, 16 Nov 2023 01:43:53 +0000 (UTC) From: John Snow To: qemu-devel@nongnu.org Cc: Peter Maydell , Michael Roth , Markus Armbruster , John Snow Subject: [PATCH 08/19] qapi/schema: add static typing and assertions to lookup_type() Date: Wed, 15 Nov 2023 20:43:39 -0500 Message-ID: <20231116014350.653792-9-jsnow@redhat.com> In-Reply-To: <20231116014350.653792-1-jsnow@redhat.com> References: <20231116014350.653792-1-jsnow@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.8 Received-SPF: pass client-ip=170.10.133.124; envelope-from=jsnow@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -21 X-Spam_score: -2.2 X-Spam_bar: -- X-Spam_report: (-2.2 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.099, 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.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org This function is a bit hard to type as-is; mypy needs some assertions to assist with the type narrowing. Signed-off-by: John Snow --- scripts/qapi/schema.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index a1094283828..3308f334872 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -968,8 +968,12 @@ def lookup_entity(self, name, typ=None): return None return ent - def lookup_type(self, name): - return self.lookup_entity(name, QAPISchemaType) + def lookup_type(self, name: str) -> Optional[QAPISchemaType]: + typ = self.lookup_entity(name, QAPISchemaType) + if typ is None: + return None + assert isinstance(typ, QAPISchemaType) + return typ def resolve_type(self, name, info, what): typ = self.lookup_type(name) From patchwork Thu Nov 16 01:43:40 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: John Snow X-Patchwork-Id: 13457499 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org 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 smtp.lore.kernel.org (Postfix) with ESMTPS id AA128C072A2 for ; Thu, 16 Nov 2023 01:48:19 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1r3RQO-0005Jp-Ja; Wed, 15 Nov 2023 20:44:08 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3RQJ-0005GM-LV for qemu-devel@nongnu.org; Wed, 15 Nov 2023 20:44:03 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3RQE-0001zp-20 for qemu-devel@nongnu.org; Wed, 15 Nov 2023 20:44:03 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1700099036; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=xCAXe+RXvP9aNPfdOK7rQogOvX6tsD3P/HmDMl4jIYA=; b=IYs3RH8vpnDga1bQ5PUAfy6k2e1zUbtcCZv1z85Fx3eo3W/w7oOGe13cJhV774ejqXUs9e lZVo8QXbpEtIJhw1m9wJuLDM2Na7+QayMPsI+mWvRaciw8Nubi037R/ImIRYhsMR5N59ji hp39iZTYvorXVwnDf9Y9sOwN+auC98k= Received: from mimecast-mx02.redhat.com (mx-ext.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-511-Tvtl6IfANB-HwN9aaIQgtw-1; Wed, 15 Nov 2023 20:43:54 -0500 X-MC-Unique: Tvtl6IfANB-HwN9aaIQgtw-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.rdu2.redhat.com [10.11.54.8]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 587A03C11CC1; Thu, 16 Nov 2023 01:43:54 +0000 (UTC) Received: from scv.localdomain (unknown [10.22.32.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 15AD5C15881; Thu, 16 Nov 2023 01:43:54 +0000 (UTC) From: John Snow To: qemu-devel@nongnu.org Cc: Peter Maydell , Michael Roth , Markus Armbruster , John Snow Subject: [PATCH 09/19] qapi/schema: assert info is present when necessary Date: Wed, 15 Nov 2023 20:43:40 -0500 Message-ID: <20231116014350.653792-10-jsnow@redhat.com> In-Reply-To: <20231116014350.653792-1-jsnow@redhat.com> References: <20231116014350.653792-1-jsnow@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.8 Received-SPF: pass client-ip=170.10.129.124; envelope-from=jsnow@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -21 X-Spam_score: -2.2 X-Spam_bar: -- X-Spam_report: (-2.2 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.099, 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_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org QAPISchemaInfo is sometimes defined as an Optional field because built-in definitions don't *have* a source definition. As a consequence, there are a few places where we need to assert that it's present because the root entity definition only suggests it's "Optional". Signed-off-by: John Snow Reviewed-by: Philippe Mathieu-Daudé --- scripts/qapi/schema.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 3308f334872..c9a194103e1 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -733,6 +733,7 @@ def describe(self, info): else: assert False + assert info is not None if defined_in != info.defn_name: return "%s '%s' of %s '%s'" % (role, self.name, meta, defined_in) return "%s '%s'" % (role, self.name) @@ -823,6 +824,7 @@ def __init__(self, name, info, doc, ifcond, features, self.coroutine = coroutine def check(self, schema): + assert self.info is not None super().check(schema) if self._arg_type_name: arg_type = schema.resolve_type( From patchwork Thu Nov 16 01:43:41 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Snow X-Patchwork-Id: 13457496 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org 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 smtp.lore.kernel.org (Postfix) with ESMTPS id DB05BC2BB3F for ; Thu, 16 Nov 2023 01:46:16 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1r3RQO-0005Jn-JH; Wed, 15 Nov 2023 20:44:08 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3RQJ-0005GL-L1 for qemu-devel@nongnu.org; Wed, 15 Nov 2023 20:44:03 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3RQF-000206-D4 for qemu-devel@nongnu.org; Wed, 15 Nov 2023 20:44:03 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1700099038; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=oK5TLvGHeEz6GjH3xnyCpoO4sbbIXKIwxy0cQYHdnKU=; b=dlazBobFX5fhJ1/b2hDjl3r7HQON6JUgVDe2mNb5vzWH21p6B9oEM1w3lidjjxoASueq6V OmPfJbq8DDDhTTytzvtqrVaU/PCQGifPgN/zKD2z8CSoKgb65F8MUhxa/LOs+aaTmtU91H +RY7eYQGn8Xj9sGQqiPovIovNtRJDaU= Received: from mimecast-mx02.redhat.com (mx-ext.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-467-D7I6cy5ZNd-imEiGH6G__g-1; Wed, 15 Nov 2023 20:43:54 -0500 X-MC-Unique: D7I6cy5ZNd-imEiGH6G__g-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.rdu2.redhat.com [10.11.54.8]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id A656E3813F43; Thu, 16 Nov 2023 01:43:54 +0000 (UTC) Received: from scv.localdomain (unknown [10.22.32.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 66600C15881; Thu, 16 Nov 2023 01:43:54 +0000 (UTC) From: John Snow To: qemu-devel@nongnu.org Cc: Peter Maydell , Michael Roth , Markus Armbruster , John Snow Subject: [PATCH 10/19] qapi/schema: make QAPISchemaArrayType.element_type non-Optional Date: Wed, 15 Nov 2023 20:43:41 -0500 Message-ID: <20231116014350.653792-11-jsnow@redhat.com> In-Reply-To: <20231116014350.653792-1-jsnow@redhat.com> References: <20231116014350.653792-1-jsnow@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.8 Received-SPF: pass client-ip=170.10.129.124; envelope-from=jsnow@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -21 X-Spam_score: -2.2 X-Spam_bar: -- X-Spam_report: (-2.2 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.099, 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_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org This field should always be present and defined. Change this to be a runtime @property that can emit an error if it's called prior to check(). This helps simplify typing by avoiding the need to interrogate the value for None at multiple callsites. RFC: Yes, this is a slightly different technique than the one I used for QAPISchemaObjectTypeMember.type; I think I prefer this one as being a little less hokey, but it is more SLOC. Dealer's choice for which style wins out -- now you have an example of both. Signed-off-by: John Snow --- scripts/qapi/schema.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index c9a194103e1..462acb2bb61 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -366,7 +366,16 @@ def __init__(self, name, info, element_type): super().__init__(name, info, None) assert isinstance(element_type, str) self._element_type_name = element_type - self.element_type = None + self._element_type: Optional[QAPISchemaType] = None + + @property + def element_type(self) -> QAPISchemaType: + if self._element_type is None: + raise RuntimeError( + "QAPISchemaArray has no element_type until " + "after check() has been run." + ) + return self._element_type def need_has_if_optional(self): # When FOO is an array, we still need has_FOO to distinguish @@ -375,7 +384,7 @@ def need_has_if_optional(self): def check(self, schema): super().check(schema) - self.element_type = schema.resolve_type( + self._element_type = schema.resolve_type( self._element_type_name, self.info, self.info and self.info.defn_meta) assert not isinstance(self.element_type, QAPISchemaArrayType) From patchwork Thu Nov 16 01:43:42 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Snow X-Patchwork-Id: 13457503 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org 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 smtp.lore.kernel.org (Postfix) with ESMTPS id 45F9AC072A2 for ; Thu, 16 Nov 2023 01:48:32 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1r3RQR-0005LI-2x; Wed, 15 Nov 2023 20:44:11 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3RQL-0005In-3y for qemu-devel@nongnu.org; Wed, 15 Nov 2023 20:44:05 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3RQF-000208-HB for qemu-devel@nongnu.org; Wed, 15 Nov 2023 20:44:04 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1700099038; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=QXJzx/jpVoPhmJMyDwQ8ccRKvqe6ESeYERwYom9inLk=; b=Ow6rXgaBaVsA59YbAe7uc1uP5jKzjur9n0Cp+XCrdEJzLOdnY108IbCK8Qp3guB8Zn5CmB j481prJjXrWtPqgNZHpMvLdg8J6LSQdBO/6xOWKz0imZw2UVYuNZfpLJxx5I2HxWwAKSFQ ndo0mQ+29PUuklU0mwdI/b8yG7a25As= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-507-Mt4ACs1HMxGBvCyWTu9jfg-1; Wed, 15 Nov 2023 20:43:55 -0500 X-MC-Unique: Mt4ACs1HMxGBvCyWTu9jfg-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.rdu2.redhat.com [10.11.54.8]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 05BA1101A529; Thu, 16 Nov 2023 01:43:55 +0000 (UTC) Received: from scv.localdomain (unknown [10.22.32.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id B6CE3C15881; Thu, 16 Nov 2023 01:43:54 +0000 (UTC) From: John Snow To: qemu-devel@nongnu.org Cc: Peter Maydell , Michael Roth , Markus Armbruster , John Snow Subject: [PATCH 11/19] qapi/schema: fix QAPISchemaArrayType.check's call to resolve_type Date: Wed, 15 Nov 2023 20:43:42 -0500 Message-ID: <20231116014350.653792-12-jsnow@redhat.com> In-Reply-To: <20231116014350.653792-1-jsnow@redhat.com> References: <20231116014350.653792-1-jsnow@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.8 Received-SPF: pass client-ip=170.10.129.124; envelope-from=jsnow@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -21 X-Spam_score: -2.2 X-Spam_bar: -- X-Spam_report: (-2.2 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.099, 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_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org There's more conditionals in here than we can reasonably pack into a terse little statement, so break it apart into something more explicit. (When would a built-in array ever cause a QAPISemError? I don't know, maybe never - but the type system wasn't happy all the same.) Signed-off-by: John Snow --- scripts/qapi/schema.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 462acb2bb61..164d86c4064 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -384,9 +384,16 @@ def need_has_if_optional(self): def check(self, schema): super().check(schema) + + if self.info: + assert self.info.defn_meta # guaranteed to be set by expr.py + what = self.info.defn_meta + else: + what = 'built-in array' + self._element_type = schema.resolve_type( - self._element_type_name, self.info, - self.info and self.info.defn_meta) + self._element_type_name, self.info, what + ) assert not isinstance(self.element_type, QAPISchemaArrayType) def set_module(self, schema): From patchwork Thu Nov 16 01:43:43 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Snow X-Patchwork-Id: 13457504 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org 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 smtp.lore.kernel.org (Postfix) with ESMTPS id 7E883C2BB3F for ; Thu, 16 Nov 2023 01:48:36 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1r3RQL-0005Iy-GY; Wed, 15 Nov 2023 20:44:05 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3RQI-0005Ex-78 for qemu-devel@nongnu.org; Wed, 15 Nov 2023 20:44:02 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3RQD-0001zi-RW for qemu-devel@nongnu.org; Wed, 15 Nov 2023 20:44:01 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1700099037; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=gi5EYqfimKMm9nEhPGFz0iR7hcyEZzY6gH4iQaJdxSU=; b=EfHsGCBzR9GlbFtMAuMUU5qWN8lfx8e6KZyyOAt1SYMYWNbBCNe7HIibvxLrWoPZ1D6ohY W9zyy0eZgbAIHqN2lxg69tAvcfudPgLU1L64s3V2jAIdsiZpzxRyIr9Ww9lpXXSdGR6Y7T 4HTyAxTknWIVmZDZEdWCGZFfcnaWUqQ= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-412-NmgZ6iOIMmCGsDMei6hEew-1; Wed, 15 Nov 2023 20:43:55 -0500 X-MC-Unique: NmgZ6iOIMmCGsDMei6hEew-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.rdu2.redhat.com [10.11.54.8]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 55F13101A53B; Thu, 16 Nov 2023 01:43:55 +0000 (UTC) Received: from scv.localdomain (unknown [10.22.32.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 169C9C15881; Thu, 16 Nov 2023 01:43:55 +0000 (UTC) From: John Snow To: qemu-devel@nongnu.org Cc: Peter Maydell , Michael Roth , Markus Armbruster , John Snow Subject: [PATCH 12/19] qapi/schema: split "checked" field into "checking" and "checked" Date: Wed, 15 Nov 2023 20:43:43 -0500 Message-ID: <20231116014350.653792-13-jsnow@redhat.com> In-Reply-To: <20231116014350.653792-1-jsnow@redhat.com> References: <20231116014350.653792-1-jsnow@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.8 Received-SPF: pass client-ip=170.10.133.124; envelope-from=jsnow@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -21 X-Spam_score: -2.2 X-Spam_bar: -- X-Spam_report: (-2.2 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.099, 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.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Differentiate between "actively in the process of checking" and "checking has completed". This allows us to clean up the types of some internal fields such as QAPISchemaObjectType's members field which currently uses "None" as a canary for determining if check has completed. This simplifies the typing from a cumbersome Optional[List[T]] to merely a List[T], which is more pythonic: it is safe to iterate over an empty list with "for x in []" whereas with an Optional[List[T]] you have to rely on the more cumbersome "if L: for x in L: ..." RFC: are we guaranteed to have members here? can we just use "if members" without adding the new field? Signed-off-by: John Snow --- scripts/qapi/schema.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 164d86c4064..200bc0730d6 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -18,7 +18,7 @@ from collections import OrderedDict import os import re -from typing import List, Optional +from typing import List, Optional, cast from .common import ( POINTER_SUFFIX, @@ -447,22 +447,24 @@ def __init__(self, name, info, doc, ifcond, features, self.base = None self.local_members = local_members self.variants = variants - self.members = None + self.members: List[QAPISchemaObjectTypeMember] = [] + self._checking = False def check(self, schema): # This calls another type T's .check() exactly when the C # struct emitted by gen_object() contains that T's C struct # (pointers don't count). - if self.members is not None: - # A previous .check() completed: nothing to do - return - if self._checked: + if self._checking: # Recursed: C struct contains itself raise QAPISemError(self.info, "object %s contains itself" % self.name) + if self._checked: + # A previous .check() completed: nothing to do + return + self._checking = True super().check(schema) - assert self._checked and self.members is None + assert self._checked and not self.members seen = OrderedDict() if self._base_name: @@ -479,13 +481,17 @@ def check(self, schema): for m in self.local_members: m.check(schema) m.check_clash(self.info, seen) - members = seen.values() + + # check_clash is abstract, but local_members is asserted to be + # List[QAPISchemaObjectTypeMember]. Cast to the narrower type. + members = cast(List[QAPISchemaObjectTypeMember], list(seen.values())) if self.variants: self.variants.check(schema, seen) self.variants.check_clash(self.info, seen) - self.members = members # mark completed + self.members = members + self._checking = False # mark completed # Check that the members of this type do not cause duplicate JSON members, # and update seen to track the members seen so far. Report any errors From patchwork Thu Nov 16 01:43:44 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Snow X-Patchwork-Id: 13457490 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org 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 smtp.lore.kernel.org (Postfix) with ESMTPS id 3876AC2BB3F for ; Thu, 16 Nov 2023 01:44:59 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1r3RQh-0005Mr-Vd; Wed, 15 Nov 2023 20:44:28 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3RQL-0005Ip-7l for qemu-devel@nongnu.org; Wed, 15 Nov 2023 20:44:05 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3RQG-00021g-Gp for qemu-devel@nongnu.org; Wed, 15 Nov 2023 20:44:04 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1700099039; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=4+md9DVhvTNARGMr4KalPOeP3cyXwJOih9g+YoQcPuA=; b=B7IbmpvXswulSkhfyO/zGJdh+KZdXXT7/ct9y3BoQye4mL9adtg5Gb9L9aUWbjT3JbEwjM 23AJQFASXzEnTOYIgIm2EIyShK9lJm/9NJKKcZNMzH69oqOEsQL+RIadO/6SoZZk/O8r4v SRMsOmMP0vXNqT4Cwp28F7mUb43QnSw= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-396-IniX0dqbMwCmSqmY5UL6tQ-1; Wed, 15 Nov 2023 20:43:56 -0500 X-MC-Unique: IniX0dqbMwCmSqmY5UL6tQ-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.rdu2.redhat.com [10.11.54.8]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id A7743828B21; Thu, 16 Nov 2023 01:43:55 +0000 (UTC) Received: from scv.localdomain (unknown [10.22.32.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 66569C15882; Thu, 16 Nov 2023 01:43:55 +0000 (UTC) From: John Snow To: qemu-devel@nongnu.org Cc: Peter Maydell , Michael Roth , Markus Armbruster , John Snow Subject: [PATCH 13/19] qapi/schema: fix typing for QAPISchemaVariants.tag_member Date: Wed, 15 Nov 2023 20:43:44 -0500 Message-ID: <20231116014350.653792-14-jsnow@redhat.com> In-Reply-To: <20231116014350.653792-1-jsnow@redhat.com> References: <20231116014350.653792-1-jsnow@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.8 Received-SPF: pass client-ip=170.10.133.124; envelope-from=jsnow@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -21 X-Spam_score: -2.2 X-Spam_bar: -- X-Spam_report: (-2.2 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.099, 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.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org There are two related changes here: (1) We need to perform type narrowing for resolving the type of tag_member during check(), and (2) tag_member is a delayed initialization field, but we can hide it behind a property that raises an Exception if it's called too early. This simplifies the typing in quite a few places and avoids needing to assert that the "tag_member is not None" at a dozen callsites, which can be confusing and suggest the wrong thing to a drive-by contributor. Signed-off-by: John Snow --- scripts/qapi/schema.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 200bc0730d6..476b19aed61 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -627,25 +627,39 @@ def __init__(self, tag_name, info, tag_member, variants): assert isinstance(v, QAPISchemaVariant) self._tag_name = tag_name self.info = info - self.tag_member = tag_member + self._tag_member: Optional[QAPISchemaObjectTypeMember] = tag_member self.variants = variants + @property + def tag_member(self) -> 'QAPISchemaObjectTypeMember': + if self._tag_member is None: + raise RuntimeError( + "QAPISchemaVariants has no tag_member property until " + "after check() has been run." + ) + return self._tag_member + def set_defined_in(self, name): for v in self.variants: v.set_defined_in(name) def check(self, schema, seen): if self._tag_name: # union - self.tag_member = seen.get(c_name(self._tag_name)) + # We need to narrow the member type: + tmp = seen.get(c_name(self._tag_name)) + assert tmp is None or isinstance(tmp, QAPISchemaObjectTypeMember) + self._tag_member = tmp + base = "'base'" # Pointing to the base type when not implicit would be # nice, but we don't know it here - if not self.tag_member or self._tag_name != self.tag_member.name: + if not self._tag_member or self._tag_name != self._tag_member.name: raise QAPISemError( self.info, "discriminator '%s' is not a member of %s" % (self._tag_name, base)) # Here we do: + assert self.tag_member.defined_in base_type = schema.lookup_type(self.tag_member.defined_in) assert base_type if not base_type.is_implicit(): @@ -666,11 +680,13 @@ def check(self, schema, seen): "discriminator member '%s' of %s must not be conditional" % (self._tag_name, base)) else: # alternate + assert self._tag_member assert isinstance(self.tag_member.type, QAPISchemaEnumType) assert not self.tag_member.optional assert not self.tag_member.ifcond.is_present() if self._tag_name: # union # branches that are not explicitly covered get an empty type + assert self.tag_member.defined_in cases = {v.name for v in self.variants} for m in self.tag_member.type.members: if m.name not in cases: From patchwork Thu Nov 16 01:43:45 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Snow X-Patchwork-Id: 13457497 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org 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 smtp.lore.kernel.org (Postfix) with ESMTPS id ABE86C072A2 for ; Thu, 16 Nov 2023 01:47:31 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1r3RQW-0005Lp-De; Wed, 15 Nov 2023 20:44:16 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3RQL-0005Io-42 for qemu-devel@nongnu.org; Wed, 15 Nov 2023 20:44:05 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3RQG-00021e-Gh for qemu-devel@nongnu.org; Wed, 15 Nov 2023 20:44:04 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1700099039; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=c1/ixl/HcaQ8BKWHpVLZPVs1V8B+oy2fFnGDNW16PNo=; b=jV8kFHtCIVAjxchUaqGmsErJXjiglCVgxZAsRqnXq04rnB1M7OjbEHRmfTNhblQHOdFmxN BoME6mbY4csDXADI0ecobtRDpxsuU6j3Dih68bIpsfkLjtBSfORmkD/TaRZflIm5tCLlp7 eKkr6uoZz6GQ8u/SYGkYaF8DK8fJha0= Received: from mimecast-mx02.redhat.com (mx-ext.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-647-htlJlfQdO7CAnMY7PZVwzA-1; Wed, 15 Nov 2023 20:43:56 -0500 X-MC-Unique: htlJlfQdO7CAnMY7PZVwzA-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.rdu2.redhat.com [10.11.54.8]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 06B933813F25; Thu, 16 Nov 2023 01:43:56 +0000 (UTC) Received: from scv.localdomain (unknown [10.22.32.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id B94DBC15881; Thu, 16 Nov 2023 01:43:55 +0000 (UTC) From: John Snow To: qemu-devel@nongnu.org Cc: Peter Maydell , Michael Roth , Markus Armbruster , John Snow Subject: [PATCH 14/19] qapi/schema: assert QAPISchemaVariants are QAPISchemaObjectType Date: Wed, 15 Nov 2023 20:43:45 -0500 Message-ID: <20231116014350.653792-15-jsnow@redhat.com> In-Reply-To: <20231116014350.653792-1-jsnow@redhat.com> References: <20231116014350.653792-1-jsnow@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.8 Received-SPF: pass client-ip=170.10.129.124; envelope-from=jsnow@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -21 X-Spam_score: -2.2 X-Spam_bar: -- X-Spam_report: (-2.2 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.099, 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_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org I'm actually not too sure about this one, it seems to hold up at runtime but instead of lying and coming up with an elaborate ruse as a commit message I'm just going to admit I just cribbed my own notes from the last time I typed schema.py and I no longer remember why or if this is correct. Cool! With more seriousness, variants are only guaranteed to house a QAPISchemaType as per the definition of QAPISchemaObjectTypeMember but the only classes/types that have a check_clash method are descendents of QAPISchemaMember and the QAPISchemaVariants class itself. Signed-off-by: John Snow --- scripts/qapi/schema.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 476b19aed61..ce5b01b3182 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -717,6 +717,7 @@ def check_clash(self, info, seen): for v in self.variants: # Reset seen map for each variant, since qapi names from one # branch do not affect another branch + assert isinstance(v.type, QAPISchemaObjectType) # I think, anyway? v.type.check_clash(info, dict(seen)) From patchwork Thu Nov 16 01:43:46 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: John Snow X-Patchwork-Id: 13457494 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org 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 smtp.lore.kernel.org (Postfix) with ESMTPS id E6109C5ACB2 for ; Thu, 16 Nov 2023 01:46:10 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1r3RQk-0005O3-2R; Wed, 15 Nov 2023 20:44:32 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3RQO-0005KR-V6 for qemu-devel@nongnu.org; Wed, 15 Nov 2023 20:44:09 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3RQG-00021j-NX for qemu-devel@nongnu.org; Wed, 15 Nov 2023 20:44:08 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1700099040; 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=wDMUx5WUimMYInObg2HL2FZ5zCDslAURlR2Qdinz46k=; b=KA88bmfi/DiWtxpQgbJ9weiQwkJ1+ptmGt2qAgtvv5bckUqVWuAkj5/btogaizC0yW+jKC YCL9IwZMA36r5xFYKcv7npSP6JtS4+OsVFi7KqwFw+dHbCKprTREimqj0Bj16CPise0sO4 KkJBk1C22LiJxtPPf/m9+RiZDJcxWis= Received: from mimecast-mx02.redhat.com (mx-ext.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-368-BYGxaspXORS11NqDDhb8uA-1; Wed, 15 Nov 2023 20:43:56 -0500 X-MC-Unique: BYGxaspXORS11NqDDhb8uA-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.rdu2.redhat.com [10.11.54.8]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 5C3EB29ABA27; Thu, 16 Nov 2023 01:43:56 +0000 (UTC) Received: from scv.localdomain (unknown [10.22.32.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 1ABFBC15881; Thu, 16 Nov 2023 01:43:56 +0000 (UTC) From: John Snow To: qemu-devel@nongnu.org Cc: Peter Maydell , Michael Roth , Markus Armbruster , John Snow Subject: [PATCH 15/19] qapi/parser: demote QAPIExpression to Dict[str, Any] Date: Wed, 15 Nov 2023 20:43:46 -0500 Message-ID: <20231116014350.653792-16-jsnow@redhat.com> In-Reply-To: <20231116014350.653792-1-jsnow@redhat.com> References: <20231116014350.653792-1-jsnow@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.8 Received-SPF: pass client-ip=170.10.133.124; envelope-from=jsnow@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -21 X-Spam_score: -2.2 X-Spam_bar: -- X-Spam_report: (-2.2 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.099, 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.001, RCVD_IN_MSPIKE_WL=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01, T_SPF_HELO_TEMPERROR=0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Dict[str, object] is a stricter type, but with the way that code is currently arranged, it is infeasible to enforce this strictness. In particular, although expr.py's entire raison d'être is normalization and type-checking of QAPI Expressions, that type information is not "remembered" in any meaningful way by mypy because each individual expression is not downcast to a specific expression type that holds all the details of each expression's unique form. As a result, all of the code in schema.py that deals with actually creating type-safe specialized structures has no guarantee (myopically) that the data it is being passed is correct. There are two ways to solve this: (1) Re-assert that the incoming data is in the shape we expect it to be, or (2) Disable type checking for this data. (1) is appealing to my sense of strictness, but I gotta concede that it is asinine to re-check the shape of a QAPIExpression in schema.py when expr.py has just completed that work at length. The duplication of code and the nightmare thought of needing to update both locations if and when we change the shape of these structures makes me extremely reluctant to go down this route. (2) allows us the chance to miss updating types in the case that types are updated in expr.py, but it *is* an awful lot simpler and, importantly, gets us closer to type checking schema.py *at all*. Something is better than nothing, I'd argue. So, do the simpler dumber thing and worry about future strictness improvements later. Signed-off-by: John Snow --- scripts/qapi/parser.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py index bf31018aef0..b7f08cf36f2 100644 --- a/scripts/qapi/parser.py +++ b/scripts/qapi/parser.py @@ -19,6 +19,7 @@ import re from typing import ( TYPE_CHECKING, + Any, Dict, List, Mapping, @@ -43,7 +44,7 @@ _ExprValue = Union[List[object], Dict[str, object], str, bool] -class QAPIExpression(Dict[str, object]): +class QAPIExpression(Dict[str, Any]): # pylint: disable=too-few-public-methods def __init__(self, data: Mapping[str, object], From patchwork Thu Nov 16 01:43:47 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Snow X-Patchwork-Id: 13457507 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org 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 smtp.lore.kernel.org (Postfix) with ESMTPS id 829A6C2BB3F for ; Thu, 16 Nov 2023 01:48:44 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1r3RR2-0005R7-2B; Wed, 15 Nov 2023 20:44:48 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3RQd-0005NJ-4c for qemu-devel@nongnu.org; Wed, 15 Nov 2023 20:44:27 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3RQQ-00024d-5x for qemu-devel@nongnu.org; Wed, 15 Nov 2023 20:44:22 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1700099049; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=enrJLUtWlojyWtWCax6eEDvqVwZO3al6bjKNDT/LY3M=; b=jWB8IrsNH/VfOiLwMKTvjxBhBK4lrs1OPo7CjnjOBMs6ioq35QrrUjnJeWoH7IZyCC1E+d HLHVexvW1t99pIyt5GuQbBRSaMOwDWIi+r4u1n0bYYRBRg4SeTe6n6ovqGjF2LZWUOrTE4 CDsV4apFnXJM76gPscU2qUVan3ZAVY8= Received: from mimecast-mx02.redhat.com (mx-ext.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-94-6ebj--LxOZq-4dk1hs_XDA-1; Wed, 15 Nov 2023 20:43:57 -0500 X-MC-Unique: 6ebj--LxOZq-4dk1hs_XDA-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.rdu2.redhat.com [10.11.54.8]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id B1FFE3813F24; Thu, 16 Nov 2023 01:43:56 +0000 (UTC) Received: from scv.localdomain (unknown [10.22.32.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 6AB5CC15881; Thu, 16 Nov 2023 01:43:56 +0000 (UTC) From: John Snow To: qemu-devel@nongnu.org Cc: Peter Maydell , Michael Roth , Markus Armbruster , John Snow Subject: [PATCH 16/19] qapi/schema: add type hints Date: Wed, 15 Nov 2023 20:43:47 -0500 Message-ID: <20231116014350.653792-17-jsnow@redhat.com> In-Reply-To: <20231116014350.653792-1-jsnow@redhat.com> References: <20231116014350.653792-1-jsnow@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.8 Received-SPF: pass client-ip=170.10.133.124; envelope-from=jsnow@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -21 X-Spam_score: -2.2 X-Spam_bar: -- X-Spam_report: (-2.2 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.099, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org This patch only adds type hints, which aren't utilized at runtime and don't change the behavior of this module in any way. In a scant few locations, type hints are removed where no longer necessary due to inference power from typing all of the rest of creation; and any type hints that no longer need string quotes are changed. Signed-off-by: John Snow --- scripts/qapi/schema.py | 554 +++++++++++++++++++++++++++++------------ 1 file changed, 389 insertions(+), 165 deletions(-) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index ce5b01b3182..5d19b59def0 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -15,10 +15,20 @@ # TODO catching name collisions in generated code would be nice # pylint: disable=too-many-lines +from __future__ import annotations + from collections import OrderedDict import os import re -from typing import List, Optional, cast +from typing import ( + Any, + Callable, + Dict, + List, + Optional, + Union, + cast, +) from .common import ( POINTER_SUFFIX, @@ -30,39 +40,50 @@ ) from .error import QAPIError, QAPISemError, QAPISourceError from .expr import check_exprs -from .parser import QAPIExpression, QAPISchemaParser +from .parser import QAPIDoc, QAPIExpression, QAPISchemaParser +from .source import QAPISourceInfo class QAPISchemaIfCond: - def __init__(self, ifcond=None): + def __init__( + self, + ifcond: Optional[Union[str, Dict[str, object]]] = None, + ) -> None: self.ifcond = ifcond - def _cgen(self): + def _cgen(self) -> str: return cgen_ifcond(self.ifcond) - def gen_if(self): + def gen_if(self) -> str: return gen_if(self._cgen()) - def gen_endif(self): + def gen_endif(self) -> str: return gen_endif(self._cgen()) - def docgen(self): + def docgen(self) -> str: return docgen_ifcond(self.ifcond) - def is_present(self): + def is_present(self) -> bool: return bool(self.ifcond) class QAPISchemaEntity: - meta: Optional[str] = None + meta: str - def __init__(self, name: str, info, doc, ifcond=None, features=None): + def __init__( + self, + name: str, + info: Optional[QAPISourceInfo], + doc: Optional[QAPIDoc], + ifcond: Optional[QAPISchemaIfCond] = None, + features: Optional[List[QAPISchemaFeature]] = None, + ): assert name is None or isinstance(name, str) for f in features or []: assert isinstance(f, QAPISchemaFeature) f.set_defined_in(name) self.name = name - self._module = None + self._module: Optional[QAPISchemaModule] = None # For explicitly defined entities, info points to the (explicit) # definition. For builtins (and their arrays), info is None. # For implicitly defined entities, info points to a place that @@ -74,102 +95,162 @@ def __init__(self, name: str, info, doc, ifcond=None, features=None): self.features = features or [] self._checked = False - def __repr__(self): + def __repr__(self) -> str: if self.name is None: return "<%s at 0x%x>" % (type(self).__name__, id(self)) return "<%s:%s at 0x%x>" % (type(self).__name__, self.name, id(self)) - def c_name(self): + def c_name(self) -> str: return c_name(self.name) - def check(self, schema): + def check(self, schema: QAPISchema) -> None: # pylint: disable=unused-argument assert not self._checked - seen = {} + seen: Dict[str, QAPISchemaMember] = {} for f in self.features: f.check_clash(self.info, seen) self._checked = True - def connect_doc(self, doc=None): + def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None: doc = doc or self.doc if doc: for f in self.features: doc.connect_feature(f) - def check_doc(self): + def check_doc(self) -> None: if self.doc: self.doc.check() - def _set_module(self, schema, info): + def _set_module( + self, schema: QAPISchema, info: Optional[QAPISourceInfo] + ) -> None: assert self._checked fname = info.fname if info else QAPISchemaModule.BUILTIN_MODULE_NAME self._module = schema.module_by_fname(fname) self._module.add_entity(self) - def set_module(self, schema): + def set_module(self, schema: QAPISchema) -> None: self._set_module(schema, self.info) @property - def ifcond(self): + def ifcond(self) -> QAPISchemaIfCond: assert self._checked return self._ifcond - def is_implicit(self): + def is_implicit(self) -> bool: return not self.info - def visit(self, visitor): + def visit(self, visitor: QAPISchemaVisitor) -> None: # pylint: disable=unused-argument assert self._checked - def describe(self): + def describe(self) -> str: assert self.meta return "%s '%s'" % (self.meta, self.name) class QAPISchemaVisitor: - def visit_begin(self, schema): + def visit_begin(self, schema: QAPISchema) -> None: pass - def visit_end(self): + def visit_end(self) -> None: pass - def visit_module(self, name): + def visit_module(self, name: str) -> None: pass - def visit_needed(self, entity): + def visit_needed(self, entity: QAPISchemaEntity) -> bool: # pylint: disable=unused-argument # Default to visiting everything return True - def visit_include(self, name, info): + def visit_include(self, name: str, info: Optional[QAPISourceInfo]) -> None: pass - def visit_builtin_type(self, name, info, json_type): + def visit_builtin_type( + self, name: str, info: Optional[QAPISourceInfo], json_type: str + ) -> None: pass - def visit_enum_type(self, name, info, ifcond, features, members, prefix): + def visit_enum_type( + self, + name: str, + info: Optional[QAPISourceInfo], + ifcond: QAPISchemaIfCond, + features: List[QAPISchemaFeature], + members: List[QAPISchemaEnumMember], + prefix: Optional[str], + ) -> None: pass - def visit_array_type(self, name, info, ifcond, element_type): + def visit_array_type( + self, + name: str, + info: Optional[QAPISourceInfo], + ifcond: QAPISchemaIfCond, + element_type: QAPISchemaType, + ) -> None: pass - def visit_object_type(self, name, info, ifcond, features, - base, members, variants): + def visit_object_type( + self, + name: str, + info: Optional[QAPISourceInfo], + ifcond: QAPISchemaIfCond, + features: List[QAPISchemaFeature], + base: Optional[QAPISchemaObjectType], + members: List[QAPISchemaObjectTypeMember], + variants: Optional[QAPISchemaVariants], + ) -> None: pass - def visit_object_type_flat(self, name, info, ifcond, features, - members, variants): + def visit_object_type_flat( + self, + name: str, + info: Optional[QAPISourceInfo], + ifcond: QAPISchemaIfCond, + features: List[QAPISchemaFeature], + members: List[QAPISchemaObjectTypeMember], + variants: Optional[QAPISchemaVariants], + ) -> None: pass - def visit_alternate_type(self, name, info, ifcond, features, variants): + def visit_alternate_type( + self, + name: str, + info: Optional[QAPISourceInfo], + ifcond: QAPISchemaIfCond, + features: List[QAPISchemaFeature], + variants: QAPISchemaVariants, + ) -> None: pass - def visit_command(self, name, info, ifcond, features, - arg_type, ret_type, gen, success_response, boxed, - allow_oob, allow_preconfig, coroutine): + def visit_command( + self, + name: str, + info: Optional[QAPISourceInfo], + ifcond: QAPISchemaIfCond, + features: List[QAPISchemaFeature], + arg_type: Optional[QAPISchemaObjectType], + ret_type: Optional[QAPISchemaType], + gen: bool, + success_response: bool, + boxed: bool, + allow_oob: bool, + allow_preconfig: bool, + coroutine: bool, + ) -> None: pass - def visit_event(self, name, info, ifcond, features, arg_type, boxed): + def visit_event( + self, + name: str, + info: Optional[QAPISourceInfo], + ifcond: QAPISchemaIfCond, + features: List[QAPISchemaFeature], + arg_type: Optional[QAPISchemaObjectType], + boxed: bool, + ) -> None: pass @@ -177,9 +258,9 @@ class QAPISchemaModule: BUILTIN_MODULE_NAME = './builtin' - def __init__(self, name): + def __init__(self, name: str): self.name = name - self._entity_list = [] + self._entity_list: List[QAPISchemaEntity] = [] @staticmethod def is_system_module(name: str) -> bool: @@ -208,10 +289,10 @@ def is_builtin_module(cls, name: str) -> bool: """ return name == cls.BUILTIN_MODULE_NAME - def add_entity(self, ent): + def add_entity(self, ent: QAPISchemaEntity) -> None: self._entity_list.append(ent) - def visit(self, visitor): + def visit(self, visitor: QAPISchemaVisitor) -> None: visitor.visit_module(self.name) for entity in self._entity_list: if visitor.visit_needed(entity): @@ -219,13 +300,13 @@ def visit(self, visitor): class QAPISchemaInclude(QAPISchemaEntity): - def __init__(self, sub_module, info): + def __init__(self, sub_module: QAPISchemaModule, info: QAPISourceInfo): # Includes are internal entity objects; and may occur multiple times name = f"q_include_{info.fname}:{info.line}" super().__init__(name, info, None) self._sub_module = sub_module - def visit(self, visitor): + def visit(self, visitor: QAPISchemaVisitor) -> None: super().visit(visitor) visitor.visit_include(self._sub_module.name, self.info) @@ -237,17 +318,17 @@ def c_type(self) -> str: raise NotImplementedError() # Return the C type to be used in a parameter list. - def c_param_type(self): + def c_param_type(self) -> str: return self.c_type() # Return the C type to be used where we suppress boxing. - def c_unboxed_type(self): + def c_unboxed_type(self) -> str: return self.c_type() def json_type(self) -> str: raise NotImplementedError() - def alternate_qtype(self): + def alternate_qtype(self) -> Optional[str]: json2qtype = { 'null': 'QTYPE_QNULL', 'string': 'QTYPE_QSTRING', @@ -259,17 +340,17 @@ def alternate_qtype(self): } return json2qtype.get(self.json_type()) - def doc_type(self): + def doc_type(self) -> Optional[str]: if self.is_implicit(): return None return self.name - def need_has_if_optional(self): + def need_has_if_optional(self) -> bool: # When FOO is a pointer, has_FOO == !!FOO, i.e. has_FOO is redundant. # Except for arrays; see QAPISchemaArrayType.need_has_if_optional(). return not self.c_type().endswith(POINTER_SUFFIX) - def check(self, schema): + def check(self, schema: QAPISchema) -> None: super().check(schema) for feat in self.features: if feat.is_special(): @@ -277,7 +358,7 @@ def check(self, schema): self.info, f"feature '{feat.name}' is not supported for types") - def describe(self): + def describe(self) -> str: assert self.meta return "%s type '%s'" % (self.meta, self.name) @@ -285,7 +366,7 @@ def describe(self): class QAPISchemaBuiltinType(QAPISchemaType): meta = 'built-in' - def __init__(self, name, json_type, c_type): + def __init__(self, name: str, json_type: str, c_type: str): super().__init__(name, None, None) assert not c_type or isinstance(c_type, str) assert json_type in ('string', 'number', 'int', 'boolean', 'null', @@ -293,24 +374,24 @@ def __init__(self, name, json_type, c_type): self._json_type_name = json_type self._c_type_name = c_type - def c_name(self): + def c_name(self) -> str: return self.name - def c_type(self): + def c_type(self) -> str: return self._c_type_name - def c_param_type(self): + def c_param_type(self) -> str: if self.name == 'str': return 'const ' + self._c_type_name return self._c_type_name - def json_type(self): + def json_type(self) -> str: return self._json_type_name - def doc_type(self): + def doc_type(self) -> str: return self.json_type() - def visit(self, visitor): + def visit(self, visitor: QAPISchemaVisitor) -> None: super().visit(visitor) visitor.visit_builtin_type(self.name, self.info, self.json_type()) @@ -318,7 +399,16 @@ def visit(self, visitor): class QAPISchemaEnumType(QAPISchemaType): meta = 'enum' - def __init__(self, name, info, doc, ifcond, features, members, prefix): + def __init__( + self, + name: str, + info: Optional[QAPISourceInfo], + doc: Optional[QAPIDoc], + ifcond: Optional[QAPISchemaIfCond], + features: Optional[List[QAPISchemaFeature]], + members: List[QAPISchemaEnumMember], + prefix: Optional[str], + ): super().__init__(name, info, doc, ifcond, features) for m in members: assert isinstance(m, QAPISchemaEnumMember) @@ -327,32 +417,32 @@ def __init__(self, name, info, doc, ifcond, features, members, prefix): self.members = members self.prefix = prefix - def check(self, schema): + def check(self, schema: QAPISchema) -> None: super().check(schema) - seen = {} + seen: Dict[str, QAPISchemaMember] = {} for m in self.members: m.check_clash(self.info, seen) - def connect_doc(self, doc=None): + def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None: super().connect_doc(doc) doc = doc or self.doc for m in self.members: m.connect_doc(doc) - def is_implicit(self): + def is_implicit(self) -> bool: # See QAPISchema._def_predefineds() return self.name == 'QType' - def c_type(self): + def c_type(self) -> str: return c_name(self.name) - def member_names(self): + def member_names(self) -> List[str]: return [m.name for m in self.members] - def json_type(self): + def json_type(self) -> str: return 'string' - def visit(self, visitor): + def visit(self, visitor: QAPISchemaVisitor) -> None: super().visit(visitor) visitor.visit_enum_type( self.name, self.info, self.ifcond, self.features, @@ -362,7 +452,9 @@ def visit(self, visitor): class QAPISchemaArrayType(QAPISchemaType): meta = 'array' - def __init__(self, name, info, element_type): + def __init__( + self, name: str, info: Optional[QAPISourceInfo], element_type: str + ): super().__init__(name, info, None) assert isinstance(element_type, str) self._element_type_name = element_type @@ -377,12 +469,12 @@ def element_type(self) -> QAPISchemaType: ) return self._element_type - def need_has_if_optional(self): + def need_has_if_optional(self) -> bool: # When FOO is an array, we still need has_FOO to distinguish # absent (!has_FOO) from present and empty (has_FOO && !FOO). return True - def check(self, schema): + def check(self, schema: QAPISchema) -> None: super().check(schema) if self.info: @@ -396,42 +488,51 @@ def check(self, schema): ) assert not isinstance(self.element_type, QAPISchemaArrayType) - def set_module(self, schema): + def set_module(self, schema: QAPISchema) -> None: self._set_module(schema, self.element_type.info) @property - def ifcond(self): + def ifcond(self) -> QAPISchemaIfCond: assert self._checked return self.element_type.ifcond - def is_implicit(self): + def is_implicit(self) -> bool: return True - def c_type(self): + def c_type(self) -> str: return c_name(self.name) + POINTER_SUFFIX - def json_type(self): + def json_type(self) -> str: return 'array' - def doc_type(self): + def doc_type(self) -> Optional[str]: elt_doc_type = self.element_type.doc_type() if not elt_doc_type: return None return 'array of ' + elt_doc_type - def visit(self, visitor): + def visit(self, visitor: QAPISchemaVisitor) -> None: super().visit(visitor) visitor.visit_array_type(self.name, self.info, self.ifcond, self.element_type) - def describe(self): + def describe(self) -> str: assert self.meta return "%s type ['%s']" % (self.meta, self._element_type_name) class QAPISchemaObjectType(QAPISchemaType): - def __init__(self, name, info, doc, ifcond, features, - base, local_members, variants): + def __init__( + self, + name: str, + info: Optional[QAPISourceInfo], + doc: Optional[QAPIDoc], + ifcond: Optional[QAPISchemaIfCond], + features: Optional[List[QAPISchemaFeature]], + base: Optional[str], + local_members: List[QAPISchemaObjectTypeMember], + variants: Optional[QAPISchemaVariants], + ): # struct has local_members, optional base, and no variants # union has base, variants, and no local_members super().__init__(name, info, doc, ifcond, features) @@ -450,7 +551,7 @@ def __init__(self, name, info, doc, ifcond, features, self.members: List[QAPISchemaObjectTypeMember] = [] self._checking = False - def check(self, schema): + def check(self, schema: QAPISchema) -> None: # This calls another type T's .check() exactly when the C # struct emitted by gen_object() contains that T's C struct # (pointers don't count). @@ -496,14 +597,18 @@ def check(self, schema): # Check that the members of this type do not cause duplicate JSON members, # and update seen to track the members seen so far. Report any errors # on behalf of info, which is not necessarily self.info - def check_clash(self, info, seen): + def check_clash( + self, + info: Optional[QAPISourceInfo], + seen: Dict[str, QAPISchemaMember], + ) -> None: assert self._checked for m in self.members: m.check_clash(info, seen) if self.variants: self.variants.check_clash(info, seen) - def connect_doc(self, doc=None): + def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None: super().connect_doc(doc) doc = doc or self.doc if self.base and self.base.is_implicit(): @@ -511,34 +616,34 @@ def connect_doc(self, doc=None): for m in self.local_members: m.connect_doc(doc) - def is_implicit(self): + def is_implicit(self) -> bool: # See QAPISchema._make_implicit_object_type(), as well as # _def_predefineds() return self.name.startswith('q_') - def is_empty(self): + def is_empty(self) -> bool: assert self.members is not None return not self.members and not self.variants - def has_conditional_members(self): + def has_conditional_members(self) -> bool: assert self.members is not None return any(m.ifcond.is_present() for m in self.members) - def c_name(self): + def c_name(self) -> str: assert self.name != 'q_empty' return super().c_name() - def c_type(self): + def c_type(self) -> str: assert not self.is_implicit() return c_name(self.name) + POINTER_SUFFIX - def c_unboxed_type(self): + def c_unboxed_type(self) -> str: return c_name(self.name) - def json_type(self): + def json_type(self) -> str: return 'object' - def visit(self, visitor): + def visit(self, visitor: QAPISchemaVisitor) -> None: super().visit(visitor) visitor.visit_object_type( self.name, self.info, self.ifcond, self.features, @@ -551,7 +656,15 @@ def visit(self, visitor): class QAPISchemaAlternateType(QAPISchemaType): meta = 'alternate' - def __init__(self, name, info, doc, ifcond, features, variants): + def __init__( + self, + name: str, + info: QAPISourceInfo, + doc: Optional[QAPIDoc], + ifcond: Optional[QAPISchemaIfCond], + features: List[QAPISchemaFeature], + variants: QAPISchemaVariants, + ): super().__init__(name, info, doc, ifcond, features) assert isinstance(variants, QAPISchemaVariants) assert variants.tag_member @@ -559,7 +672,7 @@ def __init__(self, name, info, doc, ifcond, features, variants): variants.tag_member.set_defined_in(self.name) self.variants = variants - def check(self, schema): + def check(self, schema: QAPISchema) -> None: super().check(schema) self.variants.tag_member.check(schema) # Not calling self.variants.check_clash(), because there's nothing @@ -567,8 +680,8 @@ def check(self, schema): self.variants.check(schema, {}) # Alternate branch names have no relation to the tag enum values; # so we have to check for potential name collisions ourselves. - seen = {} - types_seen = {} + seen: Dict[str, QAPISchemaMember] = {} + types_seen: Dict[str, str] = {} for v in self.variants.variants: v.check_clash(self.info, seen) qtype = v.type.alternate_qtype() @@ -597,26 +710,32 @@ def check(self, schema): % (v.describe(self.info), types_seen[qt])) types_seen[qt] = v.name - def connect_doc(self, doc=None): + def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None: super().connect_doc(doc) doc = doc or self.doc for v in self.variants.variants: v.connect_doc(doc) - def c_type(self): + def c_type(self) -> str: return c_name(self.name) + POINTER_SUFFIX - def json_type(self): + def json_type(self) -> str: return 'value' - def visit(self, visitor): + def visit(self, visitor: QAPISchemaVisitor) -> None: super().visit(visitor) visitor.visit_alternate_type( self.name, self.info, self.ifcond, self.features, self.variants) class QAPISchemaVariants: - def __init__(self, tag_name, info, tag_member, variants): + def __init__( + self, + tag_name: Optional[str], + info: QAPISourceInfo, + tag_member: Optional[QAPISchemaObjectTypeMember], + variants: List[QAPISchemaVariant], + ): # Unions pass tag_name but not tag_member. # Alternates pass tag_member but not tag_name. # After check(), tag_member is always set. @@ -627,11 +746,11 @@ def __init__(self, tag_name, info, tag_member, variants): assert isinstance(v, QAPISchemaVariant) self._tag_name = tag_name self.info = info - self._tag_member: Optional[QAPISchemaObjectTypeMember] = tag_member + self._tag_member = tag_member self.variants = variants @property - def tag_member(self) -> 'QAPISchemaObjectTypeMember': + def tag_member(self) -> QAPISchemaObjectTypeMember: if self._tag_member is None: raise RuntimeError( "QAPISchemaVariants has no tag_member property until " @@ -639,11 +758,13 @@ def tag_member(self) -> 'QAPISchemaObjectTypeMember': ) return self._tag_member - def set_defined_in(self, name): + def set_defined_in(self, name: str) -> None: for v in self.variants: v.set_defined_in(name) - def check(self, schema, seen): + def check( + self, schema: QAPISchema, seen: Dict[str, QAPISchemaMember] + ) -> None: if self._tag_name: # union # We need to narrow the member type: tmp = seen.get(c_name(self._tag_name)) @@ -713,7 +834,11 @@ def check(self, schema, seen): % (v.describe(self.info), v.type.describe())) v.type.check(schema) - def check_clash(self, info, seen): + def check_clash( + self, + info: Optional[QAPISourceInfo], + seen: Dict[str, QAPISchemaMember], + ) -> None: for v in self.variants: # Reset seen map for each variant, since qapi names from one # branch do not affect another branch @@ -725,18 +850,27 @@ class QAPISchemaMember: """ Represents object members, enum members and features """ role = 'member' - def __init__(self, name, info, ifcond=None): + def __init__( + self, + name: str, + info: Optional[QAPISourceInfo], + ifcond: Optional[QAPISchemaIfCond] = None, + ): assert isinstance(name, str) self.name = name self.info = info self.ifcond = ifcond or QAPISchemaIfCond() - self.defined_in = None + self.defined_in: Optional[str] = None - def set_defined_in(self, name): + def set_defined_in(self, name: str) -> None: assert not self.defined_in self.defined_in = name - def check_clash(self, info, seen): + def check_clash( + self, + info: Optional[QAPISourceInfo], + seen: Dict[str, QAPISchemaMember], + ) -> None: cname = c_name(self.name) if cname in seen: raise QAPISemError( @@ -745,11 +879,11 @@ def check_clash(self, info, seen): % (self.describe(info), seen[cname].describe(info))) seen[cname] = self - def connect_doc(self, doc): + def connect_doc(self, doc: Optional[QAPIDoc]) -> None: if doc: doc.connect_member(self) - def describe(self, info): + def describe(self, info: Optional[QAPISourceInfo]) -> str: role = self.role meta = 'type' defined_in = self.defined_in @@ -781,14 +915,20 @@ def describe(self, info): class QAPISchemaEnumMember(QAPISchemaMember): role = 'value' - def __init__(self, name, info, ifcond=None, features=None): + def __init__( + self, + name: str, + info: Optional[QAPISourceInfo], + ifcond: Optional[QAPISchemaIfCond] = None, + features: Optional[List[QAPISchemaFeature]] = None, + ): super().__init__(name, info, ifcond) for f in features or []: assert isinstance(f, QAPISchemaFeature) f.set_defined_in(name) self.features = features or [] - def connect_doc(self, doc): + def connect_doc(self, doc: Optional[QAPIDoc]) -> None: super().connect_doc(doc) if doc: for f in self.features: @@ -798,12 +938,20 @@ def connect_doc(self, doc): class QAPISchemaFeature(QAPISchemaMember): role = 'feature' - def is_special(self): + def is_special(self) -> bool: return self.name in ('deprecated', 'unstable') class QAPISchemaObjectTypeMember(QAPISchemaMember): - def __init__(self, name, info, typ, optional, ifcond=None, features=None): + def __init__( + self, + name: str, + info: QAPISourceInfo, + typ: str, + optional: bool, + ifcond: Optional[QAPISchemaIfCond] = None, + features: Optional[List[QAPISchemaFeature]] = None, + ): super().__init__(name, info, ifcond) assert isinstance(typ, str) assert isinstance(optional, bool) @@ -815,19 +963,19 @@ def __init__(self, name, info, typ, optional, ifcond=None, features=None): self.optional = optional self.features = features or [] - def need_has(self): + def need_has(self) -> bool: assert self.type return self.optional and self.type.need_has_if_optional() - def check(self, schema): + def check(self, schema: QAPISchema) -> None: assert self.defined_in self.type = schema.resolve_type(self._type_name, self.info, self.describe) - seen = {} + seen: Dict[str, QAPISchemaMember] = {} for f in self.features: f.check_clash(self.info, seen) - def connect_doc(self, doc): + def connect_doc(self, doc: Optional[QAPIDoc]) -> None: super().connect_doc(doc) if doc: for f in self.features: @@ -837,24 +985,42 @@ def connect_doc(self, doc): class QAPISchemaVariant(QAPISchemaObjectTypeMember): role = 'branch' - def __init__(self, name, info, typ, ifcond=None): + def __init__( + self, + name: str, + info: QAPISourceInfo, + typ: str, + ifcond: QAPISchemaIfCond, + ): super().__init__(name, info, typ, False, ifcond) class QAPISchemaCommand(QAPISchemaEntity): meta = 'command' - def __init__(self, name, info, doc, ifcond, features, - arg_type, ret_type, - gen, success_response, boxed, allow_oob, allow_preconfig, - coroutine): + def __init__( + self, + name: str, + info: QAPISourceInfo, + doc: Optional[QAPIDoc], + ifcond: QAPISchemaIfCond, + features: List[QAPISchemaFeature], + arg_type: Optional[str], + ret_type: Optional[str], + gen: bool, + success_response: bool, + boxed: bool, + allow_oob: bool, + allow_preconfig: bool, + coroutine: bool, + ): super().__init__(name, info, doc, ifcond, features) assert not arg_type or isinstance(arg_type, str) assert not ret_type or isinstance(ret_type, str) self._arg_type_name = arg_type - self.arg_type = None + self.arg_type: Optional[QAPISchemaObjectType] = None self._ret_type_name = ret_type - self.ret_type = None + self.ret_type: Optional[QAPISchemaType] = None self.gen = gen self.success_response = success_response self.boxed = boxed @@ -862,7 +1028,7 @@ def __init__(self, name, info, doc, ifcond, features, self.allow_preconfig = allow_preconfig self.coroutine = coroutine - def check(self, schema): + def check(self, schema: QAPISchema) -> None: assert self.info is not None super().check(schema) if self._arg_type_name: @@ -897,14 +1063,14 @@ def check(self, schema): "command's 'returns' cannot take %s" % self.ret_type.describe()) - def connect_doc(self, doc=None): + def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None: super().connect_doc(doc) doc = doc or self.doc if doc: if self.arg_type and self.arg_type.is_implicit(): self.arg_type.connect_doc(doc) - def visit(self, visitor): + def visit(self, visitor: QAPISchemaVisitor) -> None: super().visit(visitor) visitor.visit_command( self.name, self.info, self.ifcond, self.features, @@ -916,14 +1082,23 @@ def visit(self, visitor): class QAPISchemaEvent(QAPISchemaEntity): meta = 'event' - def __init__(self, name, info, doc, ifcond, features, arg_type, boxed): + def __init__( + self, + name: str, + info: QAPISourceInfo, + doc: Optional[QAPIDoc], + ifcond: QAPISchemaIfCond, + features: List[QAPISchemaFeature], + arg_type: Optional[str], + boxed: bool, + ): super().__init__(name, info, doc, ifcond, features) assert not arg_type or isinstance(arg_type, str) self._arg_type_name = arg_type - self.arg_type = None + self.arg_type: Optional[QAPISchemaObjectType] = None self.boxed = boxed - def check(self, schema): + def check(self, schema: QAPISchema) -> None: super().check(schema) if self._arg_type_name: typ = schema.resolve_type( @@ -945,14 +1120,14 @@ def check(self, schema): self.info, "conditional event arguments require 'boxed': true") - def connect_doc(self, doc=None): + def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None: super().connect_doc(doc) doc = doc or self.doc if doc: if self.arg_type and self.arg_type.is_implicit(): self.arg_type.connect_doc(doc) - def visit(self, visitor): + def visit(self, visitor: QAPISchemaVisitor) -> None: super().visit(visitor) visitor.visit_event( self.name, self.info, self.ifcond, self.features, @@ -960,7 +1135,7 @@ def visit(self, visitor): class QAPISchema: - def __init__(self, fname): + def __init__(self, fname: str): self.fname = fname try: @@ -972,9 +1147,9 @@ def __init__(self, fname): exprs = check_exprs(parser.exprs) self.docs = parser.docs - self._entity_list = [] - self._entity_dict = {} - self._module_dict = OrderedDict() + self._entity_list: List[QAPISchemaEntity] = [] + self._entity_dict: Dict[str, QAPISchemaEntity] = {} + self._module_dict: Dict[str, QAPISchemaModule] = OrderedDict() self._schema_dir = os.path.dirname(fname) self._make_module(QAPISchemaModule.BUILTIN_MODULE_NAME) self._make_module(fname) @@ -984,7 +1159,7 @@ def __init__(self, fname): self._def_exprs(exprs) self.check() - def _def_entity(self, ent): + def _def_entity(self, ent: QAPISchemaEntity) -> None: # Only the predefined types are allowed to not have info assert ent.info or self._predefining self._entity_list.append(ent) @@ -1003,7 +1178,11 @@ def _def_entity(self, ent): ent.info, "%s is already defined" % other_ent.describe()) self._entity_dict[ent.name] = ent - def lookup_entity(self, name, typ=None): + def lookup_entity( + self, + name: str, + typ: Optional[type] = None, + ) -> Optional[QAPISchemaEntity]: ent = self._entity_dict.get(name) if typ and not isinstance(ent, typ): return None @@ -1016,7 +1195,12 @@ def lookup_type(self, name: str) -> Optional[QAPISchemaType]: assert isinstance(typ, QAPISchemaType) return typ - def resolve_type(self, name, info, what): + def resolve_type( + self, + name: str, + info: Optional[QAPISourceInfo], + what: Union[str, Callable[[Optional[QAPISourceInfo]], str]], + ) -> QAPISchemaType: typ = self.lookup_type(name) if not typ: if callable(what): @@ -1030,23 +1214,25 @@ def _module_name(self, fname: str) -> str: return fname return os.path.relpath(fname, self._schema_dir) - def _make_module(self, fname): + def _make_module(self, fname: str) -> QAPISchemaModule: name = self._module_name(fname) if name not in self._module_dict: self._module_dict[name] = QAPISchemaModule(name) return self._module_dict[name] - def module_by_fname(self, fname): + def module_by_fname(self, fname: str) -> QAPISchemaModule: name = self._module_name(fname) return self._module_dict[name] - def _def_include(self, expr: QAPIExpression): + def _def_include(self, expr: QAPIExpression) -> None: include = expr['include'] assert expr.doc is None self._def_entity( QAPISchemaInclude(self._make_module(include), expr.info)) - def _def_builtin_type(self, name, json_type, c_type): + def _def_builtin_type( + self, name: str, json_type: str, c_type: str + ) -> None: self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type)) # Instantiating only the arrays that are actually used would # be nice, but we can't as long as their generated code @@ -1054,7 +1240,7 @@ def _def_builtin_type(self, name, json_type, c_type): # schema. self._make_array_type(name, None) - def _def_predefineds(self): + def _def_predefineds(self) -> None: for t in [('str', 'string', 'char' + POINTER_SUFFIX), ('number', 'number', 'double'), ('int', 'int', 'int64_t'), @@ -1083,30 +1269,51 @@ def _def_predefineds(self): self._def_entity(QAPISchemaEnumType('QType', None, None, None, None, qtype_values, 'QTYPE')) - def _make_features(self, features, info): + def _make_features( + self, + features: Optional[List[Dict[str, Any]]], + info: Optional[QAPISourceInfo], + ) -> List[QAPISchemaFeature]: if features is None: return [] return [QAPISchemaFeature(f['name'], info, QAPISchemaIfCond(f.get('if'))) for f in features] - def _make_enum_member(self, name, ifcond, features, info): + def _make_enum_member( + self, + name: str, + ifcond: Optional[Union[str, Dict[str, Any]]], + features: Optional[List[Dict[str, Any]]], + info: Optional[QAPISourceInfo], + ) -> QAPISchemaEnumMember: return QAPISchemaEnumMember(name, info, QAPISchemaIfCond(ifcond), self._make_features(features, info)) - def _make_enum_members(self, values, info): + def _make_enum_members( + self, values: List[Dict[str, Any]], info: Optional[QAPISourceInfo] + ) -> List[QAPISchemaEnumMember]: return [self._make_enum_member(v['name'], v.get('if'), v.get('features'), info) for v in values] - def _make_array_type(self, element_type, info): + def _make_array_type( + self, element_type: str, info: Optional[QAPISourceInfo] + ) -> str: name = element_type + 'List' # reserved by check_defn_name_str() if not self.lookup_type(name): self._def_entity(QAPISchemaArrayType(name, info, element_type)) return name - def _make_implicit_object_type(self, name, info, ifcond, role, members): + def _make_implicit_object_type( + self, + name: str, + info: QAPISourceInfo, + ifcond: QAPISchemaIfCond, + role: str, + members: List[QAPISchemaObjectTypeMember], + ) -> Optional[str]: if not members: return None # See also QAPISchemaObjectTypeMember.describe() @@ -1122,7 +1329,7 @@ def _make_implicit_object_type(self, name, info, ifcond, role, members): name, info, None, ifcond, None, None, members, None)) return name - def _def_enum_type(self, expr: QAPIExpression): + def _def_enum_type(self, expr: QAPIExpression) -> None: name = expr['enum'] data = expr['data'] prefix = expr.get('prefix') @@ -1133,7 +1340,14 @@ def _def_enum_type(self, expr: QAPIExpression): name, info, expr.doc, ifcond, features, self._make_enum_members(data, info), prefix)) - def _make_member(self, name, typ, ifcond, features, info): + def _make_member( + self, + name: str, + typ: Union[List[str], str], + ifcond: QAPISchemaIfCond, + features: Optional[List[Dict[str, Any]]], + info: QAPISourceInfo, + ) -> QAPISchemaObjectTypeMember: optional = False if name.startswith('*'): name = name[1:] @@ -1144,13 +1358,17 @@ def _make_member(self, name, typ, ifcond, features, info): return QAPISchemaObjectTypeMember(name, info, typ, optional, ifcond, self._make_features(features, info)) - def _make_members(self, data, info): + def _make_members( + self, + data: Dict[str, Any], + info: QAPISourceInfo, + ) -> List[QAPISchemaObjectTypeMember]: return [self._make_member(key, value['type'], QAPISchemaIfCond(value.get('if')), value.get('features'), info) for (key, value) in data.items()] - def _def_struct_type(self, expr: QAPIExpression): + def _def_struct_type(self, expr: QAPIExpression) -> None: name = expr['struct'] base = expr.get('base') data = expr['data'] @@ -1162,13 +1380,19 @@ def _def_struct_type(self, expr: QAPIExpression): self._make_members(data, info), None)) - def _make_variant(self, case, typ, ifcond, info): + def _make_variant( + self, + case: str, + typ: str, + ifcond: QAPISchemaIfCond, + info: QAPISourceInfo, + ) -> QAPISchemaVariant: if isinstance(typ, list): assert len(typ) == 1 typ = self._make_array_type(typ[0], info) return QAPISchemaVariant(case, info, typ, ifcond) - def _def_union_type(self, expr: QAPIExpression): + def _def_union_type(self, expr: QAPIExpression) -> None: name = expr['union'] base = expr['base'] tag_name = expr['discriminator'] @@ -1193,7 +1417,7 @@ def _def_union_type(self, expr: QAPIExpression): QAPISchemaVariants( tag_name, info, None, variants))) - def _def_alternate_type(self, expr: QAPIExpression): + def _def_alternate_type(self, expr: QAPIExpression) -> None: name = expr['alternate'] data = expr['data'] assert isinstance(data, dict) @@ -1211,7 +1435,7 @@ def _def_alternate_type(self, expr: QAPIExpression): name, info, expr.doc, ifcond, features, QAPISchemaVariants(None, info, tag_member, variants))) - def _def_command(self, expr: QAPIExpression): + def _def_command(self, expr: QAPIExpression) -> None: name = expr['command'] data = expr.get('data') rets = expr.get('returns') @@ -1237,7 +1461,7 @@ def _def_command(self, expr: QAPIExpression): boxed, allow_oob, allow_preconfig, coroutine)) - def _def_event(self, expr: QAPIExpression): + def _def_event(self, expr: QAPIExpression) -> None: name = expr['event'] data = expr.get('data') boxed = expr.get('boxed', False) @@ -1251,7 +1475,7 @@ def _def_event(self, expr: QAPIExpression): self._def_entity(QAPISchemaEvent(name, info, expr.doc, ifcond, features, data, boxed)) - def _def_exprs(self, exprs): + def _def_exprs(self, exprs: List[QAPIExpression]) -> None: for expr in exprs: if 'enum' in expr: self._def_enum_type(expr) @@ -1270,7 +1494,7 @@ def _def_exprs(self, exprs): else: assert False - def check(self): + def check(self) -> None: for ent in self._entity_list: ent.check(self) ent.connect_doc() @@ -1278,7 +1502,7 @@ def check(self): for ent in self._entity_list: ent.set_module(self) - def visit(self, visitor): + def visit(self, visitor: QAPISchemaVisitor) -> None: visitor.visit_begin(self) for mod in self._module_dict.values(): mod.visit(visitor) From patchwork Thu Nov 16 01:43:48 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Snow X-Patchwork-Id: 13457491 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org 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 smtp.lore.kernel.org (Postfix) with ESMTPS id 1EB38C2BB3F for ; Thu, 16 Nov 2023 01:45:16 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1r3RQm-0005OO-IE; Wed, 15 Nov 2023 20:44:32 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3RQL-0005JE-T6 for qemu-devel@nongnu.org; Wed, 15 Nov 2023 20:44:06 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3RQI-00022o-RB for qemu-devel@nongnu.org; Wed, 15 Nov 2023 20:44:05 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1700099042; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Z9l+KTbDrQrCxNh54ZdZId0hTUVU4MQfBShpl+9MLrg=; b=euKRHXU16V1A6H4MdaK/c94+VpkE1pTu0sgSzUTCYkTWdC0nMj5oKFpwPQyEPolHEjvM9M RXQmPKi0CXGY0/oL00F59pi+hmXnVQLLhE2m46zo35leexKodDmqCQQX8AYDpeg0ZV73xx SFDimc8Jtf9iN3RjtWlWpwyBmtz1N/I= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-219-YvlO2c8NMzmPIpNNZfl4OA-1; Wed, 15 Nov 2023 20:43:57 -0500 X-MC-Unique: YvlO2c8NMzmPIpNNZfl4OA-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.rdu2.redhat.com [10.11.54.8]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 1308B811E7B; Thu, 16 Nov 2023 01:43:57 +0000 (UTC) Received: from scv.localdomain (unknown [10.22.32.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id C4984C15881; Thu, 16 Nov 2023 01:43:56 +0000 (UTC) From: John Snow To: qemu-devel@nongnu.org Cc: Peter Maydell , Michael Roth , Markus Armbruster , John Snow Subject: [PATCH 17/19] qapi/schema: turn on mypy strictness Date: Wed, 15 Nov 2023 20:43:48 -0500 Message-ID: <20231116014350.653792-18-jsnow@redhat.com> In-Reply-To: <20231116014350.653792-1-jsnow@redhat.com> References: <20231116014350.653792-1-jsnow@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.8 Received-SPF: pass client-ip=170.10.129.124; envelope-from=jsnow@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -21 X-Spam_score: -2.2 X-Spam_bar: -- X-Spam_report: (-2.2 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.099, 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_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org This patch can be rolled in with the previous one once the series is ready for merge, but for work-in-progress' sake, it's separate here. Signed-off-by: John Snow --- scripts/qapi/mypy.ini | 5 ----- 1 file changed, 5 deletions(-) diff --git a/scripts/qapi/mypy.ini b/scripts/qapi/mypy.ini index 56e0dfb1327..8109470a031 100644 --- a/scripts/qapi/mypy.ini +++ b/scripts/qapi/mypy.ini @@ -2,8 +2,3 @@ strict = True disallow_untyped_calls = False python_version = 3.8 - -[mypy-qapi.schema] -disallow_untyped_defs = False -disallow_incomplete_defs = False -check_untyped_defs = False From patchwork Thu Nov 16 01:43:49 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Snow X-Patchwork-Id: 13457506 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org 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 smtp.lore.kernel.org (Postfix) with ESMTPS id C7DBBC072A2 for ; Thu, 16 Nov 2023 01:48:37 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1r3RQo-0005Oi-JH; Wed, 15 Nov 2023 20:44:35 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3RQO-0005KQ-Ue for qemu-devel@nongnu.org; Wed, 15 Nov 2023 20:44:09 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3RQI-00022N-3H for qemu-devel@nongnu.org; Wed, 15 Nov 2023 20:44:08 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1700099041; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Y6FJzfXk8Xh4RUkNFX52+G4sAFZOc9b8NvO/oKrBAZA=; b=eGnWob7t+ZeurHioi3gXU7LZ1HZNSNR8piIw4i5MQ+0XvMIS/yTPN4/oXpNkimlLGvdCk3 pJ5D2+2ysUid/j0LOx4TvSebqKg7xuZGnuNX9p7RV9hQ7U9+TMQidDpa7kVsNVUTBMjrTr zQVYt5e6CaYXI8i6KC25yD3X1AjV08Y= Received: from mimecast-mx02.redhat.com (mx-ext.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-107-2KPuCeexMNS0BXGCVs0OyQ-1; Wed, 15 Nov 2023 20:43:57 -0500 X-MC-Unique: 2KPuCeexMNS0BXGCVs0OyQ-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.rdu2.redhat.com [10.11.54.8]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 66B143C11CC3; Thu, 16 Nov 2023 01:43:57 +0000 (UTC) Received: from scv.localdomain (unknown [10.22.32.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 264B7C15881; Thu, 16 Nov 2023 01:43:57 +0000 (UTC) From: John Snow To: qemu-devel@nongnu.org Cc: Peter Maydell , Michael Roth , Markus Armbruster , John Snow Subject: [PATCH 18/19] qapi/schema: remove unnecessary asserts Date: Wed, 15 Nov 2023 20:43:49 -0500 Message-ID: <20231116014350.653792-19-jsnow@redhat.com> In-Reply-To: <20231116014350.653792-1-jsnow@redhat.com> References: <20231116014350.653792-1-jsnow@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.8 Received-SPF: pass client-ip=170.10.129.124; envelope-from=jsnow@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -21 X-Spam_score: -2.2 X-Spam_bar: -- X-Spam_report: (-2.2 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.099, 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_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org With strict typing enabled, these runtime statements aren't necessary anymore. Signed-off-by: John Snow Reviewed-by: Markus Armbruster --- scripts/qapi/schema.py | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 5d19b59def0..b5f377e68b8 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -78,9 +78,7 @@ def __init__( ifcond: Optional[QAPISchemaIfCond] = None, features: Optional[List[QAPISchemaFeature]] = None, ): - assert name is None or isinstance(name, str) for f in features or []: - assert isinstance(f, QAPISchemaFeature) f.set_defined_in(name) self.name = name self._module: Optional[QAPISchemaModule] = None @@ -145,7 +143,6 @@ def visit(self, visitor: QAPISchemaVisitor) -> None: assert self._checked def describe(self) -> str: - assert self.meta return "%s '%s'" % (self.meta, self.name) @@ -359,7 +356,6 @@ def check(self, schema: QAPISchema) -> None: f"feature '{feat.name}' is not supported for types") def describe(self) -> str: - assert self.meta return "%s type '%s'" % (self.meta, self.name) @@ -368,7 +364,6 @@ class QAPISchemaBuiltinType(QAPISchemaType): def __init__(self, name: str, json_type: str, c_type: str): super().__init__(name, None, None) - assert not c_type or isinstance(c_type, str) assert json_type in ('string', 'number', 'int', 'boolean', 'null', 'value') self._json_type_name = json_type @@ -411,9 +406,7 @@ def __init__( ): super().__init__(name, info, doc, ifcond, features) for m in members: - assert isinstance(m, QAPISchemaEnumMember) m.set_defined_in(name) - assert prefix is None or isinstance(prefix, str) self.members = members self.prefix = prefix @@ -456,7 +449,6 @@ def __init__( self, name: str, info: Optional[QAPISourceInfo], element_type: str ): super().__init__(name, info, None) - assert isinstance(element_type, str) self._element_type_name = element_type self._element_type: Optional[QAPISchemaType] = None @@ -517,7 +509,6 @@ def visit(self, visitor: QAPISchemaVisitor) -> None: self.element_type) def describe(self) -> str: - assert self.meta return "%s type ['%s']" % (self.meta, self._element_type_name) @@ -537,12 +528,9 @@ def __init__( # union has base, variants, and no local_members super().__init__(name, info, doc, ifcond, features) self.meta = 'union' if variants else 'struct' - assert base is None or isinstance(base, str) for m in local_members: - assert isinstance(m, QAPISchemaObjectTypeMember) m.set_defined_in(name) if variants is not None: - assert isinstance(variants, QAPISchemaVariants) variants.set_defined_in(name) self._base_name = base self.base = None @@ -666,7 +654,6 @@ def __init__( variants: QAPISchemaVariants, ): super().__init__(name, info, doc, ifcond, features) - assert isinstance(variants, QAPISchemaVariants) assert variants.tag_member variants.set_defined_in(name) variants.tag_member.set_defined_in(self.name) @@ -742,8 +729,6 @@ def __init__( assert bool(tag_member) != bool(tag_name) assert (isinstance(tag_name, str) or isinstance(tag_member, QAPISchemaObjectTypeMember)) - for v in variants: - assert isinstance(v, QAPISchemaVariant) self._tag_name = tag_name self.info = info self._tag_member = tag_member @@ -856,7 +841,6 @@ def __init__( info: Optional[QAPISourceInfo], ifcond: Optional[QAPISchemaIfCond] = None, ): - assert isinstance(name, str) self.name = name self.info = info self.ifcond = ifcond or QAPISchemaIfCond() @@ -924,7 +908,6 @@ def __init__( ): super().__init__(name, info, ifcond) for f in features or []: - assert isinstance(f, QAPISchemaFeature) f.set_defined_in(name) self.features = features or [] @@ -953,10 +936,7 @@ def __init__( features: Optional[List[QAPISchemaFeature]] = None, ): super().__init__(name, info, ifcond) - assert isinstance(typ, str) - assert isinstance(optional, bool) for f in features or []: - assert isinstance(f, QAPISchemaFeature) f.set_defined_in(name) self._type_name = typ self.type: QAPISchemaType # set during check(). Kind of hokey. @@ -1015,8 +995,6 @@ def __init__( coroutine: bool, ): super().__init__(name, info, doc, ifcond, features) - assert not arg_type or isinstance(arg_type, str) - assert not ret_type or isinstance(ret_type, str) self._arg_type_name = arg_type self.arg_type: Optional[QAPISchemaObjectType] = None self._ret_type_name = ret_type @@ -1093,7 +1071,6 @@ def __init__( boxed: bool, ): super().__init__(name, info, doc, ifcond, features) - assert not arg_type or isinstance(arg_type, str) self._arg_type_name = arg_type self.arg_type: Optional[QAPISchemaObjectType] = None self.boxed = boxed From patchwork Thu Nov 16 01:43:50 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Snow X-Patchwork-Id: 13457505 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org 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 smtp.lore.kernel.org (Postfix) with ESMTPS id C46B5C5ACB2 for ; Thu, 16 Nov 2023 01:48:36 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1r3RQu-0005Ph-Qc; Wed, 15 Nov 2023 20:44:41 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3RQO-0005Jr-HV for qemu-devel@nongnu.org; Wed, 15 Nov 2023 20:44:08 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3RQK-00023Y-Rr for qemu-devel@nongnu.org; Wed, 15 Nov 2023 20:44:08 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1700099044; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=M2mScccsI/m2XDWoGf9Kl1sVKUo1rs7PmXyaA/Q5qx4=; b=AiXWpI+gHJ4f4KMWaAn/Iu2s6GKCV4HKqnth6+c5klDbEUoX99ZccVeBBqbiaEQVKo/Q+X iai71ix6AsAvJBWS4UxjZgVfa8J3pnmxYYo6nmASETvkYweilaehLw5aH/WbIrQIxjWvRU 09519wqeBTNoJjrgKiKaHYAH1Wz7DEM= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-416-saj6Tb3DNkOMQtibDYCpDQ-1; Wed, 15 Nov 2023 20:43:58 -0500 X-MC-Unique: saj6Tb3DNkOMQtibDYCpDQ-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.rdu2.redhat.com [10.11.54.8]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id BBD57811E7D; Thu, 16 Nov 2023 01:43:57 +0000 (UTC) Received: from scv.localdomain (unknown [10.22.32.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 778EBC15881; Thu, 16 Nov 2023 01:43:57 +0000 (UTC) From: John Snow To: qemu-devel@nongnu.org Cc: Peter Maydell , Michael Roth , Markus Armbruster , John Snow Subject: [PATCH 19/19] qapi/schema: refactor entity lookup helpers Date: Wed, 15 Nov 2023 20:43:50 -0500 Message-ID: <20231116014350.653792-20-jsnow@redhat.com> In-Reply-To: <20231116014350.653792-1-jsnow@redhat.com> References: <20231116014350.653792-1-jsnow@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.8 Received-SPF: pass client-ip=170.10.133.124; envelope-from=jsnow@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -21 X-Spam_score: -2.2 X-Spam_bar: -- X-Spam_report: (-2.2 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.099, 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.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org This is not a clear win, but I was confused and I couldn't help myself. Before: lookup_entity(self, name: str, typ: Optional[type] = None ) -> Optional[QAPISchemaEntity]: ... lookup_type(self, name: str) -> Optional[QAPISchemaType]: ... resolve_type(self, name: str, info: Optional[QAPISourceInfo], what: Union[str, Callable[[Optional[QAPISourceInfo]], str]] ) -> QAPISchemaType: ... After: get_entity(self, name: str) -> Optional[QAPISchemaEntity]: ... get_typed_entity(self, name: str, typ: Type[_EntityType] ) -> Optional[_EntityType]: ... lookup_type(self, name: str) -> QAPISchemaType: ... resolve_type(self, name: str, info: Optional[QAPISourceInfo], what: Union[str, Callable[[Optional[QAPISourceInfo]], str]] ) -> QAPISchemaType: ... In essence, any function that can return a None value becomes "get ..." to encourage association with the dict.get() function which has the same behavior. Any function named "lookup" or "resolve" by contrast is no longer allowed to return a None value. This means that any callers to resolve_type or lookup_type don't have to check that the function worked, they can just assume it did. Callers to resolve_type will be greeted with a QAPISemError if something has gone wrong, as they have in the past. Callers to lookup_type will be greeted with a KeyError if the entity does not exist, or a TypeError if it does, but is the wrong type. get_entity and get_typed_entity remain for any callers who are specifically interested in the negative case. These functions have only a single caller each. Signed-off-by: John Snow --- docs/sphinx/qapidoc.py | 2 +- scripts/qapi/introspect.py | 8 ++---- scripts/qapi/schema.py | 52 ++++++++++++++++++++++++-------------- 3 files changed, 36 insertions(+), 26 deletions(-) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index 8f3b9997a15..96deadbf7fc 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -508,7 +508,7 @@ def run(self): vis.visit_begin(schema) for doc in schema.docs: if doc.symbol: - vis.symbol(doc, schema.lookup_entity(doc.symbol)) + vis.symbol(doc, schema.get_entity(doc.symbol)) else: vis.freeform(doc) return vis.get_document_nodes() diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index 42981bce163..67c7d89aae0 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -227,14 +227,10 @@ def _use_type(self, typ: QAPISchemaType) -> str: # Map the various integer types to plain int if typ.json_type() == 'int': - tmp = self._schema.lookup_type('int') - assert tmp is not None - typ = tmp + typ = self._schema.lookup_type('int') elif (isinstance(typ, QAPISchemaArrayType) and typ.element_type.json_type() == 'int'): - tmp = self._schema.lookup_type('intList') - assert tmp is not None - typ = tmp + typ = self._schema.lookup_type('intList') # Add type to work queue if new if typ not in self._used_types: self._used_types.append(typ) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index b5f377e68b8..5813136e78b 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -26,6 +26,8 @@ Dict, List, Optional, + Type, + TypeVar, Union, cast, ) @@ -767,7 +769,6 @@ def check( # Here we do: assert self.tag_member.defined_in base_type = schema.lookup_type(self.tag_member.defined_in) - assert base_type if not base_type.is_implicit(): base = "base type '%s'" % self.tag_member.defined_in if not isinstance(self.tag_member.type, QAPISchemaEnumType): @@ -1111,6 +1112,12 @@ def visit(self, visitor: QAPISchemaVisitor) -> None: self.arg_type, self.boxed) +# Used for type-dependent type lookup return values. +_EntityType = TypeVar( # pylint: disable=invalid-name + '_EntityType', bound=QAPISchemaEntity +) + + class QAPISchema: def __init__(self, fname: str): self.fname = fname @@ -1155,22 +1162,28 @@ def _def_entity(self, ent: QAPISchemaEntity) -> None: ent.info, "%s is already defined" % other_ent.describe()) self._entity_dict[ent.name] = ent - def lookup_entity( + def get_entity(self, name: str) -> Optional[QAPISchemaEntity]: + return self._entity_dict.get(name) + + def get_typed_entity( self, name: str, - typ: Optional[type] = None, - ) -> Optional[QAPISchemaEntity]: - ent = self._entity_dict.get(name) - if typ and not isinstance(ent, typ): - return None + typ: Type[_EntityType] + ) -> Optional[_EntityType]: + ent = self.get_entity(name) + if ent is not None and not isinstance(ent, typ): + etype = type(ent).__name__ + ttype = typ.__name__ + raise TypeError( + f"Entity '{name}' is of type '{etype}', not '{ttype}'." + ) return ent - def lookup_type(self, name: str) -> Optional[QAPISchemaType]: - typ = self.lookup_entity(name, QAPISchemaType) - if typ is None: - return None - assert isinstance(typ, QAPISchemaType) - return typ + def lookup_type(self, name: str) -> QAPISchemaType: + ent = self.get_typed_entity(name, QAPISchemaType) + if ent is None: + raise KeyError(f"Entity '{name}' is not defined.") + return ent def resolve_type( self, @@ -1178,13 +1191,14 @@ def resolve_type( info: Optional[QAPISourceInfo], what: Union[str, Callable[[Optional[QAPISourceInfo]], str]], ) -> QAPISchemaType: - typ = self.lookup_type(name) - if not typ: + try: + return self.lookup_type(name) + except (KeyError, TypeError) as err: if callable(what): what = what(info) raise QAPISemError( - info, "%s uses unknown type '%s'" % (what, name)) - return typ + info, "%s uses unknown type '%s'" % (what, name) + ) from err def _module_name(self, fname: str) -> str: if QAPISchemaModule.is_system_module(fname): @@ -1279,7 +1293,7 @@ def _make_array_type( self, element_type: str, info: Optional[QAPISourceInfo] ) -> str: name = element_type + 'List' # reserved by check_defn_name_str() - if not self.lookup_type(name): + if not self.get_entity(name): self._def_entity(QAPISchemaArrayType(name, info, element_type)) return name @@ -1295,7 +1309,7 @@ def _make_implicit_object_type( return None # See also QAPISchemaObjectTypeMember.describe() name = 'q_obj_%s-%s' % (name, role) - typ = self.lookup_entity(name, QAPISchemaObjectType) + typ = self.get_typed_entity(name, QAPISchemaObjectType) if typ: # The implicit object type has multiple users. This can # only be a duplicate definition, which will be flagged