diff options
Diffstat (limited to 'scripts')
-rw-r--r-- | scripts/qapi/doc.py | 75 | ||||
-rw-r--r-- | scripts/qapi/expr.py | 32 | ||||
-rw-r--r-- | scripts/qapi/parser.py | 29 | ||||
-rw-r--r-- | scripts/qapi/schema.py | 97 |
4 files changed, 127 insertions, 106 deletions
diff --git a/scripts/qapi/doc.py b/scripts/qapi/doc.py index 6d5726cf6e..6f1c17f71f 100644 --- a/scripts/qapi/doc.py +++ b/scripts/qapi/doc.py @@ -12,7 +12,7 @@ from qapi.gen import QAPIGenDoc, QAPISchemaVisitor MSG_FMT = """ @deftypefn {type} {{}} {name} -{body} +{body}{members}{features}{sections} @end deftypefn """.format @@ -20,7 +20,7 @@ MSG_FMT = """ TYPE_FMT = """ @deftp {{{type}}} {name} -{body} +{body}{members}{features}{sections} @end deftp """.format @@ -149,7 +149,8 @@ def texi_member(member, desc, suffix): suffix, desc, texi_if(member.ifcond, prefix='@*')) -def texi_members(doc, what, base, variants, member_func): +def texi_members(doc, what, base=None, variants=None, + member_func=texi_member): """Format the table of members""" items = '' for section in doc.args.values(): @@ -182,6 +183,14 @@ def texi_members(doc, what, base, variants, member_func): return '\n@b{%s:}\n@table @asis\n%s@end table\n' % (what, items) +def texi_arguments(doc, boxed_arg_type): + if boxed_arg_type: + assert not doc.args + return ('\n@b{Arguments:} the members of @code{%s}\n' + % boxed_arg_type.name) + return texi_members(doc, 'Arguments') + + def texi_features(doc): """Format the table of features""" items = '' @@ -208,12 +217,22 @@ def texi_sections(doc, ifcond): return body -def texi_entity(doc, what, ifcond, base=None, variants=None, - member_func=texi_member): - return (texi_body(doc) - + texi_members(doc, what, base, variants, member_func) - + texi_features(doc) - + texi_sections(doc, ifcond)) +def texi_type(typ, doc, ifcond, members): + return TYPE_FMT(type=typ, + name=doc.symbol, + body=texi_body(doc), + members=members, + features=texi_features(doc), + sections=texi_sections(doc, ifcond)) + + +def texi_msg(typ, doc, ifcond, members): + return MSG_FMT(type=typ, + name=doc.symbol, + body=texi_body(doc), + members=members, + features=texi_features(doc), + sections=texi_sections(doc, ifcond)) class QAPISchemaGenDocVisitor(QAPISchemaVisitor): @@ -227,48 +246,36 @@ class QAPISchemaGenDocVisitor(QAPISchemaVisitor): def visit_enum_type(self, name, info, ifcond, members, prefix): doc = self.cur_doc - self._gen.add(TYPE_FMT(type='Enum', - name=doc.symbol, - body=texi_entity(doc, 'Values', ifcond, - member_func=texi_enum_value))) + self._gen.add(texi_type('Enum', doc, ifcond, + texi_members(doc, 'Values', + member_func=texi_enum_value))) def visit_object_type(self, name, info, ifcond, base, members, variants, features): doc = self.cur_doc if base and base.is_implicit(): base = None - self._gen.add(TYPE_FMT(type='Object', - name=doc.symbol, - body=texi_entity(doc, 'Members', ifcond, - base, variants))) + self._gen.add(texi_type('Object', doc, ifcond, + texi_members(doc, 'Members', base, variants))) def visit_alternate_type(self, name, info, ifcond, variants): doc = self.cur_doc - self._gen.add(TYPE_FMT(type='Alternate', - name=doc.symbol, - body=texi_entity(doc, 'Members', ifcond))) + self._gen.add(texi_type('Alternate', doc, ifcond, + texi_members(doc, 'Members'))) def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, success_response, boxed, allow_oob, allow_preconfig, features): doc = self.cur_doc - if boxed: - body = texi_body(doc) - body += ('\n@b{Arguments:} the members of @code{%s}\n' - % arg_type.name) - body += texi_features(doc) - body += texi_sections(doc, ifcond) - else: - body = texi_entity(doc, 'Arguments', ifcond) - self._gen.add(MSG_FMT(type='Command', - name=doc.symbol, - body=body)) + self._gen.add(texi_msg('Command', doc, ifcond, + texi_arguments(doc, + arg_type if boxed else None))) def visit_event(self, name, info, ifcond, arg_type, boxed): doc = self.cur_doc - self._gen.add(MSG_FMT(type='Event', - name=doc.symbol, - body=texi_entity(doc, 'Arguments', ifcond))) + self._gen.add(texi_msg('Event', doc, ifcond, + texi_arguments(doc, + arg_type if boxed else None))) def symbol(self, doc, entity): if self._gen._body: diff --git a/scripts/qapi/expr.py b/scripts/qapi/expr.py index 7c7394f835..d7a289eded 100644 --- a/scripts/qapi/expr.py +++ b/scripts/qapi/expr.py @@ -95,12 +95,6 @@ def check_flags(expr, info): info, "flag '%s' may only use true value" % key) -def normalize_if(expr): - ifcond = expr.get('if') - if isinstance(ifcond, str): - expr['if'] = [ifcond] - - def check_if(expr, info, source): def check_if_str(ifcond, info): @@ -126,6 +120,7 @@ def check_if(expr, info, source): check_if_str(elt, info) else: check_if_str(ifcond, info) + expr['if'] = [ifcond] def normalize_members(members): @@ -175,21 +170,16 @@ def check_type(value, info, source, raise QAPISemError(info, "%s uses reserved name" % key_source) check_keys(arg, info, key_source, ['type'], ['if']) check_if(arg, info, key_source) - normalize_if(arg) check_type(arg['type'], info, key_source, allow_array=True) -def normalize_features(features): - if isinstance(features, list): - features[:] = [f if isinstance(f, dict) else {'name': f} - for f in features] - - def check_features(features, info): if features is None: return if not isinstance(features, list): raise QAPISemError(info, "'features' must be an array") + features[:] = [f if isinstance(f, dict) else {'name': f} + for f in features] for f in features: source = "'features' member" assert isinstance(f, dict) @@ -198,13 +188,6 @@ def check_features(features, info): source = "%s '%s'" % (source, f['name']) check_name_str(f['name'], info, source) check_if(f, info, source) - normalize_if(f) - - -def normalize_enum(expr): - if isinstance(expr['data'], list): - expr['data'] = [m if isinstance(m, dict) else {'name': m} - for m in expr['data']] def check_enum(expr, info): @@ -219,6 +202,8 @@ def check_enum(expr, info): permit_upper = name in info.pragma.name_case_whitelist + members[:] = [m if isinstance(m, dict) else {'name': m} + for m in members] for member in members: source = "'data' member" check_keys(member, info, source, ['name'], ['if']) @@ -227,7 +212,6 @@ def check_enum(expr, info): check_name_str(member['name'], info, source, enum_member=True, permit_upper=permit_upper) check_if(member, info, source) - normalize_if(member) def check_struct(expr, info): @@ -259,7 +243,6 @@ def check_union(expr, info): check_name_str(key, info, source) check_keys(value, info, source, ['type'], ['if']) check_if(value, info, source) - normalize_if(value) check_type(value['type'], info, source, allow_array=not base) @@ -273,7 +256,6 @@ def check_alternate(expr, info): check_name_str(key, info, source) check_keys(value, info, source, ['type'], ['if']) check_if(value, info, source) - normalize_if(value) check_type(value['type'], info, source) @@ -339,7 +321,6 @@ def check_exprs(exprs): if meta == 'enum': check_keys(expr, info, meta, ['enum', 'data'], ['if', 'prefix']) - normalize_enum(expr) check_enum(expr, info) elif meta == 'union': check_keys(expr, info, meta, @@ -357,7 +338,6 @@ def check_exprs(exprs): check_keys(expr, info, meta, ['struct', 'data'], ['base', 'if', 'features']) normalize_members(expr['data']) - normalize_features(expr.get('features')) check_struct(expr, info) elif meta == 'command': check_keys(expr, info, meta, @@ -366,7 +346,6 @@ def check_exprs(exprs): 'gen', 'success-response', 'allow-oob', 'allow-preconfig']) normalize_members(expr.get('data')) - normalize_features(expr.get('features')) check_command(expr, info) elif meta == 'event': check_keys(expr, info, meta, @@ -376,7 +355,6 @@ def check_exprs(exprs): else: assert False, 'unexpected meta type' - normalize_if(expr) check_if(expr, info, meta) check_flags(expr, info) diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py index e800876ad1..342792e410 100644 --- a/scripts/qapi/parser.py +++ b/scripts/qapi/parser.py @@ -555,16 +555,31 @@ class QAPIDoc(object): self.args[member.name] = QAPIDoc.ArgSection(member.name) self.args[member.name].connect(member) + def connect_feature(self, feature): + if feature.name not in self.features: + raise QAPISemError(feature.info, + "feature '%s' lacks documentation" + % feature.name) + self.features[feature.name] = QAPIDoc.ArgSection(feature.name) + self.features[feature.name].connect(feature) + def check_expr(self, expr): if self.has_section('Returns') and 'command' not in expr: raise QAPISemError(self.info, "'Returns:' is only valid for commands") def check(self): - bogus = [name for name, section in self.args.items() - if not section.member] - if bogus: - raise QAPISemError( - self.info, - "the following documented members are not in " - "the declaration: %s" % ", ".join(bogus)) + + def check_args_section(args, info, what): + bogus = [name for name, section in args.items() + if not section.member] + if bogus: + raise QAPISemError( + self.info, + "documented member%s '%s' %s not exist" + % ("s" if len(bogus) > 1 else "", + "', '".join(bogus), + "do" if len(bogus) > 1 else "does")) + + check_args_section(self.args, self.info, 'members') + check_args_section(self.features, self.info, 'features') diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index f7d68a35f4..cf0045f34e 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -27,8 +27,11 @@ from qapi.parser import QAPISchemaParser class QAPISchemaEntity(object): meta = None - def __init__(self, name, info, doc, ifcond=None): + def __init__(self, name, 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 self._module = None # For explicitly defined entities, info points to the (explicit) @@ -39,6 +42,7 @@ class QAPISchemaEntity(object): self.info = info self.doc = doc self._ifcond = ifcond or [] + self.features = features or [] self._checked = False def c_name(self): @@ -49,8 +53,21 @@ class QAPISchemaEntity(object): if self.info: self._module = os.path.relpath(self.info.fname, os.path.dirname(schema.fname)) + seen = {} + for f in self.features: + f.check_clash(self.info, seen) + if self.doc: + self.doc.connect_feature(f) + self._checked = True + def connect_doc(self, doc=None): + pass + + def check_doc(self): + if self.doc: + self.doc.check() + @property def ifcond(self): assert self._checked @@ -217,8 +234,12 @@ class QAPISchemaEnumType(QAPISchemaType): seen = {} for m in self.members: m.check_clash(self.info, seen) - if self.doc: - self.doc.connect_member(m) + + def connect_doc(self, doc=None): + doc = doc or self.doc + if doc: + for m in self.members: + doc.connect_member(m) def is_implicit(self): # See QAPISchema._make_implicit_enum_type() and ._def_predefineds() @@ -296,7 +317,7 @@ class QAPISchemaObjectType(QAPISchemaType): # struct has local_members, optional base, and no variants # flat union has base, variants, and no local_members # simple union has local_members, variants, and no base - QAPISchemaType.__init__(self, name, info, doc, ifcond) + QAPISchemaType.__init__(self, 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: @@ -305,15 +326,11 @@ class QAPISchemaObjectType(QAPISchemaType): if variants is not None: assert isinstance(variants, QAPISchemaObjectTypeVariants) variants.set_defined_in(name) - for f in features: - assert isinstance(f, QAPISchemaFeature) - f.set_defined_in(name) self._base_name = base self.base = None self.local_members = local_members self.variants = variants self.members = None - self.features = features def check(self, schema): # This calls another type T's .check() exactly when the C @@ -345,22 +362,12 @@ class QAPISchemaObjectType(QAPISchemaType): for m in self.local_members: m.check(schema) m.check_clash(self.info, seen) - if self.doc: - self.doc.connect_member(m) members = seen.values() if self.variants: self.variants.check(schema, seen) self.variants.check_clash(self.info, seen) - # Features are in a name space separate from members - seen = {} - for f in self.features: - f.check_clash(self.info, seen) - - if self.doc: - self.doc.check() - self.members = members # mark completed # Check that the members of this type do not cause duplicate JSON members, @@ -372,6 +379,14 @@ class QAPISchemaObjectType(QAPISchemaType): for m in self.members: m.check_clash(info, seen) + def connect_doc(self, doc=None): + doc = doc or self.doc + if doc: + if self.base and self.base.is_implicit(): + self.base.connect_doc(doc) + for m in self.local_members: + doc.connect_member(m) + @property def ifcond(self): assert self._checked @@ -639,10 +654,12 @@ class QAPISchemaAlternateType(QAPISchemaType): "%s can't be distinguished from '%s'" % (v.describe(self.info), types_seen[qt])) types_seen[qt] = v.name - if self.doc: - self.doc.connect_member(v) - if self.doc: - self.doc.check() + + def connect_doc(self, doc=None): + doc = doc or self.doc + if doc: + for v in self.variants.variants: + doc.connect_member(v) def c_type(self): return c_name(self.name) + pointer_suffix @@ -662,12 +679,9 @@ class QAPISchemaCommand(QAPISchemaEntity): def __init__(self, name, info, doc, ifcond, arg_type, ret_type, gen, success_response, boxed, allow_oob, allow_preconfig, features): - QAPISchemaEntity.__init__(self, name, info, doc, ifcond) + QAPISchemaEntity.__init__(self, name, info, doc, ifcond, features) assert not arg_type or isinstance(arg_type, str) assert not ret_type or isinstance(ret_type, str) - for f in features: - assert isinstance(f, QAPISchemaFeature) - f.set_defined_in(name) self._arg_type_name = arg_type self.arg_type = None self._ret_type_name = ret_type @@ -677,7 +691,6 @@ class QAPISchemaCommand(QAPISchemaEntity): self.boxed = boxed self.allow_oob = allow_oob self.allow_preconfig = allow_preconfig - self.features = features def check(self, schema): QAPISchemaEntity.check(self, schema) @@ -707,10 +720,11 @@ class QAPISchemaCommand(QAPISchemaEntity): "command's 'returns' cannot take %s" % self.ret_type.describe()) - # Features are in a name space separate from members - seen = {} - for f in self.features: - f.check_clash(self.info, seen) + def connect_doc(self, doc=None): + 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): QAPISchemaEntity.visit(self, visitor) @@ -748,6 +762,12 @@ class QAPISchemaEvent(QAPISchemaEntity): "event's 'data' can take %s only with 'boxed': true" % self.arg_type.describe()) + def connect_doc(self, doc=None): + 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): QAPISchemaEntity.visit(self, visitor) visitor.visit_event(self.name, self.info, self.ifcond, @@ -873,8 +893,7 @@ class QAPISchema(object): self._def_entity(QAPISchemaArrayType(name, info, element_type)) return name - def _make_implicit_object_type(self, name, info, doc, ifcond, - role, members): + def _make_implicit_object_type(self, name, info, ifcond, role, members): if not members: return None # See also QAPISchemaObjectTypeMember.describe() @@ -892,7 +911,7 @@ class QAPISchema(object): # TODO kill simple unions or implement the disjunction assert (ifcond or []) == typ._ifcond # pylint: disable=protected-access else: - self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, + self._def_entity(QAPISchemaObjectType(name, info, None, ifcond, None, members, None, [])) return name @@ -939,7 +958,7 @@ class QAPISchema(object): assert len(typ) == 1 typ = self._make_array_type(typ[0], info) typ = self._make_implicit_object_type( - typ, info, None, self.lookup_type(typ), + typ, info, self.lookup_type(typ), 'wrapper', [self._make_member('data', typ, None, info)]) return QAPISchemaObjectTypeVariant(case, info, typ, ifcond) @@ -952,7 +971,7 @@ class QAPISchema(object): tag_member = None if isinstance(base, dict): base = self._make_implicit_object_type( - name, info, doc, ifcond, + name, info, ifcond, 'base', self._make_members(base, info)) if tag_name: variants = [self._make_variant(key, value['type'], @@ -999,7 +1018,7 @@ class QAPISchema(object): features = expr.get('features', []) if isinstance(data, OrderedDict): data = self._make_implicit_object_type( - name, info, doc, ifcond, 'arg', self._make_members(data, info)) + name, info, ifcond, 'arg', self._make_members(data, info)) if isinstance(rets, list): assert len(rets) == 1 rets = self._make_array_type(rets[0], info) @@ -1015,7 +1034,7 @@ class QAPISchema(object): ifcond = expr.get('if') if isinstance(data, OrderedDict): data = self._make_implicit_object_type( - name, info, doc, ifcond, 'arg', self._make_members(data, info)) + name, info, ifcond, 'arg', self._make_members(data, info)) self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed)) def _def_exprs(self, exprs): @@ -1043,6 +1062,8 @@ class QAPISchema(object): def check(self): for ent in self._entity_list: ent.check(self) + ent.connect_doc() + ent.check_doc() def visit(self, visitor): visitor.visit_begin(self) |