Commit 4da5c51c authored by Peter Maydell's avatar Peter Maydell
Browse files

Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2017-12-20' into staging



QAPI patches for 2017-12-20

# gpg: Signature made Wed 20 Dec 2017 18:53:28 GMT
# gpg:                using RSA key 0x3870B400EB918653
# gpg: Good signature from "Markus Armbruster <armbru@redhat.com>"
# gpg:                 aka "Markus Armbruster <armbru@pond.sub.org>"
# Primary key fingerprint: 354B C8B3 D7EB 2A6B 6867  4E5F 3870 B400 EB91 8653

* remotes/armbru/tags/pull-qapi-2017-12-20:
  qmp: remove qmp_cpu
  qapi-docs: fix a comment typo
  qapi2texi: De-duplicate code to add blank line before symbol
  qapi: Rename QAPIDoc.parser, .section to ._parser, ._section
  qapi2texi: Simplify representation of section text
  qapi: Simplify representation of QAPIDoc section text
  qapi: Unify representation of doc section without name
  qapi2texi: Clean up texi_sections()
  tests/qapi-schema/doc-bad-section: New, factored out of doc-good
  qapi: Make cur_doc local to QAPISchemaParser.__init__()
  qapi: Eliminate QAPISchemaParser.__init__()'s local fname
  qapi: Stop rejecting #optional
  qapi-schema: Fix query-vm-generation-id's doc comment markup

Signed-off-by: default avatarPeter Maydell <peter.maydell@linaro.org>
parents 200780a3 49ccefde
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -63,7 +63,7 @@ Comment text starting with '=' is a section title:

Double the '=' for a subsection title:

    # == Subection title
    # == Subsection title

'|' denotes examples:

+1 −12
Original line number Diff line number Diff line
@@ -1045,17 +1045,6 @@
##
{ 'command': 'system_powerdown' }

##
# @cpu:
#
# This command is a nop that is only provided for the purposes of compatibility.
#
# Since: 0.14.0
#
# Notes: Do not use this command.
##
{ 'command': 'cpu', 'data': {'index': 'int'} }

##
# @cpu-add:
#
@@ -3188,7 +3177,7 @@
#
# Show Virtual Machine Generation ID
#
# Since 2.9
# Since: 2.9
##
{ 'command': 'query-vm-generation-id', 'returns': 'GuidInfo' }

+0 −5
Original line number Diff line number Diff line
@@ -113,11 +113,6 @@ void qmp_system_powerdown(Error **erp)
    qemu_system_powerdown_request();
}

void qmp_cpu(int64_t index, Error **errp)
{
    /* Just do nothing */
}

void qmp_cpu_add(int64_t id, Error **errp)
{
    MachineClass *mc;
+49 −58
Original line number Diff line number Diff line
@@ -106,13 +106,10 @@ class QAPIDoc(object):
            # optional section name (argument/member or section name)
            self.name = name
            # the list of lines for this section
            self.content = []
            self.text = ''

        def append(self, line):
            self.content.append(line)

        def __repr__(self):
            return '\n'.join(self.content).strip()
            self.text += line.rstrip() + '\n'

    class ArgSection(Section):
        def __init__(self, name):
@@ -123,11 +120,11 @@ class QAPIDoc(object):
            self.member = member

    def __init__(self, parser, info):
        # self.parser is used to report errors with QAPIParseError.  The
        # self._parser is used to report errors with QAPIParseError.  The
        # resulting error position depends on the state of the parser.
        # It happens to be the beginning of the comment.  More or less
        # servicable, but action at a distance.
        self.parser = parser
        self._parser = parser
        self.info = info
        self.symbol = None
        self.body = QAPIDoc.Section()
@@ -136,7 +133,7 @@ class QAPIDoc(object):
        # a list of Section
        self.sections = []
        # the current section
        self.section = self.body
        self._section = self.body

    def has_section(self, name):
        """Return True if we have a section with this name."""
@@ -153,20 +150,20 @@ class QAPIDoc(object):
            return

        if line[0] != ' ':
            raise QAPIParseError(self.parser, "Missing space after #")
            raise QAPIParseError(self._parser, "Missing space after #")
        line = line[1:]

        # FIXME not nice: things like '#  @foo:' and '# @foo: ' aren't
        # recognized, and get silently treated as ordinary text
        if self.symbol:
            self._append_symbol_line(line)
        elif not self.body.content and line.startswith('@'):
        elif not self.body.text and line.startswith('@'):
            if not line.endswith(':'):
                raise QAPIParseError(self.parser, "Line should end with :")
                raise QAPIParseError(self._parser, "Line should end with :")
            self.symbol = line[1:-1]
            # FIXME invalid names other than the empty string aren't flagged
            if not self.symbol:
                raise QAPIParseError(self.parser, "Invalid name")
                raise QAPIParseError(self._parser, "Invalid name")
        else:
            self._append_freeform(line)

@@ -192,53 +189,48 @@ class QAPIDoc(object):
    def _start_args_section(self, name):
        # FIXME invalid names other than the empty string aren't flagged
        if not name:
            raise QAPIParseError(self.parser, "Invalid parameter name")
            raise QAPIParseError(self._parser, "Invalid parameter name")
        if name in self.args:
            raise QAPIParseError(self.parser,
            raise QAPIParseError(self._parser,
                                 "'%s' parameter name duplicated" % name)
        if self.sections:
            raise QAPIParseError(self.parser,
            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
        self._section = QAPIDoc.ArgSection(name)
        self.args[name] = self._section

    def _start_section(self, name=''):
    def _start_section(self, name=None):
        if name in ('Returns', 'Since') and self.has_section(name):
            raise QAPIParseError(self.parser,
            raise QAPIParseError(self._parser,
                                 "Duplicated '%s' section" % name)
        self._end_section()
        self.section = QAPIDoc.Section(name)
        self.sections.append(self.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
        if self._section:
            text = self._section.text = self._section.text.strip()
            if self._section.name and (not text or text.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
                and not self.section.content[-1]
        in_arg = isinstance(self._section, QAPIDoc.ArgSection)
        if (in_arg and self._section.text.endswith('\n\n')
                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')):
        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,
            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):
            raise QAPISemError(self.info, "Please drop the #optional tag")
        self.section.append(line)
        self._section.append(line)

    def connect_member(self, member):
        if member.name not in self.args:
@@ -265,8 +257,7 @@ class QAPISchemaParser(object):

    def __init__(self, fp, previously_included=[], incl_info=None):
        abs_fname = os.path.abspath(fp.name)
        fname = fp.name
        self.fname = fname
        self.fname = fp.name
        previously_included.append(abs_fname)
        self.incl_info = incl_info
        self.src = fp.read()
@@ -277,21 +268,21 @@ class QAPISchemaParser(object):
        self.line_pos = 0
        self.exprs = []
        self.docs = []
        self.cur_doc = None
        self.accept()
        cur_doc = None

        while self.tok is not None:
            info = {'file': fname, 'line': self.line,
            info = {'file': self.fname, 'line': self.line,
                    'parent': self.incl_info}
            if self.tok == '#':
                self.reject_expr_doc()
                self.cur_doc = self.get_doc(info)
                self.docs.append(self.cur_doc)
                self.reject_expr_doc(cur_doc)
                cur_doc = self.get_doc(info)
                self.docs.append(cur_doc)
                continue

            expr = self.get_expr(False)
            if 'include' in expr:
                self.reject_expr_doc()
                self.reject_expr_doc(cur_doc)
                if len(expr) != 1:
                    raise QAPISemError(info, "Invalid 'include' directive")
                include = expr['include']
@@ -301,7 +292,7 @@ class QAPISchemaParser(object):
                self._include(include, info, os.path.dirname(abs_fname),
                              previously_included)
            elif "pragma" in expr:
                self.reject_expr_doc()
                self.reject_expr_doc(cur_doc)
                if len(expr) != 1:
                    raise QAPISemError(info, "Invalid 'pragma' directive")
                pragma = expr['pragma']
@@ -313,22 +304,22 @@ class QAPISchemaParser(object):
            else:
                expr_elem = {'expr': expr,
                             'info': info}
                if self.cur_doc:
                    if not self.cur_doc.symbol:
                if cur_doc:
                    if not cur_doc.symbol:
                        raise QAPISemError(
                            self.cur_doc.info,
                            "Expression documentation required")
                    expr_elem['doc'] = self.cur_doc
                            cur_doc.info, "Expression documentation required")
                    expr_elem['doc'] = cur_doc
                self.exprs.append(expr_elem)
            self.cur_doc = None
        self.reject_expr_doc()
            cur_doc = None
        self.reject_expr_doc(cur_doc)

    def reject_expr_doc(self):
        if self.cur_doc and self.cur_doc.symbol:
    @staticmethod
    def reject_expr_doc(doc):
        if doc and doc.symbol:
            raise QAPISemError(
                self.cur_doc.info,
                doc.info,
                "Documentation for '%s' is not followed by the definition"
                % self.cur_doc.symbol)
                % doc.symbol)

    def _include(self, include, info, base_dir, previously_included):
        incl_abs_fname = os.path.join(base_dir, include)
+27 −38
Original line number Diff line number Diff line
@@ -13,7 +13,6 @@ MSG_FMT = """
@deftypefn {type} {{}} {name}

{body}

@end deftypefn

""".format
@@ -22,7 +21,6 @@ TYPE_FMT = """
@deftp {{{type}}} {name}

{body}

@end deftp

""".format
@@ -74,7 +72,7 @@ def texi_format(doc):
    - 1. or 1): generates an @enumerate @item
    - */-: generates an @itemize list
    """
    lines = []
    ret = ''
    doc = subst_braces(doc)
    doc = subst_vars(doc)
    doc = subst_emph(doc)
@@ -100,32 +98,32 @@ def texi_format(doc):
            line = '@subsection ' + line[3:]
        elif re.match(r'^([0-9]*\.) ', line):
            if not inlist:
                lines.append('@enumerate')
                ret += '@enumerate\n'
                inlist = 'enumerate'
            ret += '@item\n'
            line = line[line.find(' ')+1:]
            lines.append('@item')
        elif re.match(r'^[*-] ', line):
            if not inlist:
                lines.append('@itemize %s' % {'*': '@bullet',
                                              '-': '@minus'}[line[0]])
                ret += '@itemize %s\n' % {'*': '@bullet',
                                          '-': '@minus'}[line[0]]
                inlist = 'itemize'
            lines.append('@item')
            ret += '@item\n'
            line = line[2:]
        elif lastempty and inlist:
            lines.append('@end %s\n' % inlist)
            ret += '@end %s\n\n' % inlist
            inlist = ''

        lastempty = empty
        lines.append(line)
        ret += line + '\n'

    if inlist:
        lines.append('@end %s\n' % inlist)
    return '\n'.join(lines)
        ret += '@end %s\n\n' % inlist
    return ret


def texi_body(doc):
    """Format the main documentation body"""
    return texi_format(str(doc.body)) + '\n'
    return texi_format(doc.body.text)


def texi_enum_value(value):
@@ -149,15 +147,16 @@ def texi_members(doc, what, base, variants, member_func):
    items = ''
    for section in doc.args.itervalues():
        # TODO Drop fallbacks when undocumented members are outlawed
        if section.content:
            desc = texi_format(str(section))
        if section.text:
            desc = texi_format(section.text)
        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])
            members_text = ', '.join(['@t{"%s"}' % v for v in values])
            desc = 'One of ' + members_text + '\n'
        else:
            desc = 'Not documented'
        items += member_func(section.member) + desc + '\n'
            desc = 'Not documented\n'
        items += member_func(section.member) + desc
    if base:
        items += '@item The members of @code{%s}\n' % base.doc_type()
    if variants:
@@ -180,16 +179,13 @@ def texi_sections(doc):
    """Format additional sections following arguments"""
    body = ''
    for section in doc.sections:
        name, doc = (section.name, str(section))
        func = texi_format
        if name.startswith('Example'):
            func = texi_example

        if name:
        if section.name:
            # prefer @b over @strong, so txt doesn't translate it to *Foo:*
            body += '\n\n@b{%s:}\n' % name

        body += func(doc)
            body += '\n@b{%s:}\n' % section.name
        if section.name and section.name.startswith('Example'):
            body += texi_example(section.text)
        else:
            body += texi_format(section.text)
    return body


@@ -210,8 +206,6 @@ class QAPISchemaGenDocVisitor(qapi.QAPISchemaVisitor):

    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, 'Values',
@@ -221,16 +215,12 @@ class QAPISchemaGenDocVisitor(qapi.QAPISchemaVisitor):
        doc = self.cur_doc
        if base and base.is_implicit():
            base = None
        if self.out:
            self.out += '\n'
        self.out += TYPE_FMT(type='Object',
                             name=doc.symbol,
                             body=texi_entity(doc, 'Members', base, variants))

    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, 'Members'))
@@ -238,11 +228,10 @@ class QAPISchemaGenDocVisitor(qapi.QAPISchemaVisitor):
    def visit_command(self, name, info, arg_type, ret_type,
                      gen, success_response, boxed):
        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 += ('\n@b{Arguments:} the members of @code{%s}\n'
                     % arg_type.name)
            body += texi_sections(doc)
        else:
            body = texi_entity(doc, 'Arguments')
@@ -252,13 +241,13 @@ class QAPISchemaGenDocVisitor(qapi.QAPISchemaVisitor):

    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, 'Arguments'))

    def symbol(self, doc, entity):
        if self.out:
            self.out += '\n'
        self.cur_doc = doc
        entity.visit(self)
        self.cur_doc = None
Loading