From patchwork Wed Nov 11 06:51:17 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Blake X-Patchwork-Id: 542807 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 14997141408 for ; Wed, 11 Nov 2015 17:59:37 +1100 (AEDT) Received: from localhost ([::1]:38257 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZwPNX-0004gf-1L for incoming@patchwork.ozlabs.org; Wed, 11 Nov 2015 01:59:35 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:41442) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZwPG5-0006Ie-9H for qemu-devel@nongnu.org; Wed, 11 Nov 2015 01:52:12 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ZwPFt-0005dZ-0O for qemu-devel@nongnu.org; Wed, 11 Nov 2015 01:51:53 -0500 Received: from mx1.redhat.com ([209.132.183.28]:45982) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZwPFs-0005dJ-Oy for qemu-devel@nongnu.org; Wed, 11 Nov 2015 01:51:40 -0500 Received: from int-mx10.intmail.prod.int.phx2.redhat.com (int-mx10.intmail.prod.int.phx2.redhat.com [10.5.11.23]) by mx1.redhat.com (Postfix) with ESMTPS id 4C34CC0B5702; Wed, 11 Nov 2015 06:51:40 +0000 (UTC) Received: from red.redhat.com (ovpn-113-115.phx2.redhat.com [10.3.113.115]) by int-mx10.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id tAB6pW30007377; Wed, 11 Nov 2015 01:51:39 -0500 From: Eric Blake To: qemu-devel@nongnu.org Date: Tue, 10 Nov 2015 23:51:17 -0700 Message-Id: <1447224690-9743-16-git-send-email-eblake@redhat.com> In-Reply-To: <1447224690-9743-1-git-send-email-eblake@redhat.com> References: <1447224690-9743-1-git-send-email-eblake@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.23 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 209.132.183.28 Cc: armbru@redhat.com, Michael Roth Subject: [Qemu-devel] [PATCH v11 15/28] qapi: Track owner of each object member X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Future commits will migrate semantic checking away from parsing and over to the various QAPISchema*.check() methods. But to report an error message about an incorrect semantic use of a member of an object type, it helps to know which type, command, or event owns the member. In particular, when a member is inherited from a base type, it is desirable to associate the member name with the base type (and not the type calling member.check()). Rather than packing additional information into the seen array passed to each member.check() (as in seen[m.name] = {'member':m, 'owner':type}), it is easier to have each member track the name of the owner type in the first place (keeping things simpler with the existing seen[m.name] = m). The new member.owner field is set via a new set_owner() method, called when registering the members and variants arrays with an object or variant type. Track only a name, and not the actual type object, to avoid creating a circular python reference chain. Note that Variants.set_owner() method does not set the owner for the tag_member field; this field is set earlier either as part of an object's non-variant members, or explicitly by alternates. The source information is intended for human consumption in error messages, and a new describe() method is added to access the resulting information. For example, given the qapi: { 'command': 'foo', 'data': { 'string': 'str' } } an implementation of visit_command() that calls arg_type.members[0].describe() will see "'string' (parameter of foo)". To make the human-readable name of implicit types work without duplicating efforts, the describe() method has to reverse the name of implicit types, via the helper _pretty_owner(). No change to generated code. Signed-off-by: Eric Blake --- v11: set alternate tag_member owner during init, tweak comments, keep events with '-args' instead of '-data', prefer '(parameter of foo)' v10 (now in subset C): rebase to latest, set alternate tag_member owner from alternate v9 (now in subset D): rebase to earlier changes, hoist 'role' to top of class, split out _pretty_helper(), manage owner when tag_member appears as part of local_members for unions v8: don't munge implicit type names [except for event data], and instead make describe() create nicer messages. Add set_owner(), and use field 'role' instead of method _describe() v7: total rewrite: rework implicit object names, assign owner when initializing owner type rather than when creating member python object v6: rebase on new lazy array creation and simple union 'type' motion; tweak commit message --- scripts/qapi.py | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/scripts/qapi.py b/scripts/qapi.py index 6fc14be..79038a8 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -957,8 +957,10 @@ class QAPISchemaObjectType(QAPISchemaType): assert base is None or isinstance(base, str) for m in local_members: assert isinstance(m, QAPISchemaObjectTypeMember) - assert (variants is None or - isinstance(variants, QAPISchemaObjectTypeVariants)) + m.set_owner(name) + if variants is not None: + assert isinstance(variants, QAPISchemaObjectTypeVariants) + variants.set_owner(name) self._base_name = base self.base = None self.local_members = local_members @@ -1013,6 +1015,8 @@ class QAPISchemaObjectType(QAPISchemaType): class QAPISchemaObjectTypeMember(object): + role = 'member' + def __init__(self, name, typ, optional): assert isinstance(name, str) assert isinstance(typ, str) @@ -1021,8 +1025,14 @@ class QAPISchemaObjectTypeMember(object): self._type_name = typ self.type = None self.optional = optional + self.owner = None + + def set_owner(self, name): + assert not self.owner + self.owner = name def check(self, schema): + assert self.owner self.type = schema.lookup_type(self._type_name) assert self.type @@ -1031,6 +1041,22 @@ class QAPISchemaObjectTypeMember(object): assert self.name not in seen seen[self.name] = self + def _pretty_owner(self): + owner = self.owner + if owner.startswith(':obj-'): + # See QAPISchema._make_implicit_object_type() - reverse the + # mapping there to create a nice human-readable description + owner = owner[5:] + if owner.endswith('-arg'): + return '(parameter of %s)' % owner[:-4] + else: + assert owner.endswith('-wrapper') + return '(branch of %s)' % owner[:-8] + return '(%s of %s)' % (self.role, owner) + + def describe(self): + return "'%s' %s" % (self.name, self._pretty_owner()) + class QAPISchemaObjectTypeVariants(object): def __init__(self, tag_name, tag_member, variants): @@ -1047,6 +1073,10 @@ class QAPISchemaObjectTypeVariants(object): self.tag_member = tag_member self.variants = variants + def set_owner(self, name): + for v in self.variants: + v.set_owner(name) + def check(self, schema, seen): if not self.tag_member: # flat union self.tag_member = seen[self.tag_name] @@ -1066,6 +1096,8 @@ class QAPISchemaObjectTypeVariants(object): class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember): + role = 'branch' + def __init__(self, name, typ): QAPISchemaObjectTypeMember.__init__(self, name, typ, False) @@ -1085,6 +1117,8 @@ class QAPISchemaAlternateType(QAPISchemaType): QAPISchemaType.__init__(self, name, info) assert isinstance(variants, QAPISchemaObjectTypeVariants) assert not variants.tag_name + variants.set_owner(name) + variants.tag_member.set_owner(self.name) self.variants = variants def check(self, schema): @@ -1217,6 +1251,7 @@ class QAPISchema(object): def _make_implicit_object_type(self, name, info, role, members): if not members: return None + # See also QAPISchemaObjectTypeMember._pretty_owner() name = ':obj-%s-%s' % (name, role) if not self.lookup_entity(name, QAPISchemaObjectType): self._def_entity(QAPISchemaObjectType(name, info, None,