From 597494abdefc68991b41cfda03801a496c9fcc4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 25 Jan 2017 17:03:07 +0400 Subject: qapi2texi: change texi formatters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit STRUCT_FMT is generic enough, rename it to TYPE_FMT, use it for unions. Rename COMMAND_FMT to MSG_FMT, since it applies to both commands and events. Signed-off-by: Marc-André Lureau Message-Id: <20170125130308.16104-2-marcandre.lureau@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- scripts/qapi2texi.py | 46 +++++++++++++++++++--------------------------- 1 file changed, 19 insertions(+), 27 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi2texi.py b/scripts/qapi2texi.py index c1071c62c6..69f5edce4d 100755 --- a/scripts/qapi2texi.py +++ b/scripts/qapi2texi.py @@ -9,7 +9,7 @@ import sys import qapi -COMMAND_FMT = """ +MSG_FMT = """ @deftypefn {type} {{}} {name} {body} @@ -18,16 +18,7 @@ COMMAND_FMT = """ """.format -ENUM_FMT = """ -@deftp Enum {name} - -{body} - -@end deftp - -""".format - -STRUCT_FMT = """ +TYPE_FMT = """ @deftp {{{type}}} {name} {body} @@ -170,9 +161,9 @@ def texi_body(doc): def texi_alternate(expr, doc): """Format an alternate to texi""" body = texi_body(doc) - return STRUCT_FMT(type="Alternate", - name=doc.symbol, - body=body) + return TYPE_FMT(type="Alternate", + name=doc.symbol, + body=body) def texi_union(expr, doc): @@ -184,9 +175,9 @@ def texi_union(expr, doc): union = "Simple Union" body = texi_body(doc) - return STRUCT_FMT(type=union, - name=doc.symbol, - body=body) + return TYPE_FMT(type=union, + name=doc.symbol, + body=body) def texi_enum(expr, doc): @@ -195,32 +186,33 @@ def texi_enum(expr, doc): if i not in doc.args: doc.args[i] = '' body = texi_body(doc) - return ENUM_FMT(name=doc.symbol, + return TYPE_FMT(type="Enum", + name=doc.symbol, body=body) def texi_struct(expr, doc): """Format a struct to texi""" body = texi_body(doc) - return STRUCT_FMT(type="Struct", - name=doc.symbol, - body=body) + return TYPE_FMT(type="Struct", + name=doc.symbol, + body=body) def texi_command(expr, doc): """Format a command to texi""" body = texi_body(doc) - return COMMAND_FMT(type="Command", - name=doc.symbol, - body=body) + return MSG_FMT(type="Command", + name=doc.symbol, + body=body) def texi_event(expr, doc): """Format an event to texi""" body = texi_body(doc) - return COMMAND_FMT(type="Event", - name=doc.symbol, - body=body) + return MSG_FMT(type="Event", + name=doc.symbol, + body=body) def texi_expr(expr, doc): -- cgit v1.2.3 From f880cd6b6f3fe7847cde3bec1fa98891c92229d2 Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Thu, 2 Mar 2017 12:24:29 +0000 Subject: qmp: allow setting properties to empty string in qmp-shell The qmp-shell property parser currently rejects attempts to set string properties to the empty string eg (QEMU) migrate-set-parameters tls-hostname= Error while parsing command line: Expected a key=value pair, got 'tls-hostname=' command format: [arg-name1=arg1] ... [arg-nameN=argN] This is caused by checking the wrong condition after splitting the parameter on '='. The "partition" method will return "" for the separator field, if the seperator was not present, so that is the correct thing to check for malformed syntax. Signed-off-by: Daniel P. Berrange Message-Id: <20170302122429.7737-1-berrange@redhat.com> Reviewed-by: Eric Blake Reviewed-by: John Snow Signed-off-by: Markus Armbruster --- scripts/qmp/qmp-shell | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell index 0373b24b20..eccb88a4e8 100755 --- a/scripts/qmp/qmp-shell +++ b/scripts/qmp/qmp-shell @@ -166,8 +166,8 @@ class QMPShell(qmp.QEMUMonitorProtocol): def __cli_expr(self, tokens, parent): for arg in tokens: - (key, _, val) = arg.partition('=') - if not val: + (key, sep, val) = arg.partition('=') + if sep != '=': raise QMPShellError("Expected a key=value pair, got '%s'" % arg) value = self.__parse_value(val) -- cgit v1.2.3 From e04dea88727c2ac97091333ac8be6af5952634a7 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:56:50 +0100 Subject: qapi: Factor QAPISchemaParser._include() out of .__init__() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Reviewed-by: Marc-André Lureau Message-Id: <1489582656-31133-2-git-send-email-armbru@redhat.com> --- scripts/qapi.py | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi.py b/scripts/qapi.py index 53a44779d0..345cde157e 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -268,34 +268,15 @@ class QAPISchemaParser(object): continue expr = self.get_expr(False) - if isinstance(expr, dict) and "include" in expr: + if 'include' in expr: if len(expr) != 1: raise QAPISemError(info, "Invalid 'include' directive") include = expr["include"] if not isinstance(include, str): raise QAPISemError(info, "Value of 'include' must be a string") - incl_abs_fname = os.path.join(os.path.dirname(abs_fname), - include) - # catch inclusion cycle - inf = info - while inf: - if incl_abs_fname == os.path.abspath(inf['file']): - raise QAPISemError(info, "Inclusion loop for %s" - % include) - inf = inf['parent'] - - # skip multiple include of the same file - if incl_abs_fname in previously_included: - continue - try: - fobj = open(incl_abs_fname, 'r') - except IOError as e: - raise QAPISemError(info, '%s: %s' % (e.strerror, include)) - exprs_include = QAPISchemaParser(fobj, previously_included, - info) - self.exprs.extend(exprs_include.exprs) - self.docs.extend(exprs_include.docs) + self._include(include, info, os.path.dirname(abs_fname), + previously_included) else: expr_elem = {'expr': expr, 'info': info} @@ -307,6 +288,26 @@ class QAPISchemaParser(object): self.exprs.append(expr_elem) + def _include(self, include, info, base_dir, previously_included): + incl_abs_fname = os.path.join(base_dir, include) + # catch inclusion cycle + inf = info + while inf: + if incl_abs_fname == os.path.abspath(inf['file']): + raise QAPISemError(info, "Inclusion loop for %s" % include) + inf = inf['parent'] + + # skip multiple include of the same file + if incl_abs_fname in previously_included: + return + try: + fobj = open(incl_abs_fname, 'r') + except IOError as e: + raise QAPISemError(info, '%s: %s' % (e.strerror, include)) + exprs_include = QAPISchemaParser(fobj, previously_included, info) + self.exprs.extend(exprs_include.exprs) + self.docs.extend(exprs_include.docs) + def accept(self, skip_comment=True): while True: self.tok = self.src[self.cursor] -- cgit v1.2.3 From bc52d03ff589a033843b4603cfdfd1518867c626 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:56:51 +0100 Subject: qapi: Make doc comments optional where we don't need them Since we added the documentation generator in commit 3313b61, doc comments are mandatory. That's a very good idea for a schema that needs to be documented, but has proven to be annoying for testing. Make doc comments optional again, but add a new directive { 'pragma': { 'doc-required': true } } to let a QAPI schema require them. Add test cases for the new pragma directive. While there, plug a minor hole in includ directive test coverage. Require documentation in the schemas we actually want documented: qapi-schema.json and qga/qapi-schema.json. We could probably make qapi2texi.py cope with incomplete documentation, but for now, simply make it refuse to run unless the schema has 'doc-required': true. Signed-off-by: Markus Armbruster Message-Id: <1489582656-31133-3-git-send-email-armbru@redhat.com> [qapi-code-gen.txt wording tweaked] Reviewed-by: Eric Blake --- scripts/qapi.py | 24 +++++++++++++++++++++++- scripts/qapi2texi.py | 3 +++ 2 files changed, 26 insertions(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/qapi.py b/scripts/qapi.py index 345cde157e..fe9d3cf36d 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -37,6 +37,9 @@ builtin_types = { 'QType': 'QTYPE_QSTRING', } +# Are documentation comments required? +doc_required = False + # Whitelist of commands allowed to return a non-dictionary returns_whitelist = [ # From QMP: @@ -277,6 +280,15 @@ class QAPISchemaParser(object): "Value of 'include' must be a string") self._include(include, info, os.path.dirname(abs_fname), previously_included) + elif "pragma" in expr: + if len(expr) != 1: + raise QAPISemError(info, "Invalid 'pragma' directive") + pragma = expr['pragma'] + if not isinstance(pragma, dict): + raise QAPISemError( + info, "Value of 'pragma' must be a dictionary") + for name, value in pragma.iteritems(): + self._pragma(name, value, info) else: expr_elem = {'expr': expr, 'info': info} @@ -308,6 +320,16 @@ class QAPISchemaParser(object): self.exprs.extend(exprs_include.exprs) self.docs.extend(exprs_include.docs) + def _pragma(self, name, value, info): + global doc_required + if name == 'doc-required': + if not isinstance(value, bool): + raise QAPISemError(info, + "Pragma 'doc-required' must be boolean") + doc_required = value + else: + raise QAPISemError(info, "Unknown pragma '%s'" % name) + def accept(self, skip_comment=True): while True: self.tok = self.src[self.cursor] @@ -863,7 +885,7 @@ def check_exprs(exprs): expr = expr_elem['expr'] info = expr_elem['info'] - if 'doc' not in expr_elem: + if 'doc' not in expr_elem and doc_required: raise QAPISemError(info, "Expression missing documentation comment") diff --git a/scripts/qapi2texi.py b/scripts/qapi2texi.py index 69f5edce4d..06d6abfa5a 100755 --- a/scripts/qapi2texi.py +++ b/scripts/qapi2texi.py @@ -254,6 +254,9 @@ def main(argv): sys.exit(1) schema = qapi.QAPISchema(argv[1]) + if not qapi.doc_required: + print >>sys.stderr, ("%s: need pragma 'doc-required' " + "to generate documentation" % argv[0]) print texi(schema.docs) -- cgit v1.2.3 From 1554a8fae984cad4704fb94a8cef3c9b42ef6185 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:56:54 +0100 Subject: qapi: Have each QAPI schema declare its returns white-list qapi.py has a hardcoded white-list of command names that may violate the rules on permitted return types. Add a new pragma directive 'returns-whitelist', and use it to replace the hard-coded white-list. Signed-off-by: Markus Armbruster Message-Id: <1489582656-31133-6-git-send-email-armbru@redhat.com> Reviewed-by: Eric Blake --- scripts/qapi.py | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi.py b/scripts/qapi.py index fe9d3cf36d..1d86d85d49 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -41,26 +41,7 @@ builtin_types = { doc_required = False # Whitelist of commands allowed to return a non-dictionary -returns_whitelist = [ - # From QMP: - 'human-monitor-command', - 'qom-get', - 'query-migrate-cache-size', - 'query-tpm-models', - 'query-tpm-types', - 'ringbuf-read', - - # From QGA: - 'guest-file-open', - 'guest-fsfreeze-freeze', - 'guest-fsfreeze-freeze-list', - 'guest-fsfreeze-status', - 'guest-fsfreeze-thaw', - 'guest-get-time', - 'guest-set-vcpus', - 'guest-sync', - 'guest-sync-delimited', -] +returns_whitelist = [] # Whitelist of entities allowed to violate case conventions case_whitelist = [ @@ -321,12 +302,19 @@ class QAPISchemaParser(object): self.docs.extend(exprs_include.docs) def _pragma(self, name, value, info): - global doc_required + global doc_required, returns_whitelist if name == 'doc-required': if not isinstance(value, bool): raise QAPISemError(info, "Pragma 'doc-required' must be boolean") doc_required = value + elif name == 'returns-whitelist': + if (not isinstance(value, list) + or any([not isinstance(elt, str) for elt in value])): + raise QAPISemError(info, + "Pragma returns-whitelist must be" + " a list of strings") + returns_whitelist = value else: raise QAPISemError(info, "Unknown pragma '%s'" % name) -- cgit v1.2.3 From 2cfbae3c423ecd13a7722ac7a7dca7ec4168e2ff Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:56:55 +0100 Subject: qapi: Have each QAPI schema declare its name rule violations qapi.py has a hardcoded white-list of type names that may violate the rule on use of upper and lower case. Add a new pragma directive 'name-case-whitelist', and use it to replace the hard-coded white-list. Signed-off-by: Markus Armbruster Message-Id: <1489582656-31133-7-git-send-email-armbru@redhat.com> Reviewed-by: Eric Blake --- scripts/qapi.py | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi.py b/scripts/qapi.py index 1d86d85d49..78db319831 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -44,16 +44,7 @@ doc_required = False returns_whitelist = [] # Whitelist of entities allowed to violate case conventions -case_whitelist = [ - # From QMP: - 'ACPISlotType', # DIMM, visible through query-acpi-ospm-status - 'CpuInfoMIPS', # PC, visible through query-cpu - 'CpuInfoTricore', # PC, visible through query-cpu - 'QapiErrorClass', # all members, visible through errors - 'UuidInfo', # UUID, visible through query-uuid - 'X86CPURegister32', # all members, visible indirectly through qom-get - 'q_obj_CpuInfo-base', # CPU, visible through query-cpu -] +name_case_whitelist = [] enum_types = [] struct_types = [] @@ -302,7 +293,7 @@ class QAPISchemaParser(object): self.docs.extend(exprs_include.docs) def _pragma(self, name, value, info): - global doc_required, returns_whitelist + global doc_required, returns_whitelist, name_case_whitelist if name == 'doc-required': if not isinstance(value, bool): raise QAPISemError(info, @@ -315,6 +306,13 @@ class QAPISchemaParser(object): "Pragma returns-whitelist must be" " a list of strings") returns_whitelist = value + elif name == 'name-case-whitelist': + if (not isinstance(value, list) + or any([not isinstance(elt, str) for elt in value])): + raise QAPISemError(info, + "Pragma name-case-whitelist must be" + " a list of strings") + name_case_whitelist = value else: raise QAPISemError(info, "Unknown pragma '%s'" % name) @@ -1287,7 +1285,7 @@ class QAPISchemaMember(object): def check_clash(self, info, seen): cname = c_name(self.name) - if cname.lower() != cname and self.owner not in case_whitelist: + if cname.lower() != cname and self.owner not in name_case_whitelist: raise QAPISemError(info, "%s should not use uppercase" % self.describe()) if cname in seen: -- cgit v1.2.3 From 481537451302fc63eee5cf3663a92629bb78954f Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:56:58 +0100 Subject: qapi: Fix to reject empty union base gracefully MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Common Python pitfall: 'assert base_members' fires on [] in addition to None. Correct to 'assert base_members is not None'. Signed-off-by: Markus Armbruster Reviewed-by: Marc-André Lureau Reviewed-by: Eric Blake Message-Id: <1489582656-31133-10-git-send-email-armbru@redhat.com> --- scripts/qapi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/qapi.py b/scripts/qapi.py index 78db319831..f4c82100f4 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -735,7 +735,7 @@ def check_union(expr, info): raise QAPISemError(info, "Flat union '%s' must have a base" % name) base_members = find_base_members(base) - assert base_members + assert base_members is not None # The value of member 'discriminator' must name a non-optional # member of the base struct. -- cgit v1.2.3 From 42bebcc129a8bf235f41d65788eb54e199ba5e64 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:56:59 +0100 Subject: qapi2texi: Fix up output around #optional MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We use tag #optional to mark optional members, like this: # @name: #optional The name of the guest texi_body() strips #optional, but not whitespace around it. For the above, we get in qemu-qmp-qapi.texi @item @code{'name'} (optional) The name of the guest @end table The extra space can lead to artifacts in output, e.g in qemu-qmp-ref.7.pod =item C<'name'> (optional) The name of the guest and then in qemu-qmp-ref.7 .IX Item "name (optional)" .Vb 1 \& The name of the guest .Ve instead of intended plain .IX Item "name (optional)" The name of the guest Get rid of these artifacts by removing whitespace around #optional along with it. This turns three minus signs in qapi-schema.json into markup, because they're now at the beginning of the line. Drop them, they're unwanted there. Signed-off-by: Markus Armbruster Reviewed-by: Marc-André Lureau Message-Id: <1489582656-31133-11-git-send-email-armbru@redhat.com> --- scripts/qapi2texi.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/qapi2texi.py b/scripts/qapi2texi.py index 06d6abfa5a..0f3e5738c8 100755 --- a/scripts/qapi2texi.py +++ b/scripts/qapi2texi.py @@ -137,7 +137,8 @@ def texi_body(doc): desc = str(section) opt = '' if "#optional" in desc: - desc = desc.replace("#optional", "") + desc = re.sub(r'^ *#optional *\n?|\n? *#optional *$|#optional', + '', desc) opt = ' (optional)' body += "@item @code{'%s'}%s\n%s\n" % (arg, opt, texi_format(desc)) -- cgit v1.2.3 From b116fd8e302d0ff7cabf431e78ce078127b51f85 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:57:00 +0100 Subject: qapi: Avoid unwanted blank lines in QAPIDoc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We silently fix missing #optional tags for QAPIDoc by appending a line "#optional" to the section's .content. However, this interferes with .__repr__ stripping trailing blank lines from .content. Use new ArgSection instance variable .optional instead, and leave .content alone. To permit testing .optional in texi_body(), clean up texi_enum()'s hack to add empty documentation for undocumented enum values: add an ArgSection instead of ''. Signed-off-by: Markus Armbruster Reviewed-by: Marc-André Lureau Message-Id: <1489582656-31133-12-git-send-email-armbru@redhat.com> --- scripts/qapi.py | 5 +++-- scripts/qapi2texi.py | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi.py b/scripts/qapi.py index f4c82100f4..fb10d937aa 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -107,6 +107,7 @@ class QAPIDoc(object): self.name = name # the list of lines for this section self.content = [] + self.optional = False def append(self, line): self.content.append(line) @@ -982,15 +983,15 @@ def check_definition_doc(doc, expr, info): desc = doc.args.get(arg) if not desc: continue + desc.optional = opt desc_opt = "#optional" in str(desc) if desc_opt and not opt: raise QAPISemError(info, "Description has #optional, " "but the declaration doesn't") if not desc_opt and opt: - # silently fix the doc # TODO either fix the schema and make this an error, # or drop #optional entirely - desc.append("#optional") + pass doc_args = set(doc.args.keys()) args = set([name.strip('*') for name in args]) diff --git a/scripts/qapi2texi.py b/scripts/qapi2texi.py index 0f3e5738c8..0aaf45c98c 100755 --- a/scripts/qapi2texi.py +++ b/scripts/qapi2texi.py @@ -136,7 +136,7 @@ def texi_body(doc): for arg, section in doc.args.iteritems(): desc = str(section) opt = '' - if "#optional" in desc: + if section.optional: desc = re.sub(r'^ *#optional *\n?|\n? *#optional *$|#optional', '', desc) opt = ' (optional)' @@ -185,7 +185,7 @@ def texi_enum(expr, doc): """Format an enum to texi""" for i in expr['data']: if i not in doc.args: - doc.args[i] = '' + doc.args[i] = qapi.QAPIDoc.ArgSection(i) body = texi_body(doc) return TYPE_FMT(type="Enum", name=doc.symbol, -- cgit v1.2.3 From 4636211e4dddfc798d9e9072546a87f9adf7ce5a Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:57:02 +0100 Subject: qapi: Fix QAPISchemaEnumType.is_implicit() for 'QType' Missed in commit 7264f5c. Harmless, because nothing checks whether an enumeration type is implicit so far. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <1489582656-31133-14-git-send-email-armbru@redhat.com> --- scripts/qapi.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi.py b/scripts/qapi.py index fb10d937aa..dd083b703c 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -1152,8 +1152,8 @@ class QAPISchemaEnumType(QAPISchemaType): v.check_clash(self.info, seen) def is_implicit(self): - # See QAPISchema._make_implicit_enum_type() - return self.name.endswith('Kind') + # See QAPISchema._make_implicit_enum_type() and ._def_predefineds() + return self.name.endswith('Kind') or self.name == 'QType' def c_type(self): return c_name(self.name) -- cgit v1.2.3 From 069fb5b250c8f90caeb84dcc003e2147ccc4a782 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:57:03 +0100 Subject: qapi: Prepare for requiring more complete documentation We currently neglect to check all enumeration values, common members of object types and members of alternate types are documented. Unsurprisingly, many aren't. Add the necessary plumbing to find undocumented ones, except for variant members of object types. Don't enforce anything just yet, but connect each QAPIDoc.ArgSection to its QAPISchemaMember. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <1489582656-31133-15-git-send-email-armbru@redhat.com> --- scripts/qapi.py | 110 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 65 insertions(+), 45 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi.py b/scripts/qapi.py index dd083b703c..c1e0bed087 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -116,7 +116,12 @@ class QAPIDoc(object): return "\n".join(self.content).strip() class ArgSection(Section): - pass + def __init__(self, name): + QAPIDoc.Section.__init__(self, name) + self.member = None + + def connect(self, member): + self.member = member def __init__(self, parser, info): # self.parser is used to report errors with QAPIParseError. The @@ -216,6 +221,13 @@ class QAPIDoc(object): line = line.strip() self.section.append(line) + def connect_member(self, member): + if member.name not in self.args: + # Undocumented TODO outlaw + pass + else: + self.args[member.name].connect(member) + class QAPISchemaParser(object): @@ -1021,7 +1033,7 @@ def check_docs(docs): # class QAPISchemaEntity(object): - def __init__(self, name, info): + def __init__(self, name, info, doc): assert isinstance(name, str) self.name = name # For explicitly defined entities, info points to the (explicit) @@ -1030,6 +1042,7 @@ class QAPISchemaEntity(object): # triggered the implicit definition (there may be more than one # such place). self.info = info + self.doc = doc def c_name(self): return c_name(self.name) @@ -1111,7 +1124,7 @@ class QAPISchemaType(QAPISchemaEntity): class QAPISchemaBuiltinType(QAPISchemaType): def __init__(self, name, json_type, c_type): - QAPISchemaType.__init__(self, name, None) + QAPISchemaType.__init__(self, name, None, None) assert not c_type or isinstance(c_type, str) assert json_type in ('string', 'number', 'int', 'boolean', 'null', 'value') @@ -1137,8 +1150,8 @@ class QAPISchemaBuiltinType(QAPISchemaType): class QAPISchemaEnumType(QAPISchemaType): - def __init__(self, name, info, values, prefix): - QAPISchemaType.__init__(self, name, info) + def __init__(self, name, info, doc, values, prefix): + QAPISchemaType.__init__(self, name, info, doc) for v in values: assert isinstance(v, QAPISchemaMember) v.set_owner(name) @@ -1150,6 +1163,8 @@ class QAPISchemaEnumType(QAPISchemaType): seen = {} for v in self.values: v.check_clash(self.info, seen) + if self.doc: + self.doc.connect_member(v) def is_implicit(self): # See QAPISchema._make_implicit_enum_type() and ._def_predefineds() @@ -1171,7 +1186,7 @@ class QAPISchemaEnumType(QAPISchemaType): class QAPISchemaArrayType(QAPISchemaType): def __init__(self, name, info, element_type): - QAPISchemaType.__init__(self, name, info) + QAPISchemaType.__init__(self, name, info, None) assert isinstance(element_type, str) self._element_type_name = element_type self.element_type = None @@ -1194,11 +1209,11 @@ class QAPISchemaArrayType(QAPISchemaType): class QAPISchemaObjectType(QAPISchemaType): - def __init__(self, name, info, base, local_members, variants): + def __init__(self, name, info, doc, base, local_members, variants): # 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) + QAPISchemaType.__init__(self, name, info, doc) assert base is None or isinstance(base, str) for m in local_members: assert isinstance(m, QAPISchemaObjectTypeMember) @@ -1228,6 +1243,8 @@ 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) self.members = seen.values() if self.variants: self.variants.check(schema, seen) @@ -1382,8 +1399,8 @@ class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember): class QAPISchemaAlternateType(QAPISchemaType): - def __init__(self, name, info, variants): - QAPISchemaType.__init__(self, name, info) + def __init__(self, name, info, doc, variants): + QAPISchemaType.__init__(self, name, info, doc) assert isinstance(variants, QAPISchemaObjectTypeVariants) assert variants.tag_member variants.set_owner(name) @@ -1400,6 +1417,8 @@ class QAPISchemaAlternateType(QAPISchemaType): seen = {} for v in self.variants.variants: v.check_clash(self.info, seen) + if self.doc: + self.doc.connect_member(v) def c_type(self): return c_name(self.name) + pointer_suffix @@ -1415,9 +1434,9 @@ class QAPISchemaAlternateType(QAPISchemaType): class QAPISchemaCommand(QAPISchemaEntity): - def __init__(self, name, info, arg_type, ret_type, gen, success_response, - boxed): - QAPISchemaEntity.__init__(self, name, info) + def __init__(self, name, info, doc, arg_type, ret_type, + gen, success_response, boxed): + QAPISchemaEntity.__init__(self, name, info, doc) assert not arg_type or isinstance(arg_type, str) assert not ret_type or isinstance(ret_type, str) self._arg_type_name = arg_type @@ -1454,8 +1473,8 @@ class QAPISchemaCommand(QAPISchemaEntity): class QAPISchemaEvent(QAPISchemaEntity): - def __init__(self, name, info, arg_type, boxed): - QAPISchemaEntity.__init__(self, name, info) + def __init__(self, name, info, doc, arg_type, boxed): + QAPISchemaEntity.__init__(self, name, info, doc) assert not arg_type or isinstance(arg_type, str) self._arg_type_name = arg_type self.arg_type = None @@ -1537,14 +1556,14 @@ class QAPISchema(object): ('bool', 'boolean', 'bool'), ('any', 'value', 'QObject' + pointer_suffix)]: self._def_builtin_type(*t) - self.the_empty_object_type = QAPISchemaObjectType('q_empty', None, - None, [], None) + self.the_empty_object_type = QAPISchemaObjectType( + 'q_empty', None, None, None, [], None) self._def_entity(self.the_empty_object_type) qtype_values = self._make_enum_members(['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']) - self._def_entity(QAPISchemaEnumType('QType', None, qtype_values, - 'QTYPE')) + self._def_entity(QAPISchemaEnumType('QType', None, None, + qtype_values, 'QTYPE')) def _make_enum_members(self, values): return [QAPISchemaMember(v) for v in values] @@ -1553,7 +1572,7 @@ class QAPISchema(object): # See also QAPISchemaObjectTypeMember._pretty_owner() name = name + 'Kind' # Use namespace reserved by add_name() self._def_entity(QAPISchemaEnumType( - name, info, self._make_enum_members(values), None)) + name, info, None, self._make_enum_members(values), None)) return name def _make_array_type(self, element_type, info): @@ -1562,22 +1581,22 @@ class QAPISchema(object): self._def_entity(QAPISchemaArrayType(name, info, element_type)) return name - def _make_implicit_object_type(self, name, info, role, members): + def _make_implicit_object_type(self, name, info, doc, role, members): if not members: return None # See also QAPISchemaObjectTypeMember._pretty_owner() name = 'q_obj_%s-%s' % (name, role) if not self.lookup_entity(name, QAPISchemaObjectType): - self._def_entity(QAPISchemaObjectType(name, info, None, + self._def_entity(QAPISchemaObjectType(name, info, doc, None, members, None)) return name - def _def_enum_type(self, expr, info): + def _def_enum_type(self, expr, info, doc): name = expr['enum'] data = expr['data'] prefix = expr.get('prefix') self._def_entity(QAPISchemaEnumType( - name, info, self._make_enum_members(data), prefix)) + name, info, doc, self._make_enum_members(data), prefix)) def _make_member(self, name, typ, info): optional = False @@ -1593,11 +1612,11 @@ class QAPISchema(object): return [self._make_member(key, value, info) for (key, value) in data.iteritems()] - def _def_struct_type(self, expr, info): + def _def_struct_type(self, expr, info, doc): name = expr['struct'] base = expr.get('base') data = expr['data'] - self._def_entity(QAPISchemaObjectType(name, info, base, + self._def_entity(QAPISchemaObjectType(name, info, doc, base, self._make_members(data, info), None)) @@ -1609,10 +1628,10 @@ class QAPISchema(object): assert len(typ) == 1 typ = self._make_array_type(typ[0], info) typ = self._make_implicit_object_type( - typ, info, 'wrapper', [self._make_member('data', typ, info)]) + typ, info, None, 'wrapper', [self._make_member('data', typ, info)]) return QAPISchemaObjectTypeVariant(case, typ) - def _def_union_type(self, expr, info): + def _def_union_type(self, expr, info, doc): name = expr['union'] data = expr['data'] base = expr.get('base') @@ -1620,7 +1639,7 @@ class QAPISchema(object): tag_member = None if isinstance(base, dict): base = (self._make_implicit_object_type( - name, info, 'base', self._make_members(base, info))) + name, info, doc, 'base', self._make_members(base, info))) if tag_name: variants = [self._make_variant(key, value) for (key, value) in data.iteritems()] @@ -1633,24 +1652,24 @@ class QAPISchema(object): tag_member = QAPISchemaObjectTypeMember('type', typ, False) members = [tag_member] self._def_entity( - QAPISchemaObjectType(name, info, base, members, + QAPISchemaObjectType(name, info, doc, base, members, QAPISchemaObjectTypeVariants(tag_name, tag_member, variants))) - def _def_alternate_type(self, expr, info): + def _def_alternate_type(self, expr, info, doc): name = expr['alternate'] data = expr['data'] variants = [self._make_variant(key, value) for (key, value) in data.iteritems()] tag_member = QAPISchemaObjectTypeMember('type', 'QType', False) self._def_entity( - QAPISchemaAlternateType(name, info, + QAPISchemaAlternateType(name, info, doc, QAPISchemaObjectTypeVariants(None, tag_member, variants))) - def _def_command(self, expr, info): + def _def_command(self, expr, info, doc): name = expr['command'] data = expr.get('data') rets = expr.get('returns') @@ -1659,38 +1678,39 @@ class QAPISchema(object): boxed = expr.get('boxed', False) if isinstance(data, OrderedDict): data = self._make_implicit_object_type( - name, info, 'arg', self._make_members(data, info)) + name, info, doc, 'arg', self._make_members(data, info)) if isinstance(rets, list): assert len(rets) == 1 rets = self._make_array_type(rets[0], info) - self._def_entity(QAPISchemaCommand(name, info, data, rets, gen, - success_response, boxed)) + self._def_entity(QAPISchemaCommand(name, info, doc, data, rets, + gen, success_response, boxed)) - def _def_event(self, expr, info): + def _def_event(self, expr, info, doc): name = expr['event'] data = expr.get('data') boxed = expr.get('boxed', False) if isinstance(data, OrderedDict): data = self._make_implicit_object_type( - name, info, 'arg', self._make_members(data, info)) - self._def_entity(QAPISchemaEvent(name, info, data, boxed)) + name, info, doc, 'arg', self._make_members(data, info)) + self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed)) def _def_exprs(self): for expr_elem in self.exprs: expr = expr_elem['expr'] info = expr_elem['info'] + doc = expr_elem.get('doc') if 'enum' in expr: - self._def_enum_type(expr, info) + self._def_enum_type(expr, info, doc) elif 'struct' in expr: - self._def_struct_type(expr, info) + self._def_struct_type(expr, info, doc) elif 'union' in expr: - self._def_union_type(expr, info) + self._def_union_type(expr, info, doc) elif 'alternate' in expr: - self._def_alternate_type(expr, info) + self._def_alternate_type(expr, info, doc) elif 'command' in expr: - self._def_command(expr, info) + self._def_command(expr, info, doc) elif 'event' in expr: - self._def_event(expr, info) + self._def_event(expr, info, doc) else: assert False -- cgit v1.2.3 From 860e87786123368a97c879b6e7459b3f519bbc97 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:57:04 +0100 Subject: qapi: Conjure up QAPIDoc.ArgSection for undocumented members qapi2texi.py already conjures up ArgSections for undocumented enumeration values, in texi_enum. Drop that, and conjure them up for all kinds of "arguments" (enumeration values, object and alternate type members) in qapi.py instead. Take care to keep generated documentation exactly the same for now. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <1489582656-31133-16-git-send-email-armbru@redhat.com> --- scripts/qapi.py | 5 ++--- scripts/qapi2texi.py | 31 ++++++++++++++++--------------- 2 files changed, 18 insertions(+), 18 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi.py b/scripts/qapi.py index c1e0bed087..f4c8eac8f6 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -224,9 +224,8 @@ class QAPIDoc(object): def connect_member(self, member): if member.name not in self.args: # Undocumented TODO outlaw - pass - else: - self.args[member.name].connect(member) + self.args[member.name] = QAPIDoc.ArgSection(member.name) + self.args[member.name].connect(member) class QAPISchemaParser(object): diff --git a/scripts/qapi2texi.py b/scripts/qapi2texi.py index 0aaf45c98c..299dcf92d8 100755 --- a/scripts/qapi2texi.py +++ b/scripts/qapi2texi.py @@ -123,7 +123,7 @@ def texi_format(doc): return "\n".join(lines) -def texi_body(doc): +def texi_body(doc, only_documented=False): """ Format the body of a symbol documentation: - main body @@ -131,17 +131,21 @@ def texi_body(doc): - followed by "Returns/Notes/Since/Example" sections """ body = texi_format(str(doc.body)) + "\n" - if doc.args: + + args = '' + for name, section in doc.args.iteritems(): + if not section.content and not only_documented: + continue # Undocumented TODO require doc and drop + desc = str(section) + opt = '' + if section.optional: + desc = re.sub(r'^ *#optional *\n?|\n? *#optional *$|#optional', + '', desc) + opt = ' (optional)' + args += "@item @code{'%s'}%s\n%s\n" % (name, opt, texi_format(desc)) + if args: body += "@table @asis\n" - for arg, section in doc.args.iteritems(): - desc = str(section) - opt = '' - if section.optional: - desc = re.sub(r'^ *#optional *\n?|\n? *#optional *$|#optional', - '', desc) - opt = ' (optional)' - body += "@item @code{'%s'}%s\n%s\n" % (arg, opt, - texi_format(desc)) + body += args body += "@end table\n" for section in doc.sections: @@ -183,10 +187,7 @@ def texi_union(expr, doc): def texi_enum(expr, doc): """Format an enum to texi""" - for i in expr['data']: - if i not in doc.args: - doc.args[i] = qapi.QAPIDoc.ArgSection(i) - body = texi_body(doc) + body = texi_body(doc, True) return TYPE_FMT(type="Enum", name=doc.symbol, body=body) -- cgit v1.2.3 From aa964b7fdc2b9c6fd0dd530c44563b2a9d891d0f Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:57:05 +0100 Subject: qapi2texi: Convert to QAPISchemaVisitor qapi2texi works with schema expression trees. Such a tight coupling to schema language syntax is not a good idea. Convert it to the visitor interface the other generators use. No change to generated documentation. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <1489582656-31133-17-git-send-email-armbru@redhat.com> --- scripts/qapi2texi.py | 228 ++++++++++++++++++++++++++------------------------- 1 file changed, 118 insertions(+), 110 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi2texi.py b/scripts/qapi2texi.py index 299dcf92d8..6d4e75713d 100755 --- a/scripts/qapi2texi.py +++ b/scripts/qapi2texi.py @@ -123,31 +123,39 @@ def texi_format(doc): return "\n".join(lines) -def texi_body(doc, only_documented=False): - """ - Format the body of a symbol documentation: - - main body - - table of arguments - - followed by "Returns/Notes/Since/Example" sections - """ - body = texi_format(str(doc.body)) + "\n" - - args = '' - for name, section in doc.args.iteritems(): - if not section.content and not only_documented: - continue # Undocumented TODO require doc and drop - desc = str(section) - opt = '' - if section.optional: - desc = re.sub(r'^ *#optional *\n?|\n? *#optional *$|#optional', - '', desc) - opt = ' (optional)' - args += "@item @code{'%s'}%s\n%s\n" % (name, opt, texi_format(desc)) - if args: - body += "@table @asis\n" - body += args - body += "@end table\n" +def texi_body(doc): + """Format the main documentation body""" + return texi_format(str(doc.body)) + '\n' + + +def texi_enum_value(value): + """Format a table of members item for an enumeration value""" + return "@item @code{'%s'}\n" % value.name + + +def texi_member(member): + """Format a table of members item for an object type member""" + return "@item @code{'%s'}%s\n" % ( + member.name, ' (optional)' if member.optional else '') + +def texi_members(doc, member_func, show_undocumented): + """Format the table of members""" + items = '' + for section in doc.args.itervalues(): + if not section.content and not show_undocumented: + continue # Undocumented TODO require doc and drop + desc = re.sub(r'^ *#optional *\n?|\n? *#optional *$|#optional', + '', str(section)) + items += member_func(section.member) + texi_format(desc) + '\n' + if not items: + return '' + return '@table @asis\n' + items + '@end table\n' + + +def texi_sections(doc): + """Format additional sections following arguments""" + body = '' for section in doc.sections: name, doc = (section.name, str(section)) func = texi_format @@ -159,94 +167,94 @@ def texi_body(doc, only_documented=False): body += "\n\n@b{%s:}\n" % name body += func(doc) - return body -def texi_alternate(expr, doc): - """Format an alternate to texi""" - body = texi_body(doc) - return TYPE_FMT(type="Alternate", - name=doc.symbol, - body=body) - - -def texi_union(expr, doc): - """Format a union to texi""" - discriminator = expr.get("discriminator") - if discriminator: - union = "Flat Union" - else: - union = "Simple Union" - - body = texi_body(doc) - return TYPE_FMT(type=union, - name=doc.symbol, - body=body) - - -def texi_enum(expr, doc): - """Format an enum to texi""" - body = texi_body(doc, True) - return TYPE_FMT(type="Enum", - name=doc.symbol, - body=body) - - -def texi_struct(expr, doc): - """Format a struct to texi""" - body = texi_body(doc) - return TYPE_FMT(type="Struct", - name=doc.symbol, - body=body) - - -def texi_command(expr, doc): - """Format a command to texi""" - body = texi_body(doc) - return MSG_FMT(type="Command", - name=doc.symbol, - body=body) - - -def texi_event(expr, doc): - """Format an event to texi""" - body = texi_body(doc) - return MSG_FMT(type="Event", - name=doc.symbol, - body=body) - - -def texi_expr(expr, doc): - """Format an expr to texi""" - (kind, _) = expr.items()[0] - - fmt = {"command": texi_command, - "struct": texi_struct, - "enum": texi_enum, - "union": texi_union, - "alternate": texi_alternate, - "event": texi_event}[kind] - - return fmt(expr, doc) - - -def texi(docs): - """Convert QAPI schema expressions to texi documentation""" - res = [] - for doc in docs: - expr = doc.expr - if not expr: - res.append(texi_body(doc)) - continue - try: - doc = texi_expr(expr, doc) - res.append(doc) - except: - print >>sys.stderr, "error at @%s" % doc.info - raise - - return '\n'.join(res) +def texi_entity(doc, member_func=texi_member, show_undocumented=False): + return (texi_body(doc) + + texi_members(doc, member_func, show_undocumented) + + texi_sections(doc)) + + +class QAPISchemaGenDocVisitor(qapi.QAPISchemaVisitor): + def __init__(self): + self.out = None + self.cur_doc = None + + def visit_begin(self, schema): + self.out = '' + + def visit_enum_type(self, name, info, values, prefix): + doc = self.cur_doc + if self.out: + self.out += '\n' + self.out += TYPE_FMT(type='Enum', + name=doc.symbol, + body=texi_entity(doc, + member_func=texi_enum_value, + show_undocumented=True)) + + def visit_object_type(self, name, info, base, members, variants): + doc = self.cur_doc + if not variants: + typ = 'Struct' + elif variants._tag_name: # TODO unclean member access + typ = 'Flat Union' + else: + typ = 'Simple Union' + if self.out: + self.out += '\n' + self.out += TYPE_FMT(type=typ, + name=doc.symbol, + body=texi_entity(doc)) + + def visit_alternate_type(self, name, info, variants): + doc = self.cur_doc + if self.out: + self.out += '\n' + self.out += TYPE_FMT(type='Alternate', + name=doc.symbol, + body=texi_entity(doc)) + + def visit_command(self, name, info, arg_type, ret_type, + gen, success_response, boxed): + doc = self.cur_doc + if self.out: + self.out += '\n' + self.out += MSG_FMT(type='Command', + name=doc.symbol, + body=texi_entity(doc)) + + def visit_event(self, name, info, arg_type, boxed): + doc = self.cur_doc + if self.out: + self.out += '\n' + self.out += MSG_FMT(type='Event', + name=doc.symbol, + body=texi_entity(doc)) + + def symbol(self, doc, entity): + self.cur_doc = doc + entity.visit(self) + self.cur_doc = None + + def freeform(self, doc): + assert not doc.args + if self.out: + self.out += '\n' + self.out += texi_body(doc) + texi_sections(doc) + + +def texi_schema(schema): + """Convert QAPI schema documentation to Texinfo""" + gen = QAPISchemaGenDocVisitor() + gen.visit_begin(schema) + for doc in schema.docs: + if doc.symbol: + gen.symbol(doc, schema.lookup_entity(doc.symbol)) + else: + gen.freeform(doc) + return gen.out def main(argv): @@ -259,7 +267,7 @@ def main(argv): if not qapi.doc_required: print >>sys.stderr, ("%s: need pragma 'doc-required' " "to generate documentation" % argv[0]) - print texi(schema.docs) + print texi_schema(schema) if __name__ == "__main__": -- cgit v1.2.3 From 1d8bda128d2ff1f7e589c90d0ac468b95d260757 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:57:06 +0100 Subject: qapi: The #optional tag is redundant, drop We traditionally mark optional members #optional in the doc comment. Before commit 3313b61, this was entirely manual. Commit 3313b61 added some automation because its qapi2texi.py relied on #optional to determine whether a member is optional. This is no longer the case since the previous commit: the only thing qapi2texi.py still does with #optional is stripping it out. We still reject bogus qapi-schema.json and six places for qga/qapi-schema.json. Thus, you can't actually rely on #optional to see whether something is optional. Yet we still make people add it manually. That's just busy-work. Drop the code to check, fix up and strip out #optional, along with all instances of #optional. To keep it out, add code to reject it, to be dropped again once the dust settles. No change to generated documentation. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <1489582656-31133-18-git-send-email-armbru@redhat.com> --- scripts/qapi.py | 23 ++++------------------- scripts/qapi2texi.py | 3 +-- 2 files changed, 5 insertions(+), 21 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi.py b/scripts/qapi.py index f4c8eac8f6..748d7ad296 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -219,6 +219,10 @@ class QAPIDoc(object): if (in_arg or not self.section.name or not self.section.name.startswith("Example")): line = line.strip() + # TODO Drop this once the dust has settled + if (isinstance(self.section, QAPIDoc.ArgSection) + and '#optional' in line): + raise QAPISemError(self.info, "Please drop the #optional tag") self.section.append(line) def connect_member(self, member): @@ -985,25 +989,6 @@ def check_definition_doc(doc, expr, info): or (meta == 'union' and not expr.get('discriminator'))): args.append('type') - for arg in args: - if arg[0] == '*': - opt = True - desc = doc.args.get(arg[1:]) - else: - opt = False - desc = doc.args.get(arg) - if not desc: - continue - desc.optional = opt - desc_opt = "#optional" in str(desc) - if desc_opt and not opt: - raise QAPISemError(info, "Description has #optional, " - "but the declaration doesn't") - if not desc_opt and opt: - # TODO either fix the schema and make this an error, - # or drop #optional entirely - pass - doc_args = set(doc.args.keys()) args = set([name.strip('*') for name in args]) if not doc_args.issubset(args): diff --git a/scripts/qapi2texi.py b/scripts/qapi2texi.py index 6d4e75713d..45834777fc 100755 --- a/scripts/qapi2texi.py +++ b/scripts/qapi2texi.py @@ -145,8 +145,7 @@ def texi_members(doc, member_func, show_undocumented): for section in doc.args.itervalues(): if not section.content and not show_undocumented: continue # Undocumented TODO require doc and drop - desc = re.sub(r'^ *#optional *\n?|\n? *#optional *$|#optional', - '', str(section)) + desc = str(section) items += member_func(section.member) + texi_format(desc) + '\n' if not items: return '' -- cgit v1.2.3 From 0fe675af77e922f3901552be4ac0c454b7dad43d Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:57:07 +0100 Subject: qapi: Use raw strings for regular expressions consistently Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <1489582656-31133-19-git-send-email-armbru@redhat.com> --- scripts/qapi.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi.py b/scripts/qapi.py index 748d7ad296..8088f603a8 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -554,7 +554,7 @@ def discriminator_find_enum_define(expr): # Names must be letters, numbers, -, and _. They must start with letter, # except for downstream extensions which must start with __RFQDN_. # Dots are only valid in the downstream extension prefix. -valid_name = re.compile('^(__[a-zA-Z0-9.-]+_)?' +valid_name = re.compile(r'^(__[a-zA-Z0-9.-]+_)?' '[a-zA-Z][a-zA-Z0-9_-]*$') @@ -1831,10 +1831,10 @@ def cgen(code, **kwds): if indent_level: indent = genindent(indent_level) # re.subn() lacks flags support before Python 2.7, use re.compile() - raw = re.subn(re.compile("^.", re.MULTILINE), + raw = re.subn(re.compile(r'^.', re.MULTILINE), indent + r'\g<0>', raw) raw = raw[0] - return re.sub(re.escape(eatspace) + ' *', '', raw) + return re.sub(re.escape(eatspace) + r' *', '', raw) def mcgen(code, **kwds): @@ -1968,7 +1968,7 @@ def parse_command_line(extra_options="", extra_long_options=[]): for oa in opts: o, a = oa if o in ("-p", "--prefix"): - match = re.match('([A-Za-z_.-][A-Za-z0-9_.-]*)?', a) + match = re.match(r'([A-Za-z_.-][A-Za-z0-9_.-]*)?', a) if match.end() != len(a): print >>sys.stderr, \ "%s: 'funny character '%s' in argument of --prefix" \ -- cgit v1.2.3 From ef801a9bb1e2cf276a8482c4ad1910f72de223f8 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:57:08 +0100 Subject: qapi: Prefer single-quoted strings more consistently PEP 8 advises: In Python, single-quoted strings and double-quoted strings are the same. This PEP does not make a recommendation for this. Pick a rule and stick to it. When a string contains single or double quote characters, however, use the other one to avoid backslashes in the string. It improves readability. The QAPI generators succeed at picking a rule, but fail at sticking to it. Convert a bunch of double-quoted strings to single-quoted ones. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <1489582656-31133-20-git-send-email-armbru@redhat.com> --- scripts/qapi-event.py | 2 +- scripts/qapi-introspect.py | 4 +- scripts/qapi-types.py | 4 +- scripts/qapi-visit.py | 4 +- scripts/qapi.py | 96 +++++++++++++++++++++++----------------------- scripts/qapi2texi.py | 46 +++++++++++----------- 6 files changed, 78 insertions(+), 78 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py index f4eb7f85b1..0485e39145 100644 --- a/scripts/qapi-event.py +++ b/scripts/qapi-event.py @@ -223,7 +223,7 @@ fdecl.write(mcgen(''' ''', prefix=prefix)) -event_enum_name = c_name(prefix + "QAPIEvent", protect=False) +event_enum_name = c_name(prefix + 'QAPIEvent', protect=False) schema = QAPISchema(input_file) gen = QAPISchemaGenEventVisitor() diff --git a/scripts/qapi-introspect.py b/scripts/qapi-introspect.py index fb72c61d02..032bcea491 100644 --- a/scripts/qapi-introspect.py +++ b/scripts/qapi-introspect.py @@ -170,10 +170,10 @@ const char %(c_name)s[] = %(c_string)s; opt_unmask = False (input_file, output_dir, do_c, do_h, prefix, opts) = \ - parse_command_line("u", ["unmask-non-abi-names"]) + parse_command_line('u', ['unmask-non-abi-names']) for o, a in opts: - if o in ("-u", "--unmask-non-abi-names"): + if o in ('-u', '--unmask-non-abi-names'): opt_unmask = True c_comment = ''' diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py index dabc42e047..b45e7b5634 100644 --- a/scripts/qapi-types.py +++ b/scripts/qapi-types.py @@ -244,10 +244,10 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor): do_builtins = False (input_file, output_dir, do_c, do_h, prefix, opts) = \ - parse_command_line("b", ["builtins"]) + parse_command_line('b', ['builtins']) for o, a in opts: - if o in ("-b", "--builtins"): + if o in ('-b', '--builtins'): do_builtins = True c_comment = ''' diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py index 330b9f321b..3d3936e4d0 100644 --- a/scripts/qapi-visit.py +++ b/scripts/qapi-visit.py @@ -335,10 +335,10 @@ class QAPISchemaGenVisitVisitor(QAPISchemaVisitor): do_builtins = False (input_file, output_dir, do_c, do_h, prefix, opts) = \ - parse_command_line("b", ["builtins"]) + parse_command_line('b', ['builtins']) for o, a in opts: - if o in ("-b", "--builtins"): + if o in ('-b', '--builtins'): do_builtins = True c_comment = ''' diff --git a/scripts/qapi.py b/scripts/qapi.py index 8088f603a8..8b7377e51e 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -58,9 +58,9 @@ all_names = {} def error_path(parent): - res = "" + res = '' while parent: - res = ("In file included from %s:%d:\n" % (parent['file'], + res = ('In file included from %s:%d:\n' % (parent['file'], parent['line'])) + res parent = parent['parent'] return res @@ -76,10 +76,10 @@ class QAPIError(Exception): self.msg = msg def __str__(self): - loc = "%s:%d" % (self.fname, self.line) + loc = '%s:%d' % (self.fname, self.line) if self.col is not None: - loc += ":%s" % self.col - return error_path(self.info) + "%s: %s" % (loc, self.msg) + loc += ':%s' % self.col + return error_path(self.info) + '%s: %s' % (loc, self.msg) class QAPIParseError(QAPIError): @@ -113,7 +113,7 @@ class QAPIDoc(object): self.content.append(line) def __repr__(self): - return "\n".join(self.content).strip() + return '\n'.join(self.content).strip() class ArgSection(Section): def __init__(self, name): @@ -163,8 +163,8 @@ class QAPIDoc(object): # recognized, and get silently treated as ordinary text if self.symbol: self._append_symbol_line(line) - elif not self.body.content and line.startswith("@"): - if not line.endswith(":"): + elif not self.body.content and line.startswith('@'): + if not line.endswith(':'): raise QAPIParseError(self.parser, "Line should end with :") self.symbol = line[1:-1] # FIXME invalid names other than the empty string aren't flagged @@ -176,14 +176,14 @@ class QAPIDoc(object): def _append_symbol_line(self, line): name = line.split(' ', 1)[0] - if name.startswith("@") and name.endswith(":"): + if name.startswith('@') and name.endswith(':'): line = line[len(name)+1:] self._start_args_section(name[1:-1]) - elif name in ("Returns:", "Since:", + elif name in ('Returns:', 'Since:', # those are often singular or plural - "Note:", "Notes:", - "Example:", "Examples:", - "TODO:"): + 'Note:', 'Notes:', + 'Example:', 'Examples:', + 'TODO:'): line = line[len(name)+1:] self._start_section(name[:-1]) @@ -203,8 +203,8 @@ class QAPIDoc(object): self.section = QAPIDoc.ArgSection(name) self.args[name] = self.section - def _start_section(self, name=""): - if name in ("Returns", "Since") and self.has_section(name): + def _start_section(self, name=''): + if name in ('Returns', 'Since') and self.has_section(name): raise QAPIParseError(self.parser, "Duplicated '%s' section" % name) self.section = QAPIDoc.Section(name) @@ -217,7 +217,7 @@ class QAPIDoc(object): and line and not line[0].isspace()): self._start_section() if (in_arg or not self.section.name - or not self.section.name.startswith("Example")): + or not self.section.name.startswith('Example')): line = line.strip() # TODO Drop this once the dust has settled if (isinstance(self.section, QAPIDoc.ArgSection) @@ -262,7 +262,7 @@ class QAPISchemaParser(object): if 'include' in expr: if len(expr) != 1: raise QAPISemError(info, "Invalid 'include' directive") - include = expr["include"] + include = expr['include'] if not isinstance(include, str): raise QAPISemError(info, "Value of 'include' must be a string") @@ -347,7 +347,7 @@ class QAPISchemaParser(object): if not skip_comment: self.val = self.src[self.pos:self.cursor] return - elif self.tok in "{}:,[]": + elif self.tok in '{}:,[]': return elif self.tok == "'": string = '' @@ -373,7 +373,7 @@ class QAPISchemaParser(object): for _ in range(0, 4): ch = self.src[self.cursor] self.cursor += 1 - if ch not in "0123456789abcdefABCDEF": + if ch not in '0123456789abcdefABCDEF': raise QAPIParseError(self, '\\u escape needs 4 ' 'hex digits') @@ -388,28 +388,28 @@ class QAPISchemaParser(object): 'only supports non-zero ' 'values up to \\u007f') string += chr(value) - elif ch in "\\/'\"": + elif ch in '\\/\'"': string += ch else: raise QAPIParseError(self, "Unknown escape \\%s" % ch) esc = False - elif ch == "\\": + elif ch == '\\': esc = True elif ch == "'": self.val = string return else: string += ch - elif self.src.startswith("true", self.pos): + elif self.src.startswith('true', self.pos): self.val = True self.cursor += 3 return - elif self.src.startswith("false", self.pos): + elif self.src.startswith('false', self.pos): self.val = False self.cursor += 4 return - elif self.src.startswith("null", self.pos): + elif self.src.startswith('null', self.pos): self.val = None self.cursor += 3 return @@ -523,11 +523,11 @@ def find_alternate_member_qtype(qapi_type): if qapi_type in builtin_types: return builtin_types[qapi_type] elif find_struct(qapi_type): - return "QTYPE_QDICT" + return 'QTYPE_QDICT' elif find_enum(qapi_type): - return "QTYPE_QSTRING" + return 'QTYPE_QSTRING' elif find_union(qapi_type): - return "QTYPE_QDICT" + return 'QTYPE_QDICT' return None @@ -628,7 +628,7 @@ def find_union(name): def add_enum(name, info, enum_values=None, implicit=False): global enum_types add_name(name, info, 'enum', implicit) - enum_types.append({"enum_name": name, "enum_values": enum_values}) + enum_types.append({'enum_name': name, 'enum_values': enum_values}) def find_enum(name): @@ -788,7 +788,7 @@ def check_union(expr, info): raise QAPISemError(info, "Discriminator value '%s' is not found in " "enum '%s'" - % (key, enum_define["enum_name"])) + % (key, enum_define['enum_name'])) # If discriminator is user-defined, ensure all values are covered if enum_define: @@ -993,7 +993,7 @@ def check_definition_doc(doc, expr, info): args = set([name.strip('*') for name in args]) if not doc_args.issubset(args): raise QAPISemError(info, "The following documented members are not in " - "the declaration: %s" % ", ".join(doc_args - args)) + "the declaration: %s" % ', '.join(doc_args - args)) def check_docs(docs): @@ -1487,7 +1487,7 @@ class QAPISchemaEvent(QAPISchemaEntity): class QAPISchema(object): def __init__(self, fname): try: - parser = QAPISchemaParser(open(fname, "r")) + parser = QAPISchemaParser(open(fname, 'r')) self.exprs = check_exprs(parser.exprs) self.docs = check_docs(parser.docs) self._entity_dict = {} @@ -1740,8 +1740,8 @@ def camel_to_upper(value): l = len(c_fun_str) for i in range(l): c = c_fun_str[i] - # When c is upper and no "_" appears before, do more checks - if c.isupper() and (i > 0) and c_fun_str[i - 1] != "_": + # When c is upper and no '_' appears before, do more checks + if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_': if i < l - 1 and c_fun_str[i + 1].islower(): new_name += '_' elif c_fun_str[i - 1].isdigit(): @@ -1760,7 +1760,7 @@ c_name_trans = string.maketrans('.-', '__') # Map @name to a valid C identifier. # If @protect, avoid returning certain ticklish identifiers (like -# C keywords) by prepending "q_". +# C keywords) by prepending 'q_'. # # Used for converting 'name' from a 'name':'type' qapi definition # into a generated struct member, as well as converting type names @@ -1798,7 +1798,7 @@ def c_name(name, protect=True): name = name.translate(c_name_trans) if protect and (name in c89_words | c99_words | c11_words | gcc_words | cpp_words | polluted_words): - return "q_" + name + return 'q_' + name return name eatspace = '\033EATSPACE.' @@ -1806,9 +1806,9 @@ pointer_suffix = ' *' + eatspace def genindent(count): - ret = "" + ret = '' for _ in range(count): - ret += " " + ret += ' ' return ret indent_level = 0 @@ -1948,26 +1948,26 @@ def gen_params(arg_type, boxed, extra): # -def parse_command_line(extra_options="", extra_long_options=[]): +def parse_command_line(extra_options='', extra_long_options=[]): try: opts, args = getopt.gnu_getopt(sys.argv[1:], - "chp:o:" + extra_options, - ["source", "header", "prefix=", - "output-dir="] + extra_long_options) + 'chp:o:' + extra_options, + ['source', 'header', 'prefix=', + 'output-dir='] + extra_long_options) except getopt.GetoptError as err: print >>sys.stderr, "%s: %s" % (sys.argv[0], str(err)) sys.exit(1) - output_dir = "" - prefix = "" + output_dir = '' + prefix = '' do_c = False do_h = False extra_opts = [] for oa in opts: o, a = oa - if o in ("-p", "--prefix"): + if o in ('-p', '--prefix'): match = re.match(r'([A-Za-z_.-][A-Za-z0-9_.-]*)?', a) if match.end() != len(a): print >>sys.stderr, \ @@ -1975,11 +1975,11 @@ def parse_command_line(extra_options="", extra_long_options=[]): % (sys.argv[0], a[match.end()]) sys.exit(1) prefix = a - elif o in ("-o", "--output-dir"): - output_dir = a + "/" - elif o in ("-c", "--source"): + elif o in ('-o', '--output-dir'): + output_dir = a + '/' + elif o in ('-c', '--source'): do_c = True - elif o in ("-h", "--header"): + elif o in ('-h', '--header'): do_h = True else: extra_opts.append(oa) diff --git a/scripts/qapi2texi.py b/scripts/qapi2texi.py index 45834777fc..91cd59391a 100755 --- a/scripts/qapi2texi.py +++ b/scripts/qapi2texi.py @@ -50,7 +50,7 @@ def subst_vars(doc): def subst_braces(doc): """Replaces {} with @{ @}""" - return doc.replace("{", "@{").replace("}", "@}") + return doc.replace('{', '@{').replace('}', '@}') def texi_example(doc): @@ -79,10 +79,10 @@ def texi_format(doc): doc = subst_vars(doc) doc = subst_emph(doc) doc = subst_strong(doc) - inlist = "" + inlist = '' lastempty = False for line in doc.split('\n'): - empty = line == "" + empty = line == '' # FIXME: Doing this in a single if / elif chain is # problematic. For instance, a line without markup terminates @@ -92,35 +92,35 @@ def texi_format(doc): # # Make sure to update section "Documentation markup" in # docs/qapi-code-gen.txt when fixing this. - if line.startswith("| "): + if line.startswith('| '): line = EXAMPLE_FMT(code=line[2:]) - elif line.startswith("= "): - line = "@section " + line[2:] - elif line.startswith("== "): - line = "@subsection " + line[3:] + elif line.startswith('= '): + line = '@section ' + line[2:] + elif line.startswith('== '): + line = '@subsection ' + line[3:] elif re.match(r'^([0-9]*\.) ', line): if not inlist: - lines.append("@enumerate") - inlist = "enumerate" - line = line[line.find(" ")+1:] - lines.append("@item") + lines.append('@enumerate') + inlist = 'enumerate' + line = line[line.find(' ')+1:] + lines.append('@item') elif re.match(r'^[*-] ', line): if not inlist: - lines.append("@itemize %s" % {'*': "@bullet", - '-': "@minus"}[line[0]]) - inlist = "itemize" - lines.append("@item") + lines.append('@itemize %s' % {'*': '@bullet', + '-': '@minus'}[line[0]]) + inlist = 'itemize' + lines.append('@item') line = line[2:] elif lastempty and inlist: - lines.append("@end %s\n" % inlist) - inlist = "" + lines.append('@end %s\n' % inlist) + inlist = '' lastempty = empty lines.append(line) if inlist: - lines.append("@end %s\n" % inlist) - return "\n".join(lines) + lines.append('@end %s\n' % inlist) + return '\n'.join(lines) def texi_body(doc): @@ -158,12 +158,12 @@ def texi_sections(doc): for section in doc.sections: name, doc = (section.name, str(section)) func = texi_format - if name.startswith("Example"): + if name.startswith('Example'): func = texi_example if name: # prefer @b over @strong, so txt doesn't translate it to *Foo:* - body += "\n\n@b{%s:}\n" % name + body += '\n\n@b{%s:}\n' % name body += func(doc) return body @@ -269,5 +269,5 @@ def main(argv): print texi_schema(schema) -if __name__ == "__main__": +if __name__ == '__main__': main(sys.argv) -- cgit v1.2.3 From 71d918a1b191adaabd009910163f996289666ee7 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:57:09 +0100 Subject: qapi2texi: Plainer enum value and member name formatting Use @code{%s} instead of @code{'%s'}. Impact, using @id as example: * Texinfo -@item @code{'id'} +@item @code{id} * HTML -
'id'
+
id
* POD (for manual pages): -=item C<'id'> +=item C * Formatted manual pages: -'id' +"id" * Plain text: - ''id'' + 'id' Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <1489582656-31133-21-git-send-email-armbru@redhat.com> --- scripts/qapi2texi.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi2texi.py b/scripts/qapi2texi.py index 91cd59391a..ecfaeda89e 100755 --- a/scripts/qapi2texi.py +++ b/scripts/qapi2texi.py @@ -130,12 +130,12 @@ def texi_body(doc): def texi_enum_value(value): """Format a table of members item for an enumeration value""" - return "@item @code{'%s'}\n" % value.name + return '@item @code{%s}\n' % value.name def texi_member(member): """Format a table of members item for an object type member""" - return "@item @code{'%s'}%s\n" % ( + return '@item @code{%s}%s\n' % ( member.name, ' (optional)' if member.optional else '') -- cgit v1.2.3 From 2a1183ce9399657a896c51f388e6c7ca58f5d56d Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:57:10 +0100 Subject: qapi2texi: Present the table of members more clearly The table of members follows the main descriptive text immediately. Makes it hard to see what it is about. Start a new paragraph, and lead with a line "Members:" for object and alternate types, "Values:" for enumeration types, and "Arguments:" for commands and events. Example change (qemu-qmp-ref.txt): -- Command: set_link Sets the link status of a virtual network adapter. + + Arguments: 'name' the device name of the virtual network adapter 'up' true to set the link status to be up Returns: Nothing on success If 'name' is not a valid network device, DeviceNotFound Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <1489582656-31133-22-git-send-email-armbru@redhat.com> --- scripts/qapi2texi.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi2texi.py b/scripts/qapi2texi.py index ecfaeda89e..35e182d543 100755 --- a/scripts/qapi2texi.py +++ b/scripts/qapi2texi.py @@ -139,7 +139,7 @@ def texi_member(member): member.name, ' (optional)' if member.optional else '') -def texi_members(doc, member_func, show_undocumented): +def texi_members(doc, what, member_func, show_undocumented): """Format the table of members""" items = '' for section in doc.args.itervalues(): @@ -149,7 +149,7 @@ def texi_members(doc, member_func, show_undocumented): items += member_func(section.member) + texi_format(desc) + '\n' if not items: return '' - return '@table @asis\n' + items + '@end table\n' + return '\n@b{%s:}\n@table @asis\n%s@end table\n' % (what, items) def texi_sections(doc): @@ -169,9 +169,10 @@ def texi_sections(doc): return body -def texi_entity(doc, member_func=texi_member, show_undocumented=False): +def texi_entity(doc, what, member_func=texi_member, + show_undocumented=False): return (texi_body(doc) - + texi_members(doc, member_func, show_undocumented) + + texi_members(doc, what, member_func, show_undocumented) + texi_sections(doc)) @@ -189,7 +190,7 @@ class QAPISchemaGenDocVisitor(qapi.QAPISchemaVisitor): self.out += '\n' self.out += TYPE_FMT(type='Enum', name=doc.symbol, - body=texi_entity(doc, + body=texi_entity(doc, 'Values', member_func=texi_enum_value, show_undocumented=True)) @@ -205,7 +206,7 @@ class QAPISchemaGenDocVisitor(qapi.QAPISchemaVisitor): self.out += '\n' self.out += TYPE_FMT(type=typ, name=doc.symbol, - body=texi_entity(doc)) + body=texi_entity(doc, 'Members')) def visit_alternate_type(self, name, info, variants): doc = self.cur_doc @@ -213,7 +214,7 @@ class QAPISchemaGenDocVisitor(qapi.QAPISchemaVisitor): self.out += '\n' self.out += TYPE_FMT(type='Alternate', name=doc.symbol, - body=texi_entity(doc)) + body=texi_entity(doc, 'Members')) def visit_command(self, name, info, arg_type, ret_type, gen, success_response, boxed): @@ -222,7 +223,7 @@ class QAPISchemaGenDocVisitor(qapi.QAPISchemaVisitor): self.out += '\n' self.out += MSG_FMT(type='Command', name=doc.symbol, - body=texi_entity(doc)) + body=texi_entity(doc, 'Arguments')) def visit_event(self, name, info, arg_type, boxed): doc = self.cur_doc @@ -230,7 +231,7 @@ class QAPISchemaGenDocVisitor(qapi.QAPISchemaVisitor): self.out += '\n' self.out += MSG_FMT(type='Event', name=doc.symbol, - body=texi_entity(doc)) + body=texi_entity(doc, 'Arguments')) def symbol(self, doc, entity): self.cur_doc = doc -- cgit v1.2.3 From 5da19f14ff3393dc84f0f84c50770affd2c10acf Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:57:11 +0100 Subject: qapi2texi: Explain enum value undocumentedness more clearly Instead of not saying anything when we have no documentation, say "Not documented". Example change (qemu-qmp-ref.txt): -- Enum: GuestPanicAction An enumeration of the actions taken when guest OS panic is detected Values: 'pause' system pauses 'poweroff' + Not documented Since: 2.1 (poweroff since 2.8) Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <1489582656-31133-23-git-send-email-armbru@redhat.com> --- scripts/qapi2texi.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/qapi2texi.py b/scripts/qapi2texi.py index 35e182d543..1f01817be5 100755 --- a/scripts/qapi2texi.py +++ b/scripts/qapi2texi.py @@ -145,7 +145,10 @@ def texi_members(doc, what, member_func, show_undocumented): for section in doc.args.itervalues(): if not section.content and not show_undocumented: continue # Undocumented TODO require doc and drop - desc = str(section) + if section.content: + desc = str(section) + else: + desc = 'Not documented' items += member_func(section.member) + texi_format(desc) + '\n' if not items: return '' -- cgit v1.2.3 From 2c99f5fdc8a1e3decbb2c3bd99090ecb816a3d95 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:57:12 +0100 Subject: qapi2texi: Don't hide undocumented members and arguments Show undocumented object, alternate type members and command, event arguments exactly like undocumented enumeration type values. Example change (qemu-qmp-ref.txt): -- Command: query-rocker Return rocker switch information. + Arguments: + 'name' + Not documented + Returns: 'Rocker' information Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <1489582656-31133-24-git-send-email-armbru@redhat.com> --- scripts/qapi2texi.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi2texi.py b/scripts/qapi2texi.py index 1f01817be5..df874415e3 100755 --- a/scripts/qapi2texi.py +++ b/scripts/qapi2texi.py @@ -139,12 +139,10 @@ def texi_member(member): member.name, ' (optional)' if member.optional else '') -def texi_members(doc, what, member_func, show_undocumented): +def texi_members(doc, what, member_func): """Format the table of members""" items = '' for section in doc.args.itervalues(): - if not section.content and not show_undocumented: - continue # Undocumented TODO require doc and drop if section.content: desc = str(section) else: @@ -172,10 +170,9 @@ def texi_sections(doc): return body -def texi_entity(doc, what, member_func=texi_member, - show_undocumented=False): +def texi_entity(doc, what, member_func=texi_member): return (texi_body(doc) - + texi_members(doc, what, member_func, show_undocumented) + + texi_members(doc, what, member_func) + texi_sections(doc)) @@ -194,8 +191,7 @@ class QAPISchemaGenDocVisitor(qapi.QAPISchemaVisitor): self.out += TYPE_FMT(type='Enum', name=doc.symbol, body=texi_entity(doc, 'Values', - member_func=texi_enum_value, - show_undocumented=True)) + member_func=texi_enum_value)) def visit_object_type(self, name, info, base, members, variants): doc = self.cur_doc -- cgit v1.2.3 From c2dd311cb72b0ef59287aad3c0c7ee968c7289e2 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:57:13 +0100 Subject: qapi2texi: Implement boxed argument documentation This replaces manual references like "For the arguments, see the documentation of ..." by a generated reference "Arguments: the members of ...". Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <1489582656-31133-25-git-send-email-armbru@redhat.com> --- scripts/qapi2texi.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/qapi2texi.py b/scripts/qapi2texi.py index df874415e3..3dd0146ba0 100755 --- a/scripts/qapi2texi.py +++ b/scripts/qapi2texi.py @@ -220,9 +220,15 @@ class QAPISchemaGenDocVisitor(qapi.QAPISchemaVisitor): doc = self.cur_doc if self.out: self.out += '\n' + if boxed: + body = texi_body(doc) + body += '\n@b{Arguments:} the members of @code{%s}' % arg_type.name + body += texi_sections(doc) + else: + body = texi_entity(doc, 'Arguments') self.out += MSG_FMT(type='Command', name=doc.symbol, - body=texi_entity(doc, 'Arguments')) + body=body) def visit_event(self, name, info, arg_type, boxed): doc = self.cur_doc -- cgit v1.2.3 From 691e03133e79fd1f70ea4b524cdd10cbc23fd72a Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:57:14 +0100 Subject: qapi2texi: Include member type in generated documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The recent merge of docs/qmp-commands.txt and docs/qmp-events.txt into the schema lost type information. Fix this documentation regression. Example change (qemu-qmp-ref.txt): -- Struct: InputKeyEvent Keyboard input event. Members: - 'button' + 'button: InputButton' Which button this event is for. - 'down' + 'down: boolean' True for key-down and false for key-up events. Since: 2.0 Signed-off-by: Markus Armbruster Reviewed-by: Marc-André Lureau Reviewed-by: Eric Blake Message-Id: <1489582656-31133-26-git-send-email-armbru@redhat.com> --- scripts/qapi.py | 14 ++++++++++++++ scripts/qapi2texi.py | 8 ++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi.py b/scripts/qapi.py index 8b7377e51e..21a15918dc 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -1105,6 +1105,11 @@ class QAPISchemaType(QAPISchemaEntity): } return json2qtype.get(self.json_type()) + def doc_type(self): + if self.is_implicit(): + return None + return self.name + class QAPISchemaBuiltinType(QAPISchemaType): def __init__(self, name, json_type, c_type): @@ -1129,6 +1134,9 @@ class QAPISchemaBuiltinType(QAPISchemaType): def json_type(self): return self._json_type_name + def doc_type(self): + return self.json_type() + def visit(self, visitor): visitor.visit_builtin_type(self.name, self.info, self.json_type()) @@ -1188,6 +1196,12 @@ class QAPISchemaArrayType(QAPISchemaType): def json_type(self): return 'array' + def doc_type(self): + 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): visitor.visit_array_type(self.name, self.info, self.element_type) diff --git a/scripts/qapi2texi.py b/scripts/qapi2texi.py index 3dd0146ba0..993b65264f 100755 --- a/scripts/qapi2texi.py +++ b/scripts/qapi2texi.py @@ -135,8 +135,12 @@ def texi_enum_value(value): def texi_member(member): """Format a table of members item for an object type member""" - return '@item @code{%s}%s\n' % ( - member.name, ' (optional)' if member.optional else '') + typ = member.type.doc_type() + return '@item @code{%s%s%s}%s\n' % ( + member.name, + ': ' if typ else '', + typ if typ else '', + ' (optional)' if member.optional else '') def texi_members(doc, what, member_func): -- cgit v1.2.3 From 88f63467c5753ec49ea8e7134c6b4b59c03d73b2 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:57:15 +0100 Subject: qapi2texi: Generate reference to base type members The generated documentation doesn't mention object type members inherited from a base type. Fix that. Example change (qemu-qmp-ref.txt): -- Struct: VncServerInfo The network connection information for server Members: 'auth' (optional) authentication method used for the plain (non-websocket) VNC server + The members of 'VncBasicInfo' Since: 2.1 Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <1489582656-31133-27-git-send-email-armbru@redhat.com> --- scripts/qapi2texi.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi2texi.py b/scripts/qapi2texi.py index 993b65264f..7083d0c0d0 100755 --- a/scripts/qapi2texi.py +++ b/scripts/qapi2texi.py @@ -143,7 +143,7 @@ def texi_member(member): ' (optional)' if member.optional else '') -def texi_members(doc, what, member_func): +def texi_members(doc, what, base, member_func): """Format the table of members""" items = '' for section in doc.args.itervalues(): @@ -152,6 +152,8 @@ def texi_members(doc, what, member_func): else: desc = 'Not documented' items += member_func(section.member) + texi_format(desc) + '\n' + if base: + items += '@item The members of @code{%s}\n' % base.doc_type() if not items: return '' return '\n@b{%s:}\n@table @asis\n%s@end table\n' % (what, items) @@ -174,9 +176,9 @@ def texi_sections(doc): return body -def texi_entity(doc, what, member_func=texi_member): +def texi_entity(doc, what, base=None, member_func=texi_member): return (texi_body(doc) - + texi_members(doc, what, member_func) + + texi_members(doc, what, base, member_func) + texi_sections(doc)) @@ -205,11 +207,13 @@ class QAPISchemaGenDocVisitor(qapi.QAPISchemaVisitor): typ = 'Flat Union' else: typ = 'Simple Union' + if base and base.is_implicit(): + base = None if self.out: self.out += '\n' self.out += TYPE_FMT(type=typ, name=doc.symbol, - body=texi_entity(doc, 'Members')) + body=texi_entity(doc, 'Members', base)) def visit_alternate_type(self, name, info, variants): doc = self.cur_doc -- cgit v1.2.3 From 5169cd87673b99be8c017b1969583f0c729917d9 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:57:16 +0100 Subject: qapi2texi: Generate documentation for variant members A flat union's branch brings in the members of another type. Generate a suitable reference to that type. Example change (qemu-qmp-ref.txt): -- Flat Union: QCryptoBlockOpenOptions The options that are available for all encryption formats when opening an existing volume Members: The members of 'QCryptoBlockOptionsBase' + The members of 'QCryptoBlockOptionsQCow' when 'format' is "qcow" + The members of 'QCryptoBlockOptionsLUKS' when 'format' is "luks" Since: 2.6 A simple union's branch adds a member 'data' of some other type. Generate documentation for that member. Example change (qemu-qmp-ref.txt): -- Simple Union: SocketAddress Captures the address of a socket, which could also be a named file descriptor Members: 'type' Not documented + 'data: InetSocketAddress' when 'type' is "inet" + 'data: UnixSocketAddress' when 'type' is "unix" + 'data: VsockSocketAddress' when 'type' is "vsock" + 'data: String' when 'type' is "fd" Since: 1.3 Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <1489582656-31133-28-git-send-email-armbru@redhat.com> --- scripts/qapi2texi.py | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi2texi.py b/scripts/qapi2texi.py index 7083d0c0d0..ab6b6cda25 100755 --- a/scripts/qapi2texi.py +++ b/scripts/qapi2texi.py @@ -133,17 +133,18 @@ def texi_enum_value(value): return '@item @code{%s}\n' % value.name -def texi_member(member): +def texi_member(member, suffix=''): """Format a table of members item for an object type member""" typ = member.type.doc_type() - return '@item @code{%s%s%s}%s\n' % ( + return '@item @code{%s%s%s}%s%s\n' % ( member.name, ': ' if typ else '', typ if typ else '', - ' (optional)' if member.optional else '') + ' (optional)' if member.optional else '', + suffix) -def texi_members(doc, what, base, member_func): +def texi_members(doc, what, base, variants, member_func): """Format the table of members""" items = '' for section in doc.args.itervalues(): @@ -154,6 +155,17 @@ def texi_members(doc, what, base, member_func): items += member_func(section.member) + texi_format(desc) + '\n' if base: items += '@item The members of @code{%s}\n' % base.doc_type() + if variants: + for v in variants.variants: + when = ' when @code{%s} is @t{"%s"}' % ( + variants.tag_member.name, v.name) + if v.type.is_implicit(): + assert not v.type.base and not v.type.variants + for m in v.type.local_members: + items += member_func(m, when) + else: + items += '@item The members of @code{%s}%s\n' % ( + v.type.doc_type(), when) if not items: return '' return '\n@b{%s:}\n@table @asis\n%s@end table\n' % (what, items) @@ -176,9 +188,10 @@ def texi_sections(doc): return body -def texi_entity(doc, what, base=None, member_func=texi_member): +def texi_entity(doc, what, base=None, variants=None, + member_func=texi_member): return (texi_body(doc) - + texi_members(doc, what, base, member_func) + + texi_members(doc, what, base, variants, member_func) + texi_sections(doc)) @@ -213,7 +226,7 @@ class QAPISchemaGenDocVisitor(qapi.QAPISchemaVisitor): self.out += '\n' self.out += TYPE_FMT(type=typ, name=doc.symbol, - body=texi_entity(doc, 'Members', base)) + body=texi_entity(doc, 'Members', base, variants)) def visit_alternate_type(self, name, info, variants): doc = self.cur_doc -- cgit v1.2.3 From c19eaa64df7be01f060e04c0f13bba5de549c3f3 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:57:17 +0100 Subject: qapi2texi: Generate descriptions for simple union tags Simple union tags carry no type information, because their type is implicit. Their description should make up for it, but many have none. Generate one automatically then. Example change (qemu-qmp-ref.txt): -- Simple Union: ImageInfoSpecific A discriminated record of image format specific information structures. Members: 'type' - Not documented + One of "qcow2", "vmdk", "luks" 'data: ImageInfoSpecificQCow2' when 'type' is "qcow2" 'data: ImageInfoSpecificVmdk' when 'type' is "vmdk" 'data: QCryptoBlockInfoLUKS' when 'type' is "luks" Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <1489582656-31133-29-git-send-email-armbru@redhat.com> --- scripts/qapi2texi.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi2texi.py b/scripts/qapi2texi.py index ab6b6cda25..282adf46dc 100755 --- a/scripts/qapi2texi.py +++ b/scripts/qapi2texi.py @@ -148,11 +148,16 @@ def texi_members(doc, what, base, variants, member_func): """Format the table of members""" items = '' for section in doc.args.itervalues(): + # TODO Drop fallbacks when undocumented members are outlawed if section.content: - desc = str(section) + desc = texi_format(str(section)) + elif (variants and variants.tag_member == section.member + and not section.member.type.doc_type()): + values = section.member.type.member_names() + desc = 'One of ' + ', '.join(['@t{"%s"}' % v for v in values]) else: desc = 'Not documented' - items += member_func(section.member) + texi_format(desc) + '\n' + items += member_func(section.member) + desc + '\n' if base: items += '@item The members of @code{%s}\n' % base.doc_type() if variants: -- cgit v1.2.3 From 75b50196d9eee43f0b7d006455b9735ea5c3c333 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:57:18 +0100 Subject: qapi2texi: Use category "Object" for all object types At the protocol level, the distinction between struct, flat union and simple union is meaningless, they are all JSON objects. Document them that way. Example change (qemu-qmp-ref.txt): - -- Simple Union: InputEvent + -- Object: InputEvent Input event union. This also fixes the completely broken headings for flat and simple unions in qemu-qmp-ref.7 and qemu-ga-ref.7, by sidestepping a bug in texi2pod.pl. For instance, it mistranslates "@deftp {Simple Union} InputEvent" to "B (Simple)", but translates "@deftp Object InputEvent" to "B (Object)". Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <1489582656-31133-30-git-send-email-armbru@redhat.com> --- scripts/qapi2texi.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi2texi.py b/scripts/qapi2texi.py index 282adf46dc..8eed11a60c 100755 --- a/scripts/qapi2texi.py +++ b/scripts/qapi2texi.py @@ -219,17 +219,11 @@ class QAPISchemaGenDocVisitor(qapi.QAPISchemaVisitor): def visit_object_type(self, name, info, base, members, variants): doc = self.cur_doc - if not variants: - typ = 'Struct' - elif variants._tag_name: # TODO unclean member access - typ = 'Flat Union' - else: - typ = 'Simple Union' if base and base.is_implicit(): base = None if self.out: self.out += '\n' - self.out += TYPE_FMT(type=typ, + self.out += TYPE_FMT(type='Object', name=doc.symbol, body=texi_entity(doc, 'Members', base, variants)) -- cgit v1.2.3 From e7823a2adf7222d0513b8e7cfd8af85d407d4918 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:57:20 +0100 Subject: qapi: Fix detection of doc / expression mismatch This fixes the errors uncovered by the previous commit. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <1489582656-31133-32-git-send-email-armbru@redhat.com> --- scripts/qapi.py | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi.py b/scripts/qapi.py index 21a15918dc..37f28146eb 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -248,18 +248,21 @@ class QAPISchemaParser(object): self.line_pos = 0 self.exprs = [] self.docs = [] + self.cur_doc = None self.accept() while self.tok is not None: info = {'file': fname, 'line': self.line, 'parent': self.incl_info} if self.tok == '#': - doc = self.get_doc(info) - self.docs.append(doc) + self.reject_expr_doc() + self.cur_doc = self.get_doc(info) + self.docs.append(self.cur_doc) continue expr = self.get_expr(False) if 'include' in expr: + self.reject_expr_doc() if len(expr) != 1: raise QAPISemError(info, "Invalid 'include' directive") include = expr['include'] @@ -269,6 +272,7 @@ class QAPISchemaParser(object): self._include(include, info, os.path.dirname(abs_fname), previously_included) elif "pragma" in expr: + self.reject_expr_doc() if len(expr) != 1: raise QAPISemError(info, "Invalid 'pragma' directive") pragma = expr['pragma'] @@ -280,13 +284,23 @@ class QAPISchemaParser(object): else: expr_elem = {'expr': expr, 'info': info} - if (self.docs - and self.docs[-1].info['file'] == fname - and not self.docs[-1].expr): - self.docs[-1].expr = expr - expr_elem['doc'] = self.docs[-1] - + if self.cur_doc: + if not self.cur_doc.symbol: + raise QAPISemError( + self.cur_doc.info, + "Expression documentation required") + self.cur_doc.expr = expr + expr_elem['doc'] = self.cur_doc self.exprs.append(expr_elem) + self.cur_doc = None + self.reject_expr_doc() + + def reject_expr_doc(self): + if self.cur_doc and self.cur_doc.symbol: + raise QAPISemError( + self.cur_doc.info, + "Documentation for '%s' is not followed by the definition" + % self.cur_doc.symbol) def _include(self, include, info, base_dir, previously_included): incl_abs_fname = os.path.join(base_dir, include) @@ -950,11 +964,6 @@ def check_exprs(exprs): def check_freeform_doc(doc): - if doc.symbol: - raise QAPISemError(doc.info, - "Documention for '%s' is not followed" - " by the definition" % doc.symbol) - body = str(doc.body) if re.search(r'@\S+:', body, re.MULTILINE): raise QAPISemError(doc.info, -- cgit v1.2.3 From 7947016d1ceb08584f0d0a3f62b8049ab27219ba Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:57:21 +0100 Subject: qapi: Move detection of doc / expression name mismatch Move the check whether the doc matches the expression name from check_definition_doc() to check_exprs(). This changes the error location from the comment to the expression. Makes sense as the message talks about the expression: "Definition of '%s' follows documentation for '%s'". It's also a step towards getting rid of check_docs(). Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <1489582656-31133-33-git-send-email-armbru@redhat.com> --- scripts/qapi.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi.py b/scripts/qapi.py index 37f28146eb..9a1d830f2f 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -894,40 +894,52 @@ def check_keys(expr_elem, meta, required, optional=[]): def check_exprs(exprs): global all_names - # Learn the types and check for valid expression keys + # Populate name table with names of built-in types for builtin in builtin_types.keys(): all_names[builtin] = 'built-in' + + # Learn the types and check for valid expression keys for expr_elem in exprs: expr = expr_elem['expr'] info = expr_elem['info'] + doc = expr_elem.get('doc') - if 'doc' not in expr_elem and doc_required: + if not doc and doc_required: raise QAPISemError(info, "Expression missing documentation comment") if 'enum' in expr: + name = expr['enum'] check_keys(expr_elem, 'enum', ['data'], ['prefix']) - add_enum(expr['enum'], info, expr['data']) + add_enum(name, info, expr['data']) elif 'union' in expr: + name = expr['union'] check_keys(expr_elem, 'union', ['data'], ['base', 'discriminator']) add_union(expr, info) elif 'alternate' in expr: + name = expr['alternate'] check_keys(expr_elem, 'alternate', ['data']) - add_name(expr['alternate'], info, 'alternate') + add_name(name, info, 'alternate') elif 'struct' in expr: + name = expr['struct'] check_keys(expr_elem, 'struct', ['data'], ['base']) add_struct(expr, info) elif 'command' in expr: + name = expr['command'] check_keys(expr_elem, 'command', [], ['data', 'returns', 'gen', 'success-response', 'boxed']) - add_name(expr['command'], info, 'command') + add_name(name, info, 'command') elif 'event' in expr: + name = expr['event'] check_keys(expr_elem, 'event', [], ['data', 'boxed']) - add_name(expr['event'], info, 'event') + add_name(name, info, 'event') else: raise QAPISemError(expr_elem['info'], "Expression is missing metatype") + if doc and doc.symbol != name: + raise QAPISemError(info, "Definition of '%s' follows documentation" + " for '%s'" % (name, doc.symbol)) # Try again for hidden UnionKind enum for expr_elem in exprs: @@ -977,10 +989,6 @@ def check_definition_doc(doc, expr, info): meta = i break - name = expr[meta] - if doc.symbol != name: - raise QAPISemError(info, "Definition of '%s' follows documentation" - " for '%s'" % (name, doc.symbol)) if doc.has_section('Returns') and 'command' not in expr: raise QAPISemError(info, "'Returns:' is only valid for commands") -- cgit v1.2.3 From 2d433236df5ab15d61a117c3d8e47a4abc651ce0 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:57:22 +0100 Subject: qapi: Improve error message on @NAME: in free-form doc Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <1489582656-31133-34-git-send-email-armbru@redhat.com> --- scripts/qapi.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi.py b/scripts/qapi.py index 9a1d830f2f..4edcea1883 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -219,6 +219,11 @@ class QAPIDoc(object): if (in_arg or not self.section.name or not self.section.name.startswith('Example')): line = line.strip() + match = re.match(r'(@\S+:)', line) + if match: + raise QAPIParseError(self.parser, + "'%s' not allowed in free-form documentation" + % match.group(1)) # TODO Drop this once the dust has settled if (isinstance(self.section, QAPIDoc.ArgSection) and '#optional' in line): @@ -975,14 +980,6 @@ def check_exprs(exprs): return exprs -def check_freeform_doc(doc): - body = str(doc.body) - if re.search(r'@\S+:', body, re.MULTILINE): - raise QAPISemError(doc.info, - "Free-form documentation block must not contain" - " @NAME: sections") - - def check_definition_doc(doc, expr, info): for i in ('enum', 'union', 'alternate', 'struct', 'command', 'event'): if i in expr: @@ -1021,9 +1018,7 @@ def check_docs(docs): raise QAPISemError(doc.info, "Empty doc section '%s'" % section.name) - if not doc.expr: - check_freeform_doc(doc) - else: + if doc.expr: check_definition_doc(doc, doc.expr, doc.info) return docs -- cgit v1.2.3 From 4ea7148e89deda8795b3233bc5ba8c4bf037230e Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:57:23 +0100 Subject: qapi: Move empty doc section checking to doc parser Results in a more precise error location, but the real reason is emptying out check_docs() step by step. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <1489582656-31133-35-git-send-email-armbru@redhat.com> --- scripts/qapi.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi.py b/scripts/qapi.py index 4edcea1883..648355ed0e 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -173,6 +173,9 @@ class QAPIDoc(object): else: self._append_freeform(line) + def end_comment(self): + self._end_section() + def _append_symbol_line(self, line): name = line.split(' ', 1)[0] @@ -200,6 +203,7 @@ class QAPIDoc(object): raise QAPIParseError(self.parser, "'@%s:' can't follow '%s' section" % (name, self.sections[0].name)) + self._end_section() self.section = QAPIDoc.ArgSection(name) self.args[name] = self.section @@ -207,9 +211,18 @@ class QAPIDoc(object): if name in ('Returns', 'Since') and self.has_section(name): raise QAPIParseError(self.parser, "Duplicated '%s' section" % name) + self._end_section() self.section = QAPIDoc.Section(name) self.sections.append(self.section) + def _end_section(self): + if self.section: + contents = str(self.section) + if self.section.name and (not contents or contents.isspace()): + raise QAPIParseError(self.parser, "Empty doc section '%s'" + % self.section.name) + self.section = None + def _append_freeform(self, line): in_arg = isinstance(self.section, QAPIDoc.ArgSection) if (in_arg and self.section.content @@ -512,6 +525,7 @@ class QAPISchemaParser(object): if self.val != '##': raise QAPIParseError(self, "Junk after '##' at end of " "documentation comment") + doc.end_comment() self.accept() return doc else: @@ -1012,12 +1026,6 @@ def check_definition_doc(doc, expr, info): def check_docs(docs): for doc in docs: - for section in doc.args.values() + doc.sections: - content = str(section) - if not content or content.isspace(): - raise QAPISemError(doc.info, - "Empty doc section '%s'" % section.name) - if doc.expr: check_definition_doc(doc, doc.expr, doc.info) -- cgit v1.2.3 From 816a57cd6ebb6fe820766ac12478a56a5fa33d52 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:57:26 +0100 Subject: qapi: Fix detection of bogus member documentation check_definition_doc() checks for member documentation without a matching member. It laboriously second-guesses what members QAPISchema._def_exprs() will create. That's a stupid game. Move the check into QAPISchema.check(), where the members are known. Delegate the actual checking to new QAPIDoc.check(). Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <1489582656-31133-38-git-send-email-armbru@redhat.com> --- scripts/qapi.py | 38 +++++++++++++------------------------- 1 file changed, 13 insertions(+), 25 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi.py b/scripts/qapi.py index 648355ed0e..ca9926b796 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -249,6 +249,15 @@ class QAPIDoc(object): self.args[member.name] = QAPIDoc.ArgSection(member.name) self.args[member.name].connect(member) + def check(self): + bogus = [name for name, section in self.args.iteritems() + if not section.member] + if bogus: + raise QAPISemError( + self.info, + "The following documented members are not in " + "the declaration: %s" % ", ".join(bogus)) + class QAPISchemaParser(object): @@ -995,34 +1004,9 @@ def check_exprs(exprs): def check_definition_doc(doc, expr, info): - for i in ('enum', 'union', 'alternate', 'struct', 'command', 'event'): - if i in expr: - meta = i - break - if doc.has_section('Returns') and 'command' not in expr: raise QAPISemError(info, "'Returns:' is only valid for commands") - if meta == 'union': - args = expr.get('base', []) - else: - args = expr.get('data', []) - if isinstance(args, str): - return - if isinstance(args, dict): - args = args.keys() - assert isinstance(args, list) - - if (meta == 'alternate' - or (meta == 'union' and not expr.get('discriminator'))): - args.append('type') - - doc_args = set(doc.args.keys()) - args = set([name.strip('*') for name in args]) - if not doc_args.issubset(args): - raise QAPISemError(info, "The following documented members are not in " - "the declaration: %s" % ', '.join(doc_args - args)) - def check_docs(docs): for doc in docs: @@ -1268,6 +1252,8 @@ class QAPISchemaObjectType(QAPISchemaType): self.variants.check(schema, seen) assert self.variants.tag_member in self.members self.variants.check_clash(schema, self.info, seen) + if self.doc: + self.doc.check() # 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 @@ -1437,6 +1423,8 @@ class QAPISchemaAlternateType(QAPISchemaType): v.check_clash(self.info, seen) if self.doc: self.doc.connect_member(v) + if self.doc: + self.doc.check() def c_type(self): return c_name(self.name) + pointer_suffix -- cgit v1.2.3 From a9f396b028bc304fd1c27b2d52b22966cfc98a61 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:57:27 +0100 Subject: qapi: Eliminate check_docs() and drop QAPIDoc.expr Move what's left in check_docs() to check_expr(). Delegate the actual checking to new QAPIDoc.check_expr(). QAPIDoc.expr is now unused; drop it. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <1489582656-31133-39-git-send-email-armbru@redhat.com> --- scripts/qapi.py | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi.py b/scripts/qapi.py index ca9926b796..1aaae8e2f6 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -138,8 +138,6 @@ class QAPIDoc(object): self.sections = [] # the current section self.section = self.body - # associated expression (to be set by expression parser) - self.expr = None def has_section(self, name): """Return True if we have a section with this name.""" @@ -249,6 +247,11 @@ class QAPIDoc(object): self.args[member.name] = QAPIDoc.ArgSection(member.name) self.args[member.name].connect(member) + 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.iteritems() if not section.member] @@ -316,7 +319,6 @@ class QAPISchemaParser(object): raise QAPISemError( self.cur_doc.info, "Expression documentation required") - self.cur_doc.expr = expr expr_elem['doc'] = self.cur_doc self.exprs.append(expr_elem) self.cur_doc = None @@ -984,6 +986,7 @@ def check_exprs(exprs): for expr_elem in exprs: expr = expr_elem['expr'] info = expr_elem['info'] + doc = expr_elem.get('doc') if 'enum' in expr: check_enum(expr, info) @@ -1000,20 +1003,10 @@ def check_exprs(exprs): else: assert False, 'unexpected meta type' - return exprs - + if doc: + doc.check_expr(expr) -def check_definition_doc(doc, expr, info): - if doc.has_section('Returns') and 'command' not in expr: - raise QAPISemError(info, "'Returns:' is only valid for commands") - - -def check_docs(docs): - for doc in docs: - if doc.expr: - check_definition_doc(doc, doc.expr, doc.info) - - return docs + return exprs # @@ -1511,7 +1504,7 @@ class QAPISchema(object): try: parser = QAPISchemaParser(open(fname, 'r')) self.exprs = check_exprs(parser.exprs) - self.docs = check_docs(parser.docs) + self.docs = parser.docs self._entity_dict = {} self._predefining = True self._def_predefineds() -- cgit v1.2.3 From 062e856b155a8b7724b8eba449fc3aa8a181d46b Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:57:28 +0100 Subject: qapi: Drop unused variable events Missed in commit e98859a Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <1489582656-31133-40-git-send-email-armbru@redhat.com> --- scripts/qapi.py | 3 --- 1 file changed, 3 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi.py b/scripts/qapi.py index 1aaae8e2f6..d5a113c7d6 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -49,7 +49,6 @@ name_case_whitelist = [] enum_types = [] struct_types = [] union_types = [] -events = [] all_names = {} # @@ -756,14 +755,12 @@ def check_command(expr, info): def check_event(expr, info): - global events name = expr['event'] boxed = expr.get('boxed', False) meta = ['struct'] if boxed: meta += ['union', 'alternate'] - events.append(name) check_type(info, "'data' for event '%s'" % name, expr.get('data'), allow_dict=not boxed, allow_optional=True, allow_metas=meta) -- cgit v1.2.3 From eda43c68440e32062c32cc99a79edbb52cf87a74 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:57:29 +0100 Subject: qapi: Simplify what gets stored in enum_types Don't invent a new dictionary structure just for enum_types, simply store the defining expression, like we do for struct_types and union_types. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <1489582656-31133-41-git-send-email-armbru@redhat.com> --- scripts/qapi.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi.py b/scripts/qapi.py index d5a113c7d6..3c6e137847 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -668,16 +668,17 @@ def find_union(name): return None -def add_enum(name, info, enum_values=None, implicit=False): +def add_enum(definition, info): global enum_types - add_name(name, info, 'enum', implicit) - enum_types.append({'enum_name': name, 'enum_values': enum_values}) + name = definition['enum'] + add_name(name, info, 'enum', 'data' not in definition) + enum_types.append(definition) def find_enum(name): global enum_types for enum in enum_types: - if enum['enum_name'] == name: + if enum['enum'] == name: return enum return None @@ -825,15 +826,15 @@ def check_union(expr, info): # If the discriminator names an enum type, then all members # of 'data' must also be members of the enum type. if enum_define: - if key not in enum_define['enum_values']: + if key not in enum_define['data']: raise QAPISemError(info, "Discriminator value '%s' is not found in " "enum '%s'" - % (key, enum_define['enum_name'])) + % (key, enum_define['enum'])) # If discriminator is user-defined, ensure all values are covered if enum_define: - for value in enum_define['enum_values']: + for value in enum_define['data']: if value not in members.keys(): raise QAPISemError(info, "Union '%s' data missing '%s' branch" % (name, value)) @@ -938,7 +939,7 @@ def check_exprs(exprs): if 'enum' in expr: name = expr['enum'] check_keys(expr_elem, 'enum', ['data'], ['prefix']) - add_enum(name, info, expr['data']) + add_enum(expr, info) elif 'union' in expr: name = expr['union'] check_keys(expr_elem, 'union', ['data'], @@ -971,13 +972,13 @@ def check_exprs(exprs): # Try again for hidden UnionKind enum for expr_elem in exprs: expr = expr_elem['expr'] - if 'union' in expr: - if not discriminator_find_enum_define(expr): - add_enum('%sKind' % expr['union'], expr_elem['info'], - implicit=True) + if 'union' in expr and not discriminator_find_enum_define(expr): + name = '%sKind' % expr['union'] elif 'alternate' in expr: - add_enum('%sKind' % expr['alternate'], expr_elem['info'], - implicit=True) + name = '%sKind' % expr['alternate'] + else: + continue + add_enum({ 'enum': name }, expr_elem['info']) # Validate that exprs make sense for expr_elem in exprs: -- cgit v1.2.3 From 6f05345f8f8eac137e0ba22a7389cdeaff60c9b2 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:57:30 +0100 Subject: qapi: Factor add_name() calls out of the meta conditional Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <1489582656-31133-42-git-send-email-armbru@redhat.com> --- scripts/qapi.py | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi.py b/scripts/qapi.py index 3c6e137847..1f79eb4d17 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -640,8 +640,6 @@ def add_name(name, info, meta, implicit=False): def add_struct(definition, info): global struct_types - name = definition['struct'] - add_name(name, info, 'struct') struct_types.append(definition) @@ -655,8 +653,6 @@ def find_struct(name): def add_union(definition, info): global union_types - name = definition['union'] - add_name(name, info, 'union') union_types.append(definition) @@ -670,8 +666,6 @@ def find_union(name): def add_enum(definition, info): global enum_types - name = definition['enum'] - add_name(name, info, 'enum', 'data' not in definition) enum_types.append(definition) @@ -937,34 +931,33 @@ def check_exprs(exprs): "Expression missing documentation comment") if 'enum' in expr: - name = expr['enum'] + meta = 'enum' check_keys(expr_elem, 'enum', ['data'], ['prefix']) add_enum(expr, info) elif 'union' in expr: - name = expr['union'] + meta = 'union' check_keys(expr_elem, 'union', ['data'], ['base', 'discriminator']) add_union(expr, info) elif 'alternate' in expr: - name = expr['alternate'] + meta = 'alternate' check_keys(expr_elem, 'alternate', ['data']) - add_name(name, info, 'alternate') elif 'struct' in expr: - name = expr['struct'] + meta = 'struct' check_keys(expr_elem, 'struct', ['data'], ['base']) add_struct(expr, info) elif 'command' in expr: - name = expr['command'] + meta = 'command' check_keys(expr_elem, 'command', [], ['data', 'returns', 'gen', 'success-response', 'boxed']) - add_name(name, info, 'command') elif 'event' in expr: - name = expr['event'] + meta = 'event' check_keys(expr_elem, 'event', [], ['data', 'boxed']) - add_name(name, info, 'event') else: raise QAPISemError(expr_elem['info'], "Expression is missing metatype") + name = expr[meta] + add_name(name, info, meta) if doc and doc.symbol != name: raise QAPISemError(info, "Definition of '%s' follows documentation" " for '%s'" % (name, doc.symbol)) @@ -979,6 +972,7 @@ def check_exprs(exprs): else: continue add_enum({ 'enum': name }, expr_elem['info']) + add_name(name, info, 'enum', implicit=True) # Validate that exprs make sense for expr_elem in exprs: -- cgit v1.2.3 From 5f018446fe69ec85711db364e8d1f4c4de372bf5 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:57:31 +0100 Subject: qapi: enum_types is a list used like a dict, make it one Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <1489582656-31133-43-git-send-email-armbru@redhat.com> --- scripts/qapi.py | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi.py b/scripts/qapi.py index 1f79eb4d17..735ddaa605 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -46,7 +46,7 @@ returns_whitelist = [] # Whitelist of entities allowed to violate case conventions name_case_whitelist = [] -enum_types = [] +enum_types = {} struct_types = [] union_types = [] all_names = {} @@ -567,7 +567,7 @@ def find_alternate_member_qtype(qapi_type): return builtin_types[qapi_type] elif find_struct(qapi_type): return 'QTYPE_QDICT' - elif find_enum(qapi_type): + elif qapi_type in enum_types: return 'QTYPE_QSTRING' elif find_union(qapi_type): return 'QTYPE_QDICT' @@ -591,7 +591,7 @@ def discriminator_find_enum_define(expr): if not discriminator_type: return None - return find_enum(discriminator_type) + return enum_types.get(discriminator_type) # Names must be letters, numbers, -, and _. They must start with letter, @@ -664,23 +664,6 @@ def find_union(name): return None -def add_enum(definition, info): - global enum_types - enum_types.append(definition) - - -def find_enum(name): - global enum_types - for enum in enum_types: - if enum['enum'] == name: - return enum - return None - - -def is_enum(name): - return find_enum(name) is not None - - def check_type(info, source, value, allow_array=False, allow_dict=False, allow_optional=False, allow_metas=[]): @@ -799,7 +782,7 @@ def check_union(expr, info): "Discriminator '%s' is not a member of base " "struct '%s'" % (discriminator, base)) - enum_define = find_enum(discriminator_type) + enum_define = enum_types.get(discriminator_type) allow_metas = ['struct'] # Do not allow string discriminator if not enum_define: @@ -933,7 +916,7 @@ def check_exprs(exprs): if 'enum' in expr: meta = 'enum' check_keys(expr_elem, 'enum', ['data'], ['prefix']) - add_enum(expr, info) + enum_types[expr[meta]] = expr elif 'union' in expr: meta = 'union' check_keys(expr_elem, 'union', ['data'], @@ -971,7 +954,7 @@ def check_exprs(exprs): name = '%sKind' % expr['alternate'] else: continue - add_enum({ 'enum': name }, expr_elem['info']) + enum_types[name] = {'enum': name} add_name(name, info, 'enum', implicit=True) # Validate that exprs make sense -- cgit v1.2.3 From ed285bf821eeb5d82bb2777707cc02b11194b7b1 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:57:32 +0100 Subject: qapi: struct_types is a list used like a dict, make it one Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <1489582656-31133-44-git-send-email-armbru@redhat.com> --- scripts/qapi.py | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi.py b/scripts/qapi.py index 735ddaa605..f4d1a483e5 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -47,7 +47,7 @@ returns_whitelist = [] name_case_whitelist = [] enum_types = {} -struct_types = [] +struct_types = {} union_types = [] all_names = {} @@ -555,7 +555,7 @@ class QAPISchemaParser(object): def find_base_members(base): if isinstance(base, dict): return base - base_struct_define = find_struct(base) + base_struct_define = struct_types.get(base) if not base_struct_define: return None return base_struct_define['data'] @@ -565,7 +565,7 @@ def find_base_members(base): def find_alternate_member_qtype(qapi_type): if qapi_type in builtin_types: return builtin_types[qapi_type] - elif find_struct(qapi_type): + elif qapi_type in struct_types: return 'QTYPE_QDICT' elif qapi_type in enum_types: return 'QTYPE_QSTRING' @@ -638,19 +638,6 @@ def add_name(name, info, meta, implicit=False): all_names[name] = meta -def add_struct(definition, info): - global struct_types - struct_types.append(definition) - - -def find_struct(name): - global struct_types - for struct in struct_types: - if struct['struct'] == name: - return struct - return None - - def add_union(definition, info): global union_types union_types.append(definition) @@ -928,7 +915,7 @@ def check_exprs(exprs): elif 'struct' in expr: meta = 'struct' check_keys(expr_elem, 'struct', ['data'], ['base']) - add_struct(expr, info) + struct_types[expr[meta]] = expr elif 'command' in expr: meta = 'command' check_keys(expr_elem, 'command', [], -- cgit v1.2.3 From 768562ded0354f6834b48c99626b6fadf19b757b Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:57:33 +0100 Subject: qapi: union_types is a list used like a dict, make it one Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <1489582656-31133-45-git-send-email-armbru@redhat.com> --- scripts/qapi.py | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi.py b/scripts/qapi.py index f4d1a483e5..82c25a113e 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -48,7 +48,7 @@ name_case_whitelist = [] enum_types = {} struct_types = {} -union_types = [] +union_types = {} all_names = {} # @@ -569,7 +569,7 @@ def find_alternate_member_qtype(qapi_type): return 'QTYPE_QDICT' elif qapi_type in enum_types: return 'QTYPE_QSTRING' - elif find_union(qapi_type): + elif qapi_type in union_types: return 'QTYPE_QDICT' return None @@ -638,19 +638,6 @@ def add_name(name, info, meta, implicit=False): all_names[name] = meta -def add_union(definition, info): - global union_types - union_types.append(definition) - - -def find_union(name): - global union_types - for union in union_types: - if union['union'] == name: - return union - return None - - def check_type(info, source, value, allow_array=False, allow_dict=False, allow_optional=False, allow_metas=[]): @@ -908,7 +895,7 @@ def check_exprs(exprs): meta = 'union' check_keys(expr_elem, 'union', ['data'], ['base', 'discriminator']) - add_union(expr, info) + union_types[expr[meta]] = expr elif 'alternate' in expr: meta = 'alternate' check_keys(expr_elem, 'alternate', ['data']) -- cgit v1.2.3 From 6bbfb12de6541794c10baf007ad327262f94f10a Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:57:34 +0100 Subject: qapi: Drop unused .check_clash() parameter schema Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <1489582656-31133-46-git-send-email-armbru@redhat.com> --- scripts/qapi.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi.py b/scripts/qapi.py index 82c25a113e..b798ae47b2 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -1183,7 +1183,7 @@ class QAPISchemaObjectType(QAPISchemaType): self.base = schema.lookup_type(self._base_name) assert isinstance(self.base, QAPISchemaObjectType) self.base.check(schema) - self.base.check_clash(schema, self.info, seen) + self.base.check_clash(self.info, seen) for m in self.local_members: m.check(schema) m.check_clash(self.info, seen) @@ -1193,14 +1193,14 @@ class QAPISchemaObjectType(QAPISchemaType): if self.variants: self.variants.check(schema, seen) assert self.variants.tag_member in self.members - self.variants.check_clash(schema, self.info, seen) + self.variants.check_clash(self.info, seen) if self.doc: self.doc.check() # 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, schema, info, seen): + def check_clash(self, info, seen): assert not self.variants # not implemented for m in self.members: m.check_clash(info, seen) @@ -1329,12 +1329,12 @@ class QAPISchemaObjectTypeVariants(object): assert isinstance(v.type, QAPISchemaObjectType) v.type.check(schema) - def check_clash(self, schema, info, seen): + 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) - v.type.check_clash(schema, info, dict(seen)) + v.type.check_clash(info, dict(seen)) class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember): -- cgit v1.2.3 From c261394978d000000a095d7b4986226d0a4a551c Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:57:35 +0100 Subject: qapi: Make pylint a bit happier Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <1489582656-31133-47-git-send-email-armbru@redhat.com> --- scripts/qapi-commands.py | 6 +++--- scripts/qapi-visit.py | 1 - scripts/qapi.py | 8 ++++---- 3 files changed, 7 insertions(+), 8 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py index 0c05449cb6..1943de4852 100644 --- a/scripts/qapi-commands.py +++ b/scripts/qapi-commands.py @@ -13,7 +13,6 @@ # See the COPYING file in the top-level directory. from qapi import * -import re def gen_command_decl(name, arg_type, boxed, ret_type): @@ -84,7 +83,8 @@ static void qmp_marshal_output_%(c_name)s(%(c_type)s ret_in, QObject **ret_out, def gen_marshal_proto(name): - return 'void qmp_marshal_%s(QDict *args, QObject **ret, Error **errp)' % c_name(name) + return ('void qmp_marshal_%s(QDict *args, QObject **ret, Error **errp)' + % c_name(name)) def gen_marshal_decl(name): @@ -198,7 +198,7 @@ def gen_register_command(name, success_response): options = 'QCO_NO_SUCCESS_RESP' ret = mcgen(''' - qmp_register_command(cmds, "%(name)s", + qmp_register_command(cmds, "%(name)s", qmp_marshal_%(c_name)s, %(opts)s); ''', name=name, c_name=c_name(name), diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py index 3d3936e4d0..5737aefa05 100644 --- a/scripts/qapi-visit.py +++ b/scripts/qapi-visit.py @@ -13,7 +13,6 @@ # See the COPYING file in the top-level directory. from qapi import * -import re def gen_visit_decl(name, scalar=False): diff --git a/scripts/qapi.py b/scripts/qapi.py index b798ae47b2..d19300d657 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -11,13 +11,13 @@ # This work is licensed under the terms of the GNU GPL, version 2. # See the COPYING file in the top-level directory. -import re -from ordereddict import OrderedDict import errno import getopt import os -import sys +import re import string +import sys +from ordereddict import OrderedDict builtin_types = { 'str': 'QTYPE_QSTRING', @@ -1587,7 +1587,7 @@ class QAPISchema(object): tag_member = None if isinstance(base, dict): base = (self._make_implicit_object_type( - name, info, doc, 'base', self._make_members(base, info))) + name, info, doc, 'base', self._make_members(base, info))) if tag_name: variants = [self._make_variant(key, value) for (key, value) in data.iteritems()] -- cgit v1.2.3 From 012b126de2ded4e93b5ed069be5544ad8a2e6c15 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Mar 2017 13:57:36 +0100 Subject: qapi: Fix a misleading parser error message When choking on a token where an expression is expected, we report 'Expected "{", "[" or string'. Close, but no cigar. Fix it to Expected '"{", "[", string, boolean or "null"'. Missed in commit e53188a. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <1489582656-31133-48-git-send-email-armbru@redhat.com> --- scripts/qapi.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/qapi.py b/scripts/qapi.py index d19300d657..e88c047c2e 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -519,7 +519,8 @@ class QAPISchemaParser(object): expr = self.val self.accept() else: - raise QAPIParseError(self, 'Expected "{", "[" or string') + raise QAPIParseError(self, 'Expected "{", "[", string, ' + 'boolean or "null"') return expr def get_doc(self, info): -- cgit v1.2.3