mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-03-26 18:22:55 -07:00
Merge tag 'pull-qapi-2025-07-14-v2' of https://repo.or.cz/qemu/armbru into staging
QAPI patches patches for 2025-07-14 # -----BEGIN PGP SIGNATURE----- # # iQJGBAABCAAwFiEENUvIs9frKmtoZ05fOHC0AOuRhlMFAmh19QoSHGFybWJydUBy # ZWRoYXQuY29tAAoJEDhwtADrkYZThVIP/2v9M9C9k7q3bAku38gO6LqU/TJINKEt # ZlJt7L3LWRC51rpTmhTs6ZchdErs0wnNs2gCH/HjANDofa8lz9u7mpqUo+hVIW2r # eUi7uEURIBqsfMgantrQu1uhJg3rbtd9gWtxwTJm3qoFKjwxrWlxhEqOFlNSoqkv # DpoBD6X7zhUtEq6Z5IIsOBI2zqaXNleSsYWdgxJou8cvHMZkgdcvEyE36nRFsl2k # JSbnRLzFxsxuxd0bIBSrA3+I1pC9pWYS9ZY1qTnyPIX7MxeVvYupaWdOYlpELxi5 # 4zz7stS5wcvUJ+X7+HuobOofBjHljcHVxE06ACr38sQ2Ajqn3jhijQrDrtYdxG5P # 3kCnP+AY+qFa2CN/Ik7q2be5jz6Ws/3kxZrv/4/LrHa5ez1xB/orooQVZId0eKke # 5iQOyA9gNXAdJ2JJYdIAZduBg2b8tY892qtMufBA+XDNA3vGek2OixWC0QY5hZKW # y3AKjVHP/sProyCvbyT7Fta8sIRLcIVD0IHjzr7pLVd/lp/WzKAJpg6y8J2wvn6z # cnEUC5CceiYa8SbGOVfswR1yy06IIpJkw6cxoCzB3a2blfkvxGv4mCY6QiZh8K3W # 6xj6u1J539NWRGVkvnQWJ4/7MdFAOEqih4AqRpRLnRXJ+hzDVUkgCeZwtr+v6AVi # mNVLttTKPpvy # =2BHn # -----END PGP SIGNATURE----- # gpg: Signature made Tue 15 Jul 2025 02:28:26 EDT # gpg: using RSA key 354BC8B3D7EB2A6B68674E5F3870B400EB918653 # gpg: issuer "armbru@redhat.com" # gpg: Good signature from "Markus Armbruster <armbru@redhat.com>" [full] # gpg: aka "Markus Armbruster <armbru@pond.sub.org>" [full] # Primary key fingerprint: 354B C8B3 D7EB 2A6B 6867 4E5F 3870 B400 EB91 8653 * tag 'pull-qapi-2025-07-14-v2' of https://repo.or.cz/qemu/armbru: (29 commits) qapi: add cross-references to misc modules qapi: add cross-references to yank.json qapi: add cross-references to virtio.json qapi: add cross-references to ui.json qapi: add cross-references to sockets.json qapi: add cross-references to run-state.json qapi: add cross-references to replay.json qapi: add cross-references to QOM qapi: add cross-references to pci.json qapi: add cross-references to net.json qapi: add cross-references to migration.json qapi: add cross-references to Machine core qapi: add cross-references to job.json qapi: add cross-references to dump.json qapi: add cross-references to crypto.json qapi: add cross-references to block layer qapi: add cross-references to authz.json qapi: add cross-references to acpi.json qapi: rephrase return docs to avoid type name qapi: remove trivial "Returns:" sections ... Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
@@ -876,25 +876,35 @@ structuring content.
|
||||
Headings and subheadings
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
A free-form documentation comment containing a line which starts with
|
||||
some ``=`` symbols and then a space defines a section heading::
|
||||
Free-form documentation does not start with ``@SYMBOL`` and can contain
|
||||
arbitrary rST markup. Headings can be marked up using the standard rST
|
||||
syntax::
|
||||
|
||||
##
|
||||
# = This is a top level heading
|
||||
# *************************
|
||||
# This is a level 2 heading
|
||||
# *************************
|
||||
#
|
||||
# This is a free-form comment which will go under the
|
||||
# top level heading.
|
||||
##
|
||||
|
||||
##
|
||||
# == This is a second level heading
|
||||
# This is a third level heading
|
||||
# ==============================
|
||||
#
|
||||
# Level 4
|
||||
# _______
|
||||
#
|
||||
# Level 5
|
||||
# ^^^^^^^
|
||||
#
|
||||
# Level 6
|
||||
# """""""
|
||||
##
|
||||
|
||||
A heading line must be the first line of the documentation
|
||||
comment block.
|
||||
|
||||
Section headings must always be correctly nested, so you can only
|
||||
define a third-level heading inside a second-level heading, and so on.
|
||||
Level 1 headings are reserved for use by the generated documentation
|
||||
page itself, leaving level 2 as the highest level that should be used.
|
||||
|
||||
|
||||
Documentation markup
|
||||
|
||||
@@ -242,6 +242,37 @@ Example::
|
||||
}
|
||||
|
||||
|
||||
``:return-nodesc:``
|
||||
-------------------
|
||||
|
||||
Document the return type of a QAPI command, without an accompanying
|
||||
description.
|
||||
|
||||
:availability: This field list is only available in the body of the
|
||||
Command directive.
|
||||
:syntax: ``:return-nodesc: type``
|
||||
:type: `sphinx.util.docfields.Field
|
||||
<https://pydoc.dev/sphinx/latest/sphinx.util.docfields.Field.html?private=1>`_
|
||||
|
||||
|
||||
Example::
|
||||
|
||||
.. qapi:command:: query-replay
|
||||
:since: 5.2
|
||||
|
||||
Retrieve the record/replay information. It includes current
|
||||
instruction count which may be used for ``replay-break`` and
|
||||
``replay-seek`` commands.
|
||||
|
||||
:return-nodesc: ReplayInfo
|
||||
|
||||
.. qmp-example::
|
||||
|
||||
-> { "execute": "query-replay" }
|
||||
<- { "return": {
|
||||
"mode": "play", "filename": "log.rr", "icount": 220414 }
|
||||
}
|
||||
|
||||
``:value:``
|
||||
-----------
|
||||
|
||||
|
||||
@@ -11,7 +11,9 @@
|
||||
# later. See the COPYING file in the top-level directory.
|
||||
|
||||
##
|
||||
# = Firmware
|
||||
# ********
|
||||
# Firmware
|
||||
# ********
|
||||
##
|
||||
|
||||
{ 'pragma': {
|
||||
|
||||
@@ -2,5 +2,4 @@ QEMU Guest Agent Protocol Reference
|
||||
===================================
|
||||
|
||||
.. qapi-doc:: qga/qapi-schema.json
|
||||
:transmogrify:
|
||||
:namespace: QGA
|
||||
|
||||
@@ -7,5 +7,4 @@ QEMU QMP Reference Manual
|
||||
:local:
|
||||
|
||||
.. qapi-doc:: qapi/qapi-schema.json
|
||||
:transmogrify:
|
||||
:namespace: QMP
|
||||
|
||||
@@ -5,5 +5,4 @@ QEMU Storage Daemon QMP Reference Manual
|
||||
:local:
|
||||
|
||||
.. qapi-doc:: storage-daemon/qapi/qapi-schema.json
|
||||
:transmogrify:
|
||||
:namespace: QSD
|
||||
|
||||
@@ -10,7 +10,9 @@
|
||||
# later. See the COPYING file in the top-level directory.
|
||||
|
||||
##
|
||||
# = vhost user backend discovery & capabilities
|
||||
# *******************************************
|
||||
# vhost user backend discovery & capabilities
|
||||
# *******************************************
|
||||
##
|
||||
|
||||
##
|
||||
|
||||
@@ -532,6 +532,14 @@ class QAPICommand(QAPIObject):
|
||||
names=("return",),
|
||||
can_collapse=True,
|
||||
),
|
||||
# :return-nodesc: TypeName
|
||||
CompatField(
|
||||
"returnvalue",
|
||||
label=_("Return"),
|
||||
names=("return-nodesc",),
|
||||
bodyrolename="type",
|
||||
has_arg=False,
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
@@ -64,8 +64,6 @@ from sphinx.util import logging
|
||||
from sphinx.util.docutils import SphinxDirective, switch_source_input
|
||||
from sphinx.util.nodes import nested_parse_with_titles
|
||||
|
||||
from qapidoc_legacy import QAPISchemaGenRSTVisitor # type: ignore
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import (
|
||||
@@ -218,6 +216,11 @@ class Transmogrifier:
|
||||
typ = self.format_type(member)
|
||||
self.add_field(kind, member.name, body, info, typ)
|
||||
|
||||
@staticmethod
|
||||
def reformat_arobase(text: str) -> str:
|
||||
""" reformats @var to ``var`` """
|
||||
return re.sub(r"@([\w-]+)", r"``\1``", text)
|
||||
|
||||
# Transmogrification helpers
|
||||
|
||||
def visit_paragraph(self, section: QAPIDoc.Section) -> None:
|
||||
@@ -255,22 +258,28 @@ class Transmogrifier:
|
||||
def visit_returns(self, section: QAPIDoc.Section) -> None:
|
||||
assert isinstance(self.entity, QAPISchemaCommand)
|
||||
rtype = self.entity.ret_type
|
||||
# q_empty can produce None, but we won't be documenting anything
|
||||
# without an explicit return statement in the doc block, and we
|
||||
# should not have any such explicit statements when there is no
|
||||
# return value.
|
||||
# return statements will not be present (and won't be
|
||||
# autogenerated) for any command that doesn't return
|
||||
# *something*, so rtype will always be defined here.
|
||||
assert rtype
|
||||
|
||||
typ = self.format_type(rtype)
|
||||
assert typ
|
||||
assert section.text
|
||||
self.add_field("return", typ, section.text, section.info)
|
||||
|
||||
if section.text:
|
||||
self.add_field("return", typ, section.text, section.info)
|
||||
else:
|
||||
self.add_lines(f":return-nodesc: {typ}", section.info)
|
||||
|
||||
def visit_errors(self, section: QAPIDoc.Section) -> None:
|
||||
# FIXME: the formatting for errors may be inconsistent and may
|
||||
# or may not require different newline placement to ensure
|
||||
# proper rendering as a nested list.
|
||||
self.add_lines(f":error:\n{section.text}", section.info)
|
||||
# If the section text does not start with a space, it means text
|
||||
# began on the same line as the "Error:" string and we should
|
||||
# not insert a newline in this case.
|
||||
if section.text[0].isspace():
|
||||
text = f":error:\n{section.text}"
|
||||
else:
|
||||
text = f":error: {section.text}"
|
||||
self.add_lines(text, section.info)
|
||||
|
||||
def preamble(self, ent: QAPISchemaDefinition) -> None:
|
||||
"""
|
||||
@@ -357,8 +366,7 @@ class Transmogrifier:
|
||||
|
||||
# Add sections in source order:
|
||||
for i, section in enumerate(sections):
|
||||
# @var is translated to ``var``:
|
||||
section.text = re.sub(r"@([\w-]+)", r"``\1``", section.text)
|
||||
section.text = self.reformat_arobase(section.text)
|
||||
|
||||
if section.kind == QAPIDoc.Kind.PLAIN:
|
||||
self.visit_paragraph(section)
|
||||
@@ -393,44 +401,9 @@ class Transmogrifier:
|
||||
self.ensure_blank_line()
|
||||
|
||||
def visit_freeform(self, doc: QAPIDoc) -> None:
|
||||
# TODO: Once the old qapidoc transformer is deprecated, freeform
|
||||
# sections can be updated to pure rST, and this transformed removed.
|
||||
#
|
||||
# For now, translate our micro-format into rST. Code adapted
|
||||
# from Peter Maydell's freeform().
|
||||
|
||||
assert len(doc.all_sections) == 1, doc.all_sections
|
||||
body = doc.all_sections[0]
|
||||
text = body.text
|
||||
info = doc.info
|
||||
|
||||
if re.match(r"=+ ", text):
|
||||
# Section/subsection heading (if present, will always be the
|
||||
# first line of the block)
|
||||
(heading, _, text) = text.partition("\n")
|
||||
(leader, _, heading) = heading.partition(" ")
|
||||
# Implicit +1 for heading in the containing .rst doc
|
||||
level = len(leader) + 1
|
||||
|
||||
# https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html#sections
|
||||
markers = ' #*=_^"'
|
||||
overline = level <= 2
|
||||
marker = markers[level]
|
||||
|
||||
self.ensure_blank_line()
|
||||
# This credits all 2 or 3 lines to the single source line.
|
||||
if overline:
|
||||
self.add_line(marker * len(heading), info)
|
||||
self.add_line(heading, info)
|
||||
self.add_line(marker * len(heading), info)
|
||||
self.ensure_blank_line()
|
||||
|
||||
# Eat blank line(s) and advance info
|
||||
trimmed = text.lstrip("\n")
|
||||
text = trimmed
|
||||
info = info.next_line(len(text) - len(trimmed) + 1)
|
||||
|
||||
self.add_lines(text, info)
|
||||
self.add_lines(self.reformat_arobase(body.text), doc.info)
|
||||
self.ensure_blank_line()
|
||||
|
||||
def visit_entity(self, ent: QAPISchemaDefinition) -> None:
|
||||
@@ -504,15 +477,9 @@ class QAPIDocDirective(NestedDirective):
|
||||
option_spec = {
|
||||
"qapifile": directives.unchanged_required,
|
||||
"namespace": directives.unchanged,
|
||||
"transmogrify": directives.flag,
|
||||
}
|
||||
has_content = False
|
||||
|
||||
def new_serialno(self) -> str:
|
||||
"""Return a unique new ID string suitable for use as a node's ID"""
|
||||
env = self.state.document.settings.env
|
||||
return "qapidoc-%d" % env.new_serialno("qapidoc")
|
||||
|
||||
def transmogrify(self, schema: QAPISchema) -> nodes.Element:
|
||||
logger.info("Transmogrifying QAPI to rST ...")
|
||||
vis = Transmogrifier()
|
||||
@@ -590,21 +557,10 @@ class QAPIDocDirective(NestedDirective):
|
||||
outfile.write(f" {rcol}")
|
||||
outfile.write("\n")
|
||||
|
||||
def legacy(self, schema: QAPISchema) -> nodes.Element:
|
||||
vis = QAPISchemaGenRSTVisitor(self)
|
||||
vis.visit_begin(schema)
|
||||
for doc in schema.docs:
|
||||
if doc.symbol:
|
||||
vis.symbol(doc, schema.lookup_entity(doc.symbol))
|
||||
else:
|
||||
vis.freeform(doc)
|
||||
return vis.get_document_node() # type: ignore
|
||||
|
||||
def run(self) -> Sequence[nodes.Node]:
|
||||
env = self.state.document.settings.env
|
||||
qapifile = env.config.qapidoc_srctree + "/" + self.arguments[0]
|
||||
qapidir = os.path.dirname(qapifile)
|
||||
transmogrify = "transmogrify" in self.options
|
||||
|
||||
try:
|
||||
schema = QAPISchema(qapifile)
|
||||
@@ -617,11 +573,7 @@ class QAPIDocDirective(NestedDirective):
|
||||
# so they are displayed nicely to the user
|
||||
raise ExtensionError(str(err)) from err
|
||||
|
||||
if transmogrify:
|
||||
contentnode = self.transmogrify(schema)
|
||||
else:
|
||||
contentnode = self.legacy(schema)
|
||||
|
||||
contentnode = self.transmogrify(schema)
|
||||
return contentnode.children
|
||||
|
||||
|
||||
|
||||
@@ -1,440 +0,0 @@
|
||||
# coding=utf-8
|
||||
# type: ignore
|
||||
#
|
||||
# QEMU qapidoc QAPI file parsing extension
|
||||
#
|
||||
# Copyright (c) 2020 Linaro
|
||||
#
|
||||
# This work is licensed under the terms of the GNU GPLv2 or later.
|
||||
# See the COPYING file in the top-level directory.
|
||||
|
||||
"""
|
||||
qapidoc is a Sphinx extension that implements the qapi-doc directive
|
||||
|
||||
The purpose of this extension is to read the documentation comments
|
||||
in QAPI schema files, and insert them all into the current document.
|
||||
|
||||
It implements one new rST directive, "qapi-doc::".
|
||||
Each qapi-doc:: directive takes one argument, which is the
|
||||
pathname of the schema file to process, relative to the source tree.
|
||||
|
||||
The docs/conf.py file must set the qapidoc_srctree config value to
|
||||
the root of the QEMU source tree.
|
||||
|
||||
The Sphinx documentation on writing extensions is at:
|
||||
https://www.sphinx-doc.org/en/master/development/index.html
|
||||
"""
|
||||
|
||||
import re
|
||||
import textwrap
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.statemachine import ViewList
|
||||
from qapi.error import QAPISemError
|
||||
from qapi.gen import QAPISchemaVisitor
|
||||
from qapi.parser import QAPIDoc
|
||||
|
||||
|
||||
def dedent(text: str) -> str:
|
||||
# Adjust indentation to make description text parse as paragraph.
|
||||
|
||||
lines = text.splitlines(True)
|
||||
if re.match(r"\s+", lines[0]):
|
||||
# First line is indented; description started on the line after
|
||||
# the name. dedent the whole block.
|
||||
return textwrap.dedent(text)
|
||||
|
||||
# Descr started on same line. Dedent line 2+.
|
||||
return lines[0] + textwrap.dedent("".join(lines[1:]))
|
||||
|
||||
|
||||
class QAPISchemaGenRSTVisitor(QAPISchemaVisitor):
|
||||
"""A QAPI schema visitor which generates docutils/Sphinx nodes
|
||||
|
||||
This class builds up a tree of docutils/Sphinx nodes corresponding
|
||||
to documentation for the various QAPI objects. To use it, first
|
||||
create a QAPISchemaGenRSTVisitor object, and call its
|
||||
visit_begin() method. Then you can call one of the two methods
|
||||
'freeform' (to add documentation for a freeform documentation
|
||||
chunk) or 'symbol' (to add documentation for a QAPI symbol). These
|
||||
will cause the visitor to build up the tree of document
|
||||
nodes. Once you've added all the documentation via 'freeform' and
|
||||
'symbol' method calls, you can call 'get_document_nodes' to get
|
||||
the final list of document nodes (in a form suitable for returning
|
||||
from a Sphinx directive's 'run' method).
|
||||
"""
|
||||
def __init__(self, sphinx_directive):
|
||||
self._cur_doc = None
|
||||
self._sphinx_directive = sphinx_directive
|
||||
self._top_node = nodes.section()
|
||||
self._active_headings = [self._top_node]
|
||||
|
||||
def _make_dlitem(self, term, defn):
|
||||
"""Return a dlitem node with the specified term and definition.
|
||||
|
||||
term should be a list of Text and literal nodes.
|
||||
defn should be one of:
|
||||
- a string, which will be handed to _parse_text_into_node
|
||||
- a list of Text and literal nodes, which will be put into
|
||||
a paragraph node
|
||||
"""
|
||||
dlitem = nodes.definition_list_item()
|
||||
dlterm = nodes.term('', '', *term)
|
||||
dlitem += dlterm
|
||||
if defn:
|
||||
dldef = nodes.definition()
|
||||
if isinstance(defn, list):
|
||||
dldef += nodes.paragraph('', '', *defn)
|
||||
else:
|
||||
self._parse_text_into_node(defn, dldef)
|
||||
dlitem += dldef
|
||||
return dlitem
|
||||
|
||||
def _make_section(self, title):
|
||||
"""Return a section node with optional title"""
|
||||
section = nodes.section(ids=[self._sphinx_directive.new_serialno()])
|
||||
if title:
|
||||
section += nodes.title(title, title)
|
||||
return section
|
||||
|
||||
def _nodes_for_ifcond(self, ifcond, with_if=True):
|
||||
"""Return list of Text, literal nodes for the ifcond
|
||||
|
||||
Return a list which gives text like ' (If: condition)'.
|
||||
If with_if is False, we don't return the "(If: " and ")".
|
||||
"""
|
||||
|
||||
doc = ifcond.docgen()
|
||||
if not doc:
|
||||
return []
|
||||
doc = nodes.literal('', doc)
|
||||
if not with_if:
|
||||
return [doc]
|
||||
|
||||
nodelist = [nodes.Text(' ('), nodes.strong('', 'If: ')]
|
||||
nodelist.append(doc)
|
||||
nodelist.append(nodes.Text(')'))
|
||||
return nodelist
|
||||
|
||||
def _nodes_for_one_member(self, member):
|
||||
"""Return list of Text, literal nodes for this member
|
||||
|
||||
Return a list of doctree nodes which give text like
|
||||
'name: type (optional) (If: ...)' suitable for use as the
|
||||
'term' part of a definition list item.
|
||||
"""
|
||||
term = [nodes.literal('', member.name)]
|
||||
if member.type.doc_type():
|
||||
term.append(nodes.Text(': '))
|
||||
term.append(nodes.literal('', member.type.doc_type()))
|
||||
if member.optional:
|
||||
term.append(nodes.Text(' (optional)'))
|
||||
if member.ifcond.is_present():
|
||||
term.extend(self._nodes_for_ifcond(member.ifcond))
|
||||
return term
|
||||
|
||||
def _nodes_for_variant_when(self, branches, variant):
|
||||
"""Return list of Text, literal nodes for variant 'when' clause
|
||||
|
||||
Return a list of doctree nodes which give text like
|
||||
'when tagname is variant (If: ...)' suitable for use in
|
||||
the 'branches' part of a definition list.
|
||||
"""
|
||||
term = [nodes.Text(' when '),
|
||||
nodes.literal('', branches.tag_member.name),
|
||||
nodes.Text(' is '),
|
||||
nodes.literal('', '"%s"' % variant.name)]
|
||||
if variant.ifcond.is_present():
|
||||
term.extend(self._nodes_for_ifcond(variant.ifcond))
|
||||
return term
|
||||
|
||||
def _nodes_for_members(self, doc, what, base=None, branches=None):
|
||||
"""Return list of doctree nodes for the table of members"""
|
||||
dlnode = nodes.definition_list()
|
||||
for section in doc.args.values():
|
||||
term = self._nodes_for_one_member(section.member)
|
||||
# TODO drop fallbacks when undocumented members are outlawed
|
||||
if section.text:
|
||||
defn = dedent(section.text)
|
||||
else:
|
||||
defn = [nodes.Text('Not documented')]
|
||||
|
||||
dlnode += self._make_dlitem(term, defn)
|
||||
|
||||
if base:
|
||||
dlnode += self._make_dlitem([nodes.Text('The members of '),
|
||||
nodes.literal('', base.doc_type())],
|
||||
None)
|
||||
|
||||
if branches:
|
||||
for v in branches.variants:
|
||||
if v.type.name == 'q_empty':
|
||||
continue
|
||||
assert not v.type.is_implicit()
|
||||
term = [nodes.Text('The members of '),
|
||||
nodes.literal('', v.type.doc_type())]
|
||||
term.extend(self._nodes_for_variant_when(branches, v))
|
||||
dlnode += self._make_dlitem(term, None)
|
||||
|
||||
if not dlnode.children:
|
||||
return []
|
||||
|
||||
section = self._make_section(what)
|
||||
section += dlnode
|
||||
return [section]
|
||||
|
||||
def _nodes_for_enum_values(self, doc):
|
||||
"""Return list of doctree nodes for the table of enum values"""
|
||||
seen_item = False
|
||||
dlnode = nodes.definition_list()
|
||||
for section in doc.args.values():
|
||||
termtext = [nodes.literal('', section.member.name)]
|
||||
if section.member.ifcond.is_present():
|
||||
termtext.extend(self._nodes_for_ifcond(section.member.ifcond))
|
||||
# TODO drop fallbacks when undocumented members are outlawed
|
||||
if section.text:
|
||||
defn = dedent(section.text)
|
||||
else:
|
||||
defn = [nodes.Text('Not documented')]
|
||||
|
||||
dlnode += self._make_dlitem(termtext, defn)
|
||||
seen_item = True
|
||||
|
||||
if not seen_item:
|
||||
return []
|
||||
|
||||
section = self._make_section('Values')
|
||||
section += dlnode
|
||||
return [section]
|
||||
|
||||
def _nodes_for_arguments(self, doc, arg_type):
|
||||
"""Return list of doctree nodes for the arguments section"""
|
||||
if arg_type and not arg_type.is_implicit():
|
||||
assert not doc.args
|
||||
section = self._make_section('Arguments')
|
||||
dlnode = nodes.definition_list()
|
||||
dlnode += self._make_dlitem(
|
||||
[nodes.Text('The members of '),
|
||||
nodes.literal('', arg_type.name)],
|
||||
None)
|
||||
section += dlnode
|
||||
return [section]
|
||||
|
||||
return self._nodes_for_members(doc, 'Arguments')
|
||||
|
||||
def _nodes_for_features(self, doc):
|
||||
"""Return list of doctree nodes for the table of features"""
|
||||
seen_item = False
|
||||
dlnode = nodes.definition_list()
|
||||
for section in doc.features.values():
|
||||
dlnode += self._make_dlitem(
|
||||
[nodes.literal('', section.member.name)], dedent(section.text))
|
||||
seen_item = True
|
||||
|
||||
if not seen_item:
|
||||
return []
|
||||
|
||||
section = self._make_section('Features')
|
||||
section += dlnode
|
||||
return [section]
|
||||
|
||||
def _nodes_for_sections(self, doc):
|
||||
"""Return list of doctree nodes for additional sections"""
|
||||
nodelist = []
|
||||
for section in doc.sections:
|
||||
if section.kind == QAPIDoc.Kind.TODO:
|
||||
# Hide TODO: sections
|
||||
continue
|
||||
|
||||
if section.kind == QAPIDoc.Kind.PLAIN:
|
||||
# Sphinx cannot handle sectionless titles;
|
||||
# Instead, just append the results to the prior section.
|
||||
container = nodes.container()
|
||||
self._parse_text_into_node(section.text, container)
|
||||
nodelist += container.children
|
||||
continue
|
||||
|
||||
snode = self._make_section(section.kind.name.title())
|
||||
self._parse_text_into_node(dedent(section.text), snode)
|
||||
nodelist.append(snode)
|
||||
return nodelist
|
||||
|
||||
def _nodes_for_if_section(self, ifcond):
|
||||
"""Return list of doctree nodes for the "If" section"""
|
||||
nodelist = []
|
||||
if ifcond.is_present():
|
||||
snode = self._make_section('If')
|
||||
snode += nodes.paragraph(
|
||||
'', '', *self._nodes_for_ifcond(ifcond, with_if=False)
|
||||
)
|
||||
nodelist.append(snode)
|
||||
return nodelist
|
||||
|
||||
def _add_doc(self, typ, sections):
|
||||
"""Add documentation for a command/object/enum...
|
||||
|
||||
We assume we're documenting the thing defined in self._cur_doc.
|
||||
typ is the type of thing being added ("Command", "Object", etc)
|
||||
|
||||
sections is a list of nodes for sections to add to the definition.
|
||||
"""
|
||||
|
||||
doc = self._cur_doc
|
||||
snode = nodes.section(ids=[self._sphinx_directive.new_serialno()])
|
||||
snode += nodes.title('', '', *[nodes.literal(doc.symbol, doc.symbol),
|
||||
nodes.Text(' (' + typ + ')')])
|
||||
self._parse_text_into_node(doc.body.text, snode)
|
||||
for s in sections:
|
||||
if s is not None:
|
||||
snode += s
|
||||
self._add_node_to_current_heading(snode)
|
||||
|
||||
def visit_enum_type(self, name, info, ifcond, features, members, prefix):
|
||||
doc = self._cur_doc
|
||||
self._add_doc('Enum',
|
||||
self._nodes_for_enum_values(doc)
|
||||
+ self._nodes_for_features(doc)
|
||||
+ self._nodes_for_sections(doc)
|
||||
+ self._nodes_for_if_section(ifcond))
|
||||
|
||||
def visit_object_type(self, name, info, ifcond, features,
|
||||
base, members, branches):
|
||||
doc = self._cur_doc
|
||||
if base and base.is_implicit():
|
||||
base = None
|
||||
self._add_doc('Object',
|
||||
self._nodes_for_members(doc, 'Members', base, branches)
|
||||
+ self._nodes_for_features(doc)
|
||||
+ self._nodes_for_sections(doc)
|
||||
+ self._nodes_for_if_section(ifcond))
|
||||
|
||||
def visit_alternate_type(self, name, info, ifcond, features,
|
||||
alternatives):
|
||||
doc = self._cur_doc
|
||||
self._add_doc('Alternate',
|
||||
self._nodes_for_members(doc, 'Members')
|
||||
+ self._nodes_for_features(doc)
|
||||
+ self._nodes_for_sections(doc)
|
||||
+ self._nodes_for_if_section(ifcond))
|
||||
|
||||
def visit_command(self, name, info, ifcond, features, arg_type,
|
||||
ret_type, gen, success_response, boxed, allow_oob,
|
||||
allow_preconfig, coroutine):
|
||||
doc = self._cur_doc
|
||||
self._add_doc('Command',
|
||||
self._nodes_for_arguments(doc, arg_type)
|
||||
+ self._nodes_for_features(doc)
|
||||
+ self._nodes_for_sections(doc)
|
||||
+ self._nodes_for_if_section(ifcond))
|
||||
|
||||
def visit_event(self, name, info, ifcond, features, arg_type, boxed):
|
||||
doc = self._cur_doc
|
||||
self._add_doc('Event',
|
||||
self._nodes_for_arguments(doc, arg_type)
|
||||
+ self._nodes_for_features(doc)
|
||||
+ self._nodes_for_sections(doc)
|
||||
+ self._nodes_for_if_section(ifcond))
|
||||
|
||||
def symbol(self, doc, entity):
|
||||
"""Add documentation for one symbol to the document tree
|
||||
|
||||
This is the main entry point which causes us to add documentation
|
||||
nodes for a symbol (which could be a 'command', 'object', 'event',
|
||||
etc). We do this by calling 'visit' on the schema entity, which
|
||||
will then call back into one of our visit_* methods, depending
|
||||
on what kind of thing this symbol is.
|
||||
"""
|
||||
self._cur_doc = doc
|
||||
entity.visit(self)
|
||||
self._cur_doc = None
|
||||
|
||||
def _start_new_heading(self, heading, level):
|
||||
"""Start a new heading at the specified heading level
|
||||
|
||||
Create a new section whose title is 'heading' and which is placed
|
||||
in the docutils node tree as a child of the most recent level-1
|
||||
heading. Subsequent document sections (commands, freeform doc chunks,
|
||||
etc) will be placed as children of this new heading section.
|
||||
"""
|
||||
if len(self._active_headings) < level:
|
||||
raise QAPISemError(self._cur_doc.info,
|
||||
'Level %d subheading found outside a '
|
||||
'level %d heading'
|
||||
% (level, level - 1))
|
||||
snode = self._make_section(heading)
|
||||
self._active_headings[level - 1] += snode
|
||||
self._active_headings = self._active_headings[:level]
|
||||
self._active_headings.append(snode)
|
||||
return snode
|
||||
|
||||
def _add_node_to_current_heading(self, node):
|
||||
"""Add the node to whatever the current active heading is"""
|
||||
self._active_headings[-1] += node
|
||||
|
||||
def freeform(self, doc):
|
||||
"""Add a piece of 'freeform' documentation to the document tree
|
||||
|
||||
A 'freeform' document chunk doesn't relate to any particular
|
||||
symbol (for instance, it could be an introduction).
|
||||
|
||||
If the freeform document starts with a line of the form
|
||||
'= Heading text', this is a section or subsection heading, with
|
||||
the heading level indicated by the number of '=' signs.
|
||||
"""
|
||||
|
||||
# QAPIDoc documentation says free-form documentation blocks
|
||||
# must have only a body section, nothing else.
|
||||
assert not doc.sections
|
||||
assert not doc.args
|
||||
assert not doc.features
|
||||
self._cur_doc = doc
|
||||
|
||||
text = doc.body.text
|
||||
if re.match(r'=+ ', text):
|
||||
# Section/subsection heading (if present, will always be
|
||||
# the first line of the block)
|
||||
(heading, _, text) = text.partition('\n')
|
||||
(leader, _, heading) = heading.partition(' ')
|
||||
node = self._start_new_heading(heading, len(leader))
|
||||
if text == '':
|
||||
return
|
||||
else:
|
||||
node = nodes.container()
|
||||
|
||||
self._parse_text_into_node(text, node)
|
||||
self._cur_doc = None
|
||||
|
||||
def _parse_text_into_node(self, doctext, node):
|
||||
"""Parse a chunk of QAPI-doc-format text into the node
|
||||
|
||||
The doc comment can contain most inline rST markup, including
|
||||
bulleted and enumerated lists.
|
||||
As an extra permitted piece of markup, @var will be turned
|
||||
into ``var``.
|
||||
"""
|
||||
|
||||
# Handle the "@var means ``var`` case
|
||||
doctext = re.sub(r'@([\w-]+)', r'``\1``', doctext)
|
||||
|
||||
rstlist = ViewList()
|
||||
for line in doctext.splitlines():
|
||||
# The reported line number will always be that of the start line
|
||||
# of the doc comment, rather than the actual location of the error.
|
||||
# Being more precise would require overhaul of the QAPIDoc class
|
||||
# to track lines more exactly within all the sub-parts of the doc
|
||||
# comment, as well as counting lines here.
|
||||
rstlist.append(line, self._cur_doc.info.fname,
|
||||
self._cur_doc.info.line)
|
||||
# Append a blank line -- in some cases rST syntax errors get
|
||||
# attributed to the line after one with actual text, and if there
|
||||
# isn't anything in the ViewList corresponding to that then Sphinx
|
||||
# 1.6's AutodocReporter will then misidentify the source/line location
|
||||
# in the error message (usually attributing it to the top-level
|
||||
# .rst file rather than the offending .json file). The extra blank
|
||||
# line won't affect the rendered output.
|
||||
rstlist.append("", self._cur_doc.info.fname, self._cur_doc.info.line)
|
||||
self._sphinx_directive.do_parse(rstlist, node)
|
||||
|
||||
def get_document_node(self):
|
||||
"""Return the root docutils node which makes up the document"""
|
||||
return self._top_node
|
||||
@@ -3,6 +3,6 @@
|
||||
|
||||
python3 -m isort --sp . -c ../scripts/qapi/
|
||||
# Force isort to recognize "compat" as a local module and not third-party
|
||||
python3 -m isort --sp . -c -p compat -p qapidoc_legacy \
|
||||
python3 -m isort --sp . -c -p compat \
|
||||
../docs/sphinx/qapi_domain.py \
|
||||
../docs/sphinx/qapidoc.py
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
##
|
||||
# = ACPI
|
||||
# ****
|
||||
# ACPI
|
||||
# ****
|
||||
##
|
||||
|
||||
##
|
||||
@@ -106,7 +108,7 @@
|
||||
##
|
||||
# @query-acpi-ospm-status:
|
||||
#
|
||||
# Return a list of ACPIOSTInfo for devices that support status
|
||||
# Return a list of `ACPIOSTInfo` for devices that support status
|
||||
# reporting via ACPI _OST method.
|
||||
#
|
||||
# Since: 2.1
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
# See the COPYING file in the top-level directory.
|
||||
|
||||
##
|
||||
# = Audio
|
||||
# *****
|
||||
# Audio
|
||||
# *****
|
||||
##
|
||||
|
||||
##
|
||||
@@ -535,8 +537,6 @@
|
||||
#
|
||||
# Return information about audiodev configuration
|
||||
#
|
||||
# Returns: array of @Audiodev
|
||||
#
|
||||
# Since: 8.0
|
||||
##
|
||||
{ 'command': 'query-audiodevs',
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
# vim: filetype=python
|
||||
|
||||
##
|
||||
# = User authorization
|
||||
# ******************
|
||||
# User authorization
|
||||
# ******************
|
||||
##
|
||||
|
||||
##
|
||||
@@ -75,7 +77,7 @@
|
||||
# Properties for authz-listfile objects.
|
||||
#
|
||||
# @filename: File name to load the configuration from. The file must
|
||||
# contain valid JSON for AuthZListProperties.
|
||||
# contain valid JSON for `AuthZListProperties`.
|
||||
#
|
||||
# @refresh: If true, inotify is used to monitor the file,
|
||||
# automatically reloading changes. If an error occurs during
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,8 @@
|
||||
# vim: filetype=python
|
||||
|
||||
##
|
||||
# == Block device exports
|
||||
# Block device exports
|
||||
# ====================
|
||||
##
|
||||
|
||||
{ 'include': 'sockets.json' }
|
||||
@@ -37,9 +38,9 @@
|
||||
##
|
||||
# @NbdServerOptions:
|
||||
#
|
||||
# Keep this type consistent with the NbdServerOptionsLegacy type. The
|
||||
# only intended difference is using SocketAddress instead of
|
||||
# SocketAddressLegacy.
|
||||
# Keep this type consistent with the `NbdServerOptionsLegacy` type. The
|
||||
# only intended difference is using `SocketAddress` instead of
|
||||
# `SocketAddressLegacy`.
|
||||
#
|
||||
# @addr: Address on which to listen (since 4.2).
|
||||
##
|
||||
@@ -50,9 +51,9 @@
|
||||
##
|
||||
# @NbdServerOptionsLegacy:
|
||||
#
|
||||
# Keep this type consistent with the NbdServerOptions type. The only
|
||||
# intended difference is using SocketAddressLegacy instead of
|
||||
# SocketAddress.
|
||||
# Keep this type consistent with the `NbdServerOptions` type. The only
|
||||
# intended difference is using `SocketAddressLegacy` instead of
|
||||
# `SocketAddress`.
|
||||
#
|
||||
# @addr: Address on which to listen (since 1.3).
|
||||
##
|
||||
@@ -64,7 +65,7 @@
|
||||
# @nbd-server-start:
|
||||
#
|
||||
# Start an NBD server listening on the given host and port. Block
|
||||
# devices can then be exported using @nbd-server-add. The NBD server
|
||||
# devices can then be exported using `nbd-server-add`. The NBD server
|
||||
# will present them as named exports; for example, another QEMU
|
||||
# instance could refer to them as "nbd:HOST:PORT:exportname=NAME".
|
||||
#
|
||||
@@ -80,8 +81,8 @@
|
||||
##
|
||||
# @BlockExportOptionsNbdBase:
|
||||
#
|
||||
# An NBD block export (common options shared between nbd-server-add
|
||||
# and the NBD branch of block-export-add).
|
||||
# An NBD block export (common options shared between `nbd-server-add`
|
||||
# and the NBD branch of `block-export-add`).
|
||||
#
|
||||
# @name: Export name. If unspecified, the @device parameter is used
|
||||
# as the export name. (Since 2.12)
|
||||
@@ -98,7 +99,7 @@
|
||||
# @BlockExportOptionsNbd:
|
||||
#
|
||||
# An NBD block export (distinct options used in the NBD branch of
|
||||
# block-export-add).
|
||||
# `block-export-add`).
|
||||
#
|
||||
# @bitmaps: Also export each of the named dirty bitmaps reachable from
|
||||
# @device, so the NBD client can use NBD_OPT_SET_META_CONTEXT with
|
||||
@@ -124,7 +125,7 @@
|
||||
# A vhost-user-blk block export.
|
||||
#
|
||||
# @addr: The vhost-user socket on which to listen. Both 'unix' and
|
||||
# 'fd' SocketAddress types are supported. Passed fds must be UNIX
|
||||
# 'fd' `SocketAddress` types are supported. Passed fds must be UNIX
|
||||
# domain sockets.
|
||||
#
|
||||
# @logical-block-size: Logical block size in bytes. Defaults to 512
|
||||
@@ -216,7 +217,7 @@
|
||||
##
|
||||
# @NbdServerAddOptions:
|
||||
#
|
||||
# An NBD block export, per legacy nbd-server-add command.
|
||||
# An NBD block export, per legacy `nbd-server-add` command.
|
||||
#
|
||||
# @device: The device name or node name of the node to be exported
|
||||
#
|
||||
@@ -245,7 +246,7 @@
|
||||
#
|
||||
# Features:
|
||||
#
|
||||
# @deprecated: This command is deprecated. Use @block-export-add
|
||||
# @deprecated: This command is deprecated. Use `block-export-add`
|
||||
# instead.
|
||||
#
|
||||
# Errors:
|
||||
@@ -288,12 +289,12 @@
|
||||
#
|
||||
# @name: Block export id.
|
||||
#
|
||||
# @mode: Mode of command operation. See @BlockExportRemoveMode
|
||||
# @mode: Mode of command operation. See `BlockExportRemoveMode`
|
||||
# description. Default is 'safe'.
|
||||
#
|
||||
# Features:
|
||||
#
|
||||
# @deprecated: This command is deprecated. Use @block-export-del
|
||||
# @deprecated: This command is deprecated. Use `block-export-del`
|
||||
# instead.
|
||||
#
|
||||
# Errors:
|
||||
@@ -312,7 +313,7 @@
|
||||
# @nbd-server-stop:
|
||||
#
|
||||
# Stop QEMU's embedded NBD server, and unregister all devices
|
||||
# previously added via @nbd-server-add.
|
||||
# previously added via `nbd-server-add`.
|
||||
#
|
||||
# Since: 1.3
|
||||
##
|
||||
@@ -421,7 +422,7 @@
|
||||
#
|
||||
# @id: Block export id.
|
||||
#
|
||||
# @mode: Mode of command operation. See @BlockExportRemoveMode
|
||||
# @mode: Mode of command operation. See `BlockExportRemoveMode`
|
||||
# description. Default is 'safe'.
|
||||
#
|
||||
# Errors:
|
||||
@@ -459,7 +460,7 @@
|
||||
# @node-name: The node name of the block node that is exported
|
||||
#
|
||||
# @shutting-down: True if the export is shutting down (e.g. after a
|
||||
# block-export-del command, but before the shutdown has completed)
|
||||
# `block-export-del` command, but before the shutdown has completed)
|
||||
#
|
||||
# Since: 5.2
|
||||
##
|
||||
@@ -472,7 +473,7 @@
|
||||
##
|
||||
# @query-block-exports:
|
||||
#
|
||||
# Returns: A list of BlockExportInfo describing all block exports
|
||||
# Returns: A list describing all block exports
|
||||
#
|
||||
# Since: 5.2
|
||||
##
|
||||
|
||||
@@ -2,13 +2,16 @@
|
||||
# vim: filetype=python
|
||||
|
||||
##
|
||||
# = Block devices
|
||||
# *************
|
||||
# Block devices
|
||||
# *************
|
||||
##
|
||||
|
||||
{ 'include': 'block-core.json' }
|
||||
|
||||
##
|
||||
# == Additional block stuff (VM related)
|
||||
# Additional block stuff (VM related)
|
||||
# ===================================
|
||||
##
|
||||
|
||||
##
|
||||
@@ -86,7 +89,7 @@
|
||||
# Return a list of information about each persistent reservation
|
||||
# manager.
|
||||
#
|
||||
# Returns: a list of @PRManagerInfo for each persistent reservation
|
||||
# Returns: a list of manager info for each persistent reservation
|
||||
# manager
|
||||
#
|
||||
# Since: 3.0
|
||||
@@ -137,7 +140,7 @@
|
||||
#
|
||||
# If the tray was already open before, this will be a no-op.
|
||||
#
|
||||
# Once the tray opens, a DEVICE_TRAY_MOVED event is emitted. There
|
||||
# Once the tray opens, a `DEVICE_TRAY_MOVED` event is emitted. There
|
||||
# are cases in which no such event will be generated, these include:
|
||||
#
|
||||
# - if the guest has locked the tray, @force is false and the guest
|
||||
@@ -296,7 +299,7 @@
|
||||
# @BlockdevChangeReadOnlyMode:
|
||||
#
|
||||
# Specifies the new read-only mode of a block device subject to the
|
||||
# @blockdev-change-medium command.
|
||||
# `blockdev-change-medium` command.
|
||||
#
|
||||
# @retain: Retains the current read-only mode
|
||||
#
|
||||
@@ -314,9 +317,9 @@
|
||||
#
|
||||
# Changes the medium inserted into a block device by ejecting the
|
||||
# current medium and loading a new image file which is inserted as the
|
||||
# new medium (this command combines blockdev-open-tray,
|
||||
# blockdev-remove-medium, blockdev-insert-medium and
|
||||
# blockdev-close-tray).
|
||||
# new medium (this command combines `blockdev-open-tray`,
|
||||
# `blockdev-remove-medium`, `blockdev-insert-medium` and
|
||||
# `blockdev-close-tray`).
|
||||
#
|
||||
# @device: Block device name
|
||||
#
|
||||
@@ -331,7 +334,7 @@
|
||||
# to 'retain'
|
||||
#
|
||||
# @force: if false (the default), an eject request through
|
||||
# blockdev-open-tray will be sent to the guest if it has locked
|
||||
# `blockdev-open-tray` will be sent to the guest if it has locked
|
||||
# the tray (and the tray will not be opened immediately); if true,
|
||||
# the tray will be opened regardless of whether it is locked.
|
||||
# (since 7.1)
|
||||
@@ -519,7 +522,7 @@
|
||||
# @id: The name or QOM path of the guest device.
|
||||
#
|
||||
# @boundaries: list of interval boundary values (see description in
|
||||
# BlockLatencyHistogramInfo definition). If specified, all
|
||||
# `BlockLatencyHistogramInfo` definition). If specified, all
|
||||
# latency histograms are removed, and empty ones created for all
|
||||
# io types with intervals corresponding to @boundaries (except for
|
||||
# io types, for which specific boundaries are set through the
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
#
|
||||
|
||||
##
|
||||
# = Character devices
|
||||
# *****************
|
||||
# Character devices
|
||||
# *****************
|
||||
##
|
||||
|
||||
{ 'include': 'sockets.json' }
|
||||
@@ -36,8 +38,6 @@
|
||||
#
|
||||
# Return information about current character devices.
|
||||
#
|
||||
# Returns: a list of @ChardevInfo
|
||||
#
|
||||
# Since: 0.14
|
||||
#
|
||||
# .. qmp-example::
|
||||
@@ -82,8 +82,6 @@
|
||||
#
|
||||
# Return information about character device backends.
|
||||
#
|
||||
# Returns: a list of @ChardevBackendInfo
|
||||
#
|
||||
# Since: 2.0
|
||||
#
|
||||
# .. qmp-example::
|
||||
@@ -772,8 +770,6 @@
|
||||
#
|
||||
# @backend: backend type and parameters
|
||||
#
|
||||
# Returns: ChardevReturn.
|
||||
#
|
||||
# Since: 1.4
|
||||
#
|
||||
# .. qmp-example::
|
||||
@@ -812,8 +808,6 @@
|
||||
#
|
||||
# @backend: new backend type and parameters
|
||||
#
|
||||
# Returns: ChardevReturn.
|
||||
#
|
||||
# Since: 2.10
|
||||
#
|
||||
# .. qmp-example::
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
# vim: filetype=python
|
||||
|
||||
##
|
||||
# = Common data types
|
||||
# *****************
|
||||
# Common data types
|
||||
# *****************
|
||||
##
|
||||
|
||||
##
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
# vim: filetype=python
|
||||
|
||||
##
|
||||
# = Compatibility policy
|
||||
# ********************
|
||||
# Compatibility policy
|
||||
# ********************
|
||||
##
|
||||
|
||||
##
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user