mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-02 07:13:54 -06:00
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:
parent
faeacf858b
commit
323c668934
4 changed files with 80 additions and 36 deletions
|
@ -35,6 +35,7 @@ from docutils.parsers.rst import Directive, directives
|
|||
from docutils.statemachine import ViewList
|
||||
from qapi.error import QAPIError, QAPISemError
|
||||
from qapi.gen import QAPISchemaVisitor
|
||||
from qapi.parser import QAPIDoc
|
||||
from qapi.schema import QAPISchema
|
||||
|
||||
from sphinx import addnodes
|
||||
|
@ -258,11 +259,11 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor):
|
|||
"""Return list of doctree nodes for additional sections"""
|
||||
nodelist = []
|
||||
for section in doc.sections:
|
||||
if section.tag and section.tag == 'TODO':
|
||||
if section.kind == QAPIDoc.Kind.TODO:
|
||||
# Hide TODO: sections
|
||||
continue
|
||||
|
||||
if not section.tag:
|
||||
if section.kind == QAPIDoc.Kind.PLAIN:
|
||||
# Sphinx cannot handle sectionless titles;
|
||||
# Instead, just append the results to the prior section.
|
||||
container = nodes.container()
|
||||
|
@ -270,7 +271,7 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor):
|
|||
nodelist += container.children
|
||||
continue
|
||||
|
||||
snode = self._make_section(section.tag)
|
||||
snode = self._make_section(section.kind.name.title())
|
||||
self._parse_text_into_node(dedent(section.text), snode)
|
||||
nodelist.append(snode)
|
||||
return nodelist
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -113,7 +113,7 @@ The _one_ {and only}, description on the same line
|
|||
Also _one_ {and only}
|
||||
feature=enum-member-feat
|
||||
a member feature
|
||||
section=None
|
||||
section=Plain
|
||||
@two is undocumented
|
||||
doc symbol=Base
|
||||
body=
|
||||
|
@ -171,15 +171,15 @@ description starts on the same line
|
|||
a feature
|
||||
feature=cmd-feat2
|
||||
another feature
|
||||
section=None
|
||||
section=Plain
|
||||
.. note:: @arg3 is undocumented
|
||||
section=Returns
|
||||
@Object
|
||||
section=Errors
|
||||
some
|
||||
section=TODO
|
||||
section=Todo
|
||||
frobnicate
|
||||
section=None
|
||||
section=Plain
|
||||
.. admonition:: Notes
|
||||
|
||||
- Lorem ipsum dolor sit amet
|
||||
|
@ -212,7 +212,7 @@ If you're bored enough to read this, go see a video of boxed cats
|
|||
a feature
|
||||
feature=cmd-feat2
|
||||
another feature
|
||||
section=None
|
||||
section=Plain
|
||||
.. qmp-example::
|
||||
|
||||
-> "this example"
|
||||
|
|
|
@ -122,7 +122,7 @@ def test_frontend(fname):
|
|||
for feat, section in doc.features.items():
|
||||
print(' feature=%s\n%s' % (feat, section.text))
|
||||
for section in doc.sections:
|
||||
print(' section=%s\n%s' % (section.tag, section.text))
|
||||
print(' section=%s\n%s' % (section.kind, section.text))
|
||||
|
||||
|
||||
def open_test_result(dir_name, file_name, update):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue