Skip to content

Commit

Permalink
qapi: create QAPISchemaDefinition
Browse files Browse the repository at this point in the history
Include entities don't have names, but we generally expect "entities" to
have names. Reclassify all entities with names as *definitions*, leaving
the nameless include entities as QAPISchemaEntity instances.

This is primarily to help simplify typing around expectations of what
callers expect for properties of an "entity".

Suggested-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-ID: <20240315152301.3621858-6-armbru@redhat.com>
  • Loading branch information
jnsnow authored and Markus Armbruster committed Apr 24, 2024
1 parent ce7fde0 commit 2418d1c
Showing 1 changed file with 78 additions and 58 deletions.
136 changes: 78 additions & 58 deletions scripts/qapi/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,58 +55,81 @@ def is_present(self):


class QAPISchemaEntity:
meta: Optional[str] = None
"""
A schema entity.
def __init__(self, name: str, info, doc, ifcond=None, features=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
This is either a directive, such as include, or a definition.
The latter uses sub-class `QAPISchemaDefinition`.
"""
def __init__(self, info):
self._module = 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
# triggered the implicit definition (there may be more than one
# such place).
self.info = info
self._checked = False

def __repr__(self):
return "<%s at 0x%x>" % (type(self).__name__, id(self))

def check(self, schema):
# pylint: disable=unused-argument
self._checked = True

def connect_doc(self, doc=None):
pass

def _set_module(self, schema, info):
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):
self._set_module(schema, self.info)

def visit(self, visitor):
# pylint: disable=unused-argument
assert self._checked


class QAPISchemaDefinition(QAPISchemaEntity):
meta: Optional[str] = None

def __init__(self, name: str, info, doc, ifcond=None, features=None):
assert isinstance(name, str)
super().__init__(info)
for f in features or []:
assert isinstance(f, QAPISchemaFeature)
f.set_defined_in(name)
self.name = name
self.doc = doc
self._ifcond = ifcond or QAPISchemaIfCond()
self.features = features or []
self._checked = False

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))

def c_name(self):
return c_name(self.name)

def check(self, schema):
# pylint: disable=unused-argument
assert not self._checked
super().check(schema)
seen = {}
for f in self.features:
f.check_clash(self.info, seen)
self._checked = True

def connect_doc(self, doc=None):
super().connect_doc(doc)
doc = doc or self.doc
if doc:
for f in self.features:
doc.connect_feature(f)

def _set_module(self, schema, info):
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):
self._set_module(schema, self.info)

@property
def ifcond(self):
assert self._checked
Expand All @@ -115,10 +138,6 @@ def ifcond(self):
def is_implicit(self):
return not self.info

def visit(self, visitor):
# pylint: disable=unused-argument
assert self._checked

def describe(self):
assert self.meta
return "%s '%s'" % (self.meta, self.name)
Expand Down Expand Up @@ -218,15 +237,15 @@ def visit(self, visitor):

class QAPISchemaInclude(QAPISchemaEntity):
def __init__(self, sub_module, info):
super().__init__(None, info, None)
super().__init__(info)
self._sub_module = sub_module

def visit(self, visitor):
super().visit(visitor)
visitor.visit_include(self._sub_module.name, self.info)


class QAPISchemaType(QAPISchemaEntity):
class QAPISchemaType(QAPISchemaDefinition):
# Return the C type for common use.
# For the types we commonly box, this is a pointer type.
def c_type(self):
Expand Down Expand Up @@ -797,7 +816,7 @@ def __init__(self, name, info, typ, ifcond=None):
super().__init__(name, info, typ, False, ifcond)


class QAPISchemaCommand(QAPISchemaEntity):
class QAPISchemaCommand(QAPISchemaDefinition):
meta = 'command'

def __init__(self, name, info, doc, ifcond, features,
Expand Down Expand Up @@ -868,7 +887,7 @@ def visit(self, visitor):
self.coroutine)


class QAPISchemaEvent(QAPISchemaEntity):
class QAPISchemaEvent(QAPISchemaDefinition):
meta = 'event'

def __init__(self, name, info, doc, ifcond, features, arg_type, boxed):
Expand Down Expand Up @@ -939,23 +958,24 @@ def __init__(self, fname):
self.check()

def _def_entity(self, ent):
# Only the predefined types are allowed to not have info
assert ent.info or self._predefining
self._entity_list.append(ent)
if ent.name is None:
return

def _def_definition(self, defn):
# Only the predefined types are allowed to not have info
assert defn.info or self._predefining
self._def_entity(defn)
# TODO reject names that differ only in '_' vs. '.' vs. '-',
# because they're liable to clash in generated C.
other_ent = self._entity_dict.get(ent.name)
if other_ent:
if other_ent.info:
where = QAPISourceError(other_ent.info, "previous definition")
other_defn = self._entity_dict.get(defn.name)
if other_defn:
if other_defn.info:
where = QAPISourceError(other_defn.info, "previous definition")
raise QAPISemError(
ent.info,
"'%s' is already defined\n%s" % (ent.name, where))
defn.info,
"'%s' is already defined\n%s" % (defn.name, where))
raise QAPISemError(
ent.info, "%s is already defined" % other_ent.describe())
self._entity_dict[ent.name] = ent
defn.info, "%s is already defined" % other_defn.describe())
self._entity_dict[defn.name] = defn

def lookup_entity(self, name, typ=None):
ent = self._entity_dict.get(name)
Expand Down Expand Up @@ -997,7 +1017,7 @@ def _def_include(self, expr: QAPIExpression):
QAPISchemaInclude(self._make_module(include), expr.info))

def _def_builtin_type(self, name, json_type, c_type):
self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
self._def_definition(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
# (qapi-builtin-types.[ch]) may be shared by some other
Expand All @@ -1023,15 +1043,15 @@ def _def_predefineds(self):
self._def_builtin_type(*t)
self.the_empty_object_type = QAPISchemaObjectType(
'q_empty', None, None, None, None, None, [], None)
self._def_entity(self.the_empty_object_type)
self._def_definition(self.the_empty_object_type)

qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
'qbool']
qtype_values = self._make_enum_members(
[{'name': n} for n in qtypes], None)

self._def_entity(QAPISchemaEnumType('QType', None, None, None, None,
qtype_values, 'QTYPE'))
self._def_definition(QAPISchemaEnumType(
'QType', None, None, None, None, qtype_values, 'QTYPE'))

def _make_features(self, features, info):
if features is None:
Expand All @@ -1053,7 +1073,8 @@ def _make_enum_members(self, values, info):
def _make_array_type(self, element_type, info):
name = element_type + 'List' # reserved by check_defn_name_str()
if not self.lookup_type(name):
self._def_entity(QAPISchemaArrayType(name, info, element_type))
self._def_definition(QAPISchemaArrayType(
name, info, element_type))
return name

def _make_implicit_object_type(self, name, info, ifcond, role, members):
Expand All @@ -1068,7 +1089,7 @@ def _make_implicit_object_type(self, name, info, ifcond, role, members):
# later.
pass
else:
self._def_entity(QAPISchemaObjectType(
self._def_definition(QAPISchemaObjectType(
name, info, None, ifcond, None, None, members, None))
return name

Expand All @@ -1079,7 +1100,7 @@ def _def_enum_type(self, expr: QAPIExpression):
ifcond = QAPISchemaIfCond(expr.get('if'))
info = expr.info
features = self._make_features(expr.get('features'), info)
self._def_entity(QAPISchemaEnumType(
self._def_definition(QAPISchemaEnumType(
name, info, expr.doc, ifcond, features,
self._make_enum_members(data, info), prefix))

Expand Down Expand Up @@ -1107,7 +1128,7 @@ def _def_struct_type(self, expr: QAPIExpression):
info = expr.info
ifcond = QAPISchemaIfCond(expr.get('if'))
features = self._make_features(expr.get('features'), info)
self._def_entity(QAPISchemaObjectType(
self._def_definition(QAPISchemaObjectType(
name, info, expr.doc, ifcond, features, base,
self._make_members(data, info),
None))
Expand Down Expand Up @@ -1137,7 +1158,7 @@ def _def_union_type(self, expr: QAPIExpression):
info)
for (key, value) in data.items()]
members: List[QAPISchemaObjectTypeMember] = []
self._def_entity(
self._def_definition(
QAPISchemaObjectType(name, info, expr.doc, ifcond, features,
base, members,
QAPISchemaVariants(
Expand All @@ -1156,7 +1177,7 @@ def _def_alternate_type(self, expr: QAPIExpression):
info)
for (key, value) in data.items()]
tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False)
self._def_entity(
self._def_definition(
QAPISchemaAlternateType(
name, info, expr.doc, ifcond, features,
QAPISchemaVariants(None, info, tag_member, variants)))
Expand All @@ -1181,11 +1202,10 @@ def _def_command(self, expr: QAPIExpression):
if isinstance(rets, list):
assert len(rets) == 1
rets = self._make_array_type(rets[0], info)
self._def_entity(QAPISchemaCommand(name, info, expr.doc, ifcond,
features, data, rets,
gen, success_response,
boxed, allow_oob, allow_preconfig,
coroutine))
self._def_definition(
QAPISchemaCommand(name, info, expr.doc, ifcond, features, data,
rets, gen, success_response, boxed, allow_oob,
allow_preconfig, coroutine))

def _def_event(self, expr: QAPIExpression):
name = expr['event']
Expand All @@ -1198,8 +1218,8 @@ def _def_event(self, expr: QAPIExpression):
data = self._make_implicit_object_type(
name, info, ifcond,
'arg', self._make_members(data, info))
self._def_entity(QAPISchemaEvent(name, info, expr.doc, ifcond,
features, data, boxed))
self._def_definition(QAPISchemaEvent(name, info, expr.doc, ifcond,
features, data, boxed))

def _def_exprs(self, exprs):
for expr in exprs:
Expand Down

0 comments on commit 2418d1c

Please sign in to comment.