qapi: clean up encoding of section kinds

We have several kinds of sections, and to tell them apart, we use
Section attribute @tag and also the section object's Python type:

              type        @tag
    untagged  Section     None
    @foo:     ArgSection  'foo'
    Returns:  Section     'Returns'
    Errors:   Section     'Errors'
    Since:    Section     'Since'
    TODO:     Section     'TODO'

Note:

* @foo can be a member or a feature description, depending on context.

* tag == 'Since' can be a Since: section or a member or feature
  description.  If it's a Section, it's the former, and if it's an
  ArgSection, it's the latter.

Clean this up as follows.  Move the member or feature name to new
ArgSection attribute @name, and replace @tag by enum @kind like this:

              type         kind     name
    untagged  Section      PLAIN
    @foo:     ArgSection   MEMBER   'foo'   if member or argument
              ArgSection   FEATURE  'foo'   if feature
    Returns:  Section      RETURNS
    Errors:   Section      ERRORS
    Since:    Section      SINCE
    TODO:     Section      TODO

The qapi-schema tests are updated to account for the new section names;
"TODO" becomes "Todo" and `None` becomes "Plain" there.

Signed-off-by: John Snow <jsnow@redhat.com>
Message-ID: <20250311034303.75779-34-jsnow@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
This commit is contained in:
John Snow 2025-03-10 23:42:31 -04:00 committed by Markus Armbruster
parent faeacf858b
commit 323c668934
4 changed files with 80 additions and 36 deletions

View file

@ -14,6 +14,7 @@
# This work is licensed under the terms of the GNU GPL, version 2.
# See the COPYING file in the top-level directory.
import enum
import os
import re
from typing import (
@ -574,7 +575,10 @@ class QAPISchemaParser:
)
raise QAPIParseError(self, emsg)
doc.new_tagged_section(self.info, match.group(1))
doc.new_tagged_section(
self.info,
QAPIDoc.Kind.from_string(match.group(1))
)
text = line[match.end():]
if text:
doc.append_line(text)
@ -585,7 +589,7 @@ class QAPISchemaParser:
self,
"unexpected '=' markup in definition documentation")
else:
# tag-less paragraph
# plain paragraph
doc.ensure_untagged_section(self.info)
doc.append_line(line)
line = self.get_doc_paragraph(doc)
@ -634,14 +638,33 @@ class QAPIDoc:
Free-form documentation blocks consist only of a body section.
"""
class Kind(enum.Enum):
PLAIN = 0
MEMBER = 1
FEATURE = 2
RETURNS = 3
ERRORS = 4
SINCE = 5
TODO = 6
@staticmethod
def from_string(kind: str) -> 'QAPIDoc.Kind':
return QAPIDoc.Kind[kind.upper()]
def __str__(self) -> str:
return self.name.title()
class Section:
# pylint: disable=too-few-public-methods
def __init__(self, info: QAPISourceInfo,
tag: Optional[str] = None):
def __init__(
self,
info: QAPISourceInfo,
kind: 'QAPIDoc.Kind',
):
# section source info, i.e. where it begins
self.info = info
# section tag, if any ('Returns', '@name', ...)
self.tag = tag
# section kind
self.kind = kind
# section text without tag
self.text = ''
@ -649,8 +672,14 @@ class QAPIDoc:
self.text += line + '\n'
class ArgSection(Section):
def __init__(self, info: QAPISourceInfo, tag: str):
super().__init__(info, tag)
def __init__(
self,
info: QAPISourceInfo,
kind: 'QAPIDoc.Kind',
name: str
):
super().__init__(info, kind)
self.name = name
self.member: Optional['QAPISchemaMember'] = None
def connect(self, member: 'QAPISchemaMember') -> None:
@ -662,7 +691,9 @@ class QAPIDoc:
# definition doc's symbol, None for free-form doc
self.symbol: Optional[str] = symbol
# the sections in textual order
self.all_sections: List[QAPIDoc.Section] = [QAPIDoc.Section(info)]
self.all_sections: List[QAPIDoc.Section] = [
QAPIDoc.Section(info, QAPIDoc.Kind.PLAIN)
]
# the body section
self.body: Optional[QAPIDoc.Section] = self.all_sections[0]
# dicts mapping parameter/feature names to their description
@ -679,12 +710,14 @@ class QAPIDoc:
def end(self) -> None:
for section in self.all_sections:
section.text = section.text.strip('\n')
if section.tag is not None and section.text == '':
if section.kind != QAPIDoc.Kind.PLAIN and section.text == '':
raise QAPISemError(
section.info, "text required after '%s:'" % section.tag)
section.info, "text required after '%s:'" % section.kind)
def ensure_untagged_section(self, info: QAPISourceInfo) -> None:
if self.all_sections and not self.all_sections[-1].tag:
kind = QAPIDoc.Kind.PLAIN
if self.all_sections and self.all_sections[-1].kind == kind:
# extend current section
section = self.all_sections[-1]
if not section.text:
@ -692,46 +725,56 @@ class QAPIDoc:
section.info = info
section.text += '\n'
return
# start new section
section = self.Section(info)
section = self.Section(info, kind)
self.sections.append(section)
self.all_sections.append(section)
def new_tagged_section(self, info: QAPISourceInfo, tag: str) -> None:
section = self.Section(info, tag)
if tag == 'Returns':
def new_tagged_section(
self,
info: QAPISourceInfo,
kind: 'QAPIDoc.Kind',
) -> None:
section = self.Section(info, kind)
if kind == QAPIDoc.Kind.RETURNS:
if self.returns:
raise QAPISemError(
info, "duplicated '%s' section" % tag)
info, "duplicated '%s' section" % kind)
self.returns = section
elif tag == 'Errors':
elif kind == QAPIDoc.Kind.ERRORS:
if self.errors:
raise QAPISemError(
info, "duplicated '%s' section" % tag)
info, "duplicated '%s' section" % kind)
self.errors = section
elif tag == 'Since':
elif kind == QAPIDoc.Kind.SINCE:
if self.since:
raise QAPISemError(
info, "duplicated '%s' section" % tag)
info, "duplicated '%s' section" % kind)
self.since = section
self.sections.append(section)
self.all_sections.append(section)
def _new_description(self, info: QAPISourceInfo, name: str,
desc: Dict[str, ArgSection]) -> None:
def _new_description(
self,
info: QAPISourceInfo,
name: str,
kind: 'QAPIDoc.Kind',
desc: Dict[str, ArgSection]
) -> None:
if not name:
raise QAPISemError(info, "invalid parameter name")
if name in desc:
raise QAPISemError(info, "'%s' parameter name duplicated" % name)
section = self.ArgSection(info, '@' + name)
section = self.ArgSection(info, kind, name)
self.all_sections.append(section)
desc[name] = section
def new_argument(self, info: QAPISourceInfo, name: str) -> None:
self._new_description(info, name, self.args)
self._new_description(info, name, QAPIDoc.Kind.MEMBER, self.args)
def new_feature(self, info: QAPISourceInfo, name: str) -> None:
self._new_description(info, name, self.features)
self._new_description(info, name, QAPIDoc.Kind.FEATURE, self.features)
def append_line(self, line: str) -> None:
self.all_sections[-1].append_line(line)
@ -744,7 +787,7 @@ class QAPIDoc:
"%s '%s' lacks documentation"
% (member.role, member.name))
self.args[member.name] = QAPIDoc.ArgSection(
self.info, '@' + member.name)
self.info, QAPIDoc.Kind.MEMBER, member.name)
self.args[member.name].connect(member)
def connect_feature(self, feature: 'QAPISchemaFeature') -> None: