QAPI patches patches for 2025-03-14

-----BEGIN PGP SIGNATURE-----
 
 iQJGBAABCAAwFiEENUvIs9frKmtoZ05fOHC0AOuRhlMFAmfT/U0SHGFybWJydUBy
 ZWRoYXQuY29tAAoJEDhwtADrkYZThb4P/i2FNedYYeU+qOAtjKwCE0bnbtxWdthj
 Zd+0u0LOXxkK7+nqgva+2+Szl4Ee0rYrbwVjd26nYRtB/m1/q1Glj1GTTAO+Xzpb
 3q4/ByFTDG3/mFktfVkE5HAJ7RGbjI3toRFWbpw1C4RabkX+dyZZ0MVwkfBwiyY7
 bEW7cW9OZlIXbMS867n7gURqEsD+LWXzxX5ozeWZGQVTp5nbQdubulYTkxJTXK+A
 as2Q+RJhfB2lVJHAY3xN6R+gjHUNCBfwzfSFGMTMr+tYPeHZVssWeypXJJ9Qh7aA
 dVLfVCY6PbstrGD1dGybIY1HfUTjJQNiyZ3qIoRfkxsfZcO7ru6Q5CMfEgxwcu53
 FaXLB3ra3R5cmYKFVeasEKHo/xsXeb3MAKCGLLqp7gC2GGdGvZAyHJevFZJslC+Q
 /AbGtbmNYOYCkJdbT3r8bu9Qc7p2llw24Pjw/9I/qvwkKy3xdDyZQS+lT/vyYZvS
 zc/hnlJR8UQvGXtzf0OrNCf8lDswNP6r51eTpno0OCQatrDi0ZjZqIOxHUUOn1pr
 AE4JRDjtDoOqw8ltZxrulsiySSHewM4ouS3MXylpMk1PoWNq/6v8nUYL7p2RGgMq
 FKyEdInExe1dWEjwaqPABBHdAWpZbmH0wmRLgeFaDvgmqqrOqFFeBKbgLFC2xcX5
 pgR35cz28GUh
 =0HX3
 -----END PGP SIGNATURE-----

Merge tag 'pull-qapi-2025-03-14' of https://repo.or.cz/qemu/armbru into staging

QAPI patches patches for 2025-03-14

# -----BEGIN PGP SIGNATURE-----
#
# iQJGBAABCAAwFiEENUvIs9frKmtoZ05fOHC0AOuRhlMFAmfT/U0SHGFybWJydUBy
# ZWRoYXQuY29tAAoJEDhwtADrkYZThb4P/i2FNedYYeU+qOAtjKwCE0bnbtxWdthj
# Zd+0u0LOXxkK7+nqgva+2+Szl4Ee0rYrbwVjd26nYRtB/m1/q1Glj1GTTAO+Xzpb
# 3q4/ByFTDG3/mFktfVkE5HAJ7RGbjI3toRFWbpw1C4RabkX+dyZZ0MVwkfBwiyY7
# bEW7cW9OZlIXbMS867n7gURqEsD+LWXzxX5ozeWZGQVTp5nbQdubulYTkxJTXK+A
# as2Q+RJhfB2lVJHAY3xN6R+gjHUNCBfwzfSFGMTMr+tYPeHZVssWeypXJJ9Qh7aA
# dVLfVCY6PbstrGD1dGybIY1HfUTjJQNiyZ3qIoRfkxsfZcO7ru6Q5CMfEgxwcu53
# FaXLB3ra3R5cmYKFVeasEKHo/xsXeb3MAKCGLLqp7gC2GGdGvZAyHJevFZJslC+Q
# /AbGtbmNYOYCkJdbT3r8bu9Qc7p2llw24Pjw/9I/qvwkKy3xdDyZQS+lT/vyYZvS
# zc/hnlJR8UQvGXtzf0OrNCf8lDswNP6r51eTpno0OCQatrDi0ZjZqIOxHUUOn1pr
# AE4JRDjtDoOqw8ltZxrulsiySSHewM4ouS3MXylpMk1PoWNq/6v8nUYL7p2RGgMq
# FKyEdInExe1dWEjwaqPABBHdAWpZbmH0wmRLgeFaDvgmqqrOqFFeBKbgLFC2xcX5
# pgR35cz28GUh
# =0HX3
# -----END PGP SIGNATURE-----
# gpg: Signature made Fri 14 Mar 2025 05:56:29 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-03-14' of https://repo.or.cz/qemu/armbru:
  docs: enable transmogrifier for QSD and QGA
  docs: disambiguate references in qapi-domain.rst
  docs: add QAPI namespace "QMP" to qemu-qmp-ref
  docs/qapi-domain: add namespaced index support
  docs/qapi_domain: add namespace support to cross-references
  docs/qapidoc: add :namespace: option to qapi-doc directive
  docs/qapi-domain: add qapi:namespace directive
  docs/qapi-domain: add :namespace: override option
  docs/qapi_domain: add namespace support to FQN
  docs/qapi-domain: always store fully qualified name in signode
  docs/qapi_domain: isolate TYPE_CHECKING imports
  qapi/block-core: Improve x-blockdev-change documentation

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
Stefan Hajnoczi 2025-03-16 02:44:56 -04:00
commit 9beccc2df0
11 changed files with 315 additions and 117 deletions

View File

@ -161,6 +161,13 @@ qapi_allowed_fields = {
"see also",
}
# Due to a limitation in Sphinx, we need to know which indices to
# generate in advance. Adding a namespace here allows that generation.
qapi_namespaces = {
"QGA",
"QMP",
"QSD",
}
# -- Options for HTML output ----------------------------------------------

View File

@ -385,13 +385,13 @@ Type names in references can be surrounded by brackets, like
``[typename]``, to indicate an array of that type. The cross-reference
will apply only to the type name between the brackets. For example;
``:qapi:type:`[Qcow2BitmapInfoFlags]``` renders to:
:qapi:type:`[Qcow2BitmapInfoFlags]`
:qapi:type:`[QMP:Qcow2BitmapInfoFlags]`
To indicate an optional argument/member in a field list, the type name
can be suffixed with ``?``. The cross-reference will be transformed to
"type, Optional" with the link applying only to the type name. For
example; ``:qapi:type:`BitmapSyncMode?``` renders to:
:qapi:type:`BitmapSyncMode?`
:qapi:type:`QMP:BitmapSyncMode?`
Namespaces
@ -400,17 +400,38 @@ Namespaces
Mimicking the `Python domain target specification syntax
<https://www.sphinx-doc.org/en/master/usage/domains/python.html#target-specification>`_,
QAPI allows you to specify the fully qualified path for a data
type. QAPI enforces globally unique names, so it's unlikely you'll need
this specific feature, but it may be extended in the near future to
allow referencing identically named commands and data types from
different utilities; i.e. QEMU Storage Daemon vs QMP.
type.
* A namespace can be explicitly provided;
e.g. ``:qapi:type:`QMP:BitmapSyncMode``
* A module can be explicitly provided;
``:qapi:type:`block-core.BitmapSyncMode``` will render to:
:qapi:type:`block-core.BitmapSyncMode`
``:qapi:type:`QMP:block-core.BitmapSyncMode``` will render to:
:qapi:type:`QMP:block-core.BitmapSyncMode`
* If you don't want to display the "fully qualified" name, it can be
prefixed with a tilde; ``:qapi:type:`~block-core.BitmapSyncMode```
will render to: :qapi:type:`~block-core.BitmapSyncMode`
prefixed with a tilde; ``:qapi:type:`~QMP:block-core.BitmapSyncMode```
will render to: :qapi:type:`~QMP:block-core.BitmapSyncMode`
Target resolution
-----------------
Any cross-reference to a QAPI type, whether using the ```any``` style of
reference or the more explicit ```:qapi:any:`target``` syntax, allows
for the presence or absence of either the namespace or module
information.
When absent, their value will be inferred from context by the presence
of any ``qapi:namespace`` or ``qapi:module`` directives preceding the
cross-reference.
If no results are found when using the inferred values, other
namespaces/modules will be searched as a last resort; but any explicitly
provided values must always match in order to succeed.
This allows for efficient cross-referencing with a minimum of syntax in
the large majority of cases, but additional context or namespace markup
may be required outside of the QAPI reference documents when linking to
items that share a name across multiple documented QAPI schema.
Custom link text
@ -423,7 +444,7 @@ using the ``custom text <target>`` syntax.
For example, ``:qapi:cmd:`Merge dirty bitmaps
<block-dirty-bitmap-merge>``` will render as: :qapi:cmd:`Merge dirty
bitmaps <block-dirty-bitmap-merge>`
bitmaps <QMP:block-dirty-bitmap-merge>`
Directives
@ -464,8 +485,11 @@ removed in a future version.
QAPI standard options
---------------------
All QAPI directives -- *except* for module -- support these common options.
All QAPI directives -- *except* for namespace and module -- support
these common options.
* ``:namespace: name`` -- This option allows you to override the
namespace association of a given definition.
* ``:module: modname`` -- Borrowed from the Python domain, this option allows
you to override the module association of a given definition.
* ``:since: x.y`` -- Allows the documenting of "Since" information, which is
@ -480,6 +504,28 @@ All QAPI directives -- *except* for module -- support these common options.
production code.
qapi:namespace
--------------
The ``qapi:namespace`` directive marks the start of a QAPI namespace. It
does not take a content body, nor any options. All subsequent QAPI
directives are associated with the most recent namespace. This affects
the definition's "fully qualified name", allowing two different
namespaces to create an otherwise identically named definition.
This directive also influences how reference resolution works for any
references that do not explicity specify a namespace, so this directive
can be used to nudge references into preferring targets from within that
namespace.
Example::
.. qapi:namespace:: QMP
This directive has no visible effect.
qapi:module
-----------

View File

@ -5,3 +5,5 @@ QEMU Guest Agent Protocol Reference
:depth: 3
.. qapi-doc:: qga/qapi-schema.json
:transmogrify:
:namespace: QGA

View File

@ -8,3 +8,4 @@ QEMU QMP Reference Manual
.. qapi-doc:: qapi/qapi-schema.json
:transmogrify:
:namespace: QMP

View File

@ -5,3 +5,5 @@ QEMU Storage Daemon QMP Reference Manual
:depth: 3
.. qapi-doc:: storage-daemon/qapi/qapi-schema.json
:transmogrify:
:namespace: QSD

View File

@ -7,17 +7,14 @@ QAPI domain extension.
from __future__ import annotations
import re
import types
from typing import (
TYPE_CHECKING,
AbstractSet,
Any,
Dict,
Iterable,
List,
NamedTuple,
Optional,
Tuple,
Union,
Type,
cast,
)
@ -34,7 +31,6 @@ from compat import (
SpaceNode,
)
from sphinx import addnodes
from sphinx.addnodes import desc_signature, pending_xref
from sphinx.directives import ObjectDescription
from sphinx.domains import (
Domain,
@ -45,17 +41,29 @@ from sphinx.domains import (
from sphinx.locale import _, __
from sphinx.roles import XRefRole
from sphinx.util import logging
from sphinx.util.docutils import SphinxDirective
from sphinx.util.nodes import make_id, make_refnode
if TYPE_CHECKING:
from typing import (
AbstractSet,
Any,
Dict,
Iterable,
Optional,
Union,
)
from docutils.nodes import Element, Node
from sphinx.addnodes import desc_signature, pending_xref
from sphinx.application import Sphinx
from sphinx.builders import Builder
from sphinx.environment import BuildEnvironment
from sphinx.util.typing import OptionSpec
logger = logging.getLogger(__name__)
@ -89,6 +97,7 @@ class QAPIXRefRole(XRefRole):
title: str,
target: str,
) -> tuple[str, str]:
refnode["qapi:namespace"] = env.ref_context.get("qapi:namespace")
refnode["qapi:module"] = env.ref_context.get("qapi:module")
# Cross-references that begin with a tilde adjust the title to
@ -174,6 +183,30 @@ class QAPIDescription(ParserFix):
# NB: this is used for the global index, not the QAPI index.
return ("single", f"{name} (QMP {self.objtype})")
def _get_context(self) -> Tuple[str, str]:
namespace = self.options.get(
"namespace", self.env.ref_context.get("qapi:namespace", "")
)
modname = self.options.get(
"module", self.env.ref_context.get("qapi:module", "")
)
return namespace, modname
def _get_fqn(self, name: Signature) -> str:
namespace, modname = self._get_context()
# If we're documenting a module, don't include the module as
# part of the FQN; we ARE the module!
if self.objtype == "module":
modname = ""
if modname:
name = f"{modname}.{name}"
if namespace:
name = f"{namespace}:{name}"
return name
def add_target_and_index(
self, name: Signature, sig: str, signode: desc_signature
) -> None:
@ -183,14 +216,8 @@ class QAPIDescription(ParserFix):
assert self.objtype
# If we're documenting a module, don't include the module as
# part of the FQN.
modname = ""
if self.objtype != "module":
modname = self.options.get(
"module", self.env.ref_context.get("qapi:module")
)
fullname = (modname + "." if modname else "") + name
if not (fullname := signode.get("fullname", "")):
fullname = self._get_fqn(name)
node_id = make_id(
self.env, self.state.document, self.objtype, fullname
@ -209,18 +236,26 @@ class QAPIDescription(ParserFix):
(arity, indextext, node_id, "", None)
)
@staticmethod
def split_fqn(name: str) -> Tuple[str, str, str]:
if ":" in name:
ns, name = name.split(":")
else:
ns = ""
if "." in name:
module, name = name.split(".")
else:
module = ""
return (ns, module, name)
def _object_hierarchy_parts(
self, sig_node: desc_signature
) -> Tuple[str, ...]:
if "fullname" not in sig_node:
return ()
modname = sig_node.get("module")
fullname = sig_node["fullname"]
if modname:
return (modname, *fullname.split("."))
return tuple(fullname.split("."))
return self.split_fqn(sig_node["fullname"])
def _toc_entry_name(self, sig_node: desc_signature) -> str:
# This controls the name in the TOC and on the sidebar.
@ -231,13 +266,23 @@ class QAPIDescription(ParserFix):
return ""
config = self.env.app.config
*parents, name = toc_parts
namespace, modname, name = toc_parts
if config.toc_object_entries_show_parents == "domain":
return sig_node.get("fullname", name)
ret = name
if modname and modname != self.env.ref_context.get(
"qapi:module", ""
):
ret = f"{modname}.{name}"
if namespace and namespace != self.env.ref_context.get(
"qapi:namespace", ""
):
ret = f"{namespace}:{ret}"
return ret
if config.toc_object_entries_show_parents == "hide":
return name
if config.toc_object_entries_show_parents == "all":
return ".".join(parents + [name])
return sig_node.get("fullname", name)
return ""
@ -254,8 +299,9 @@ class QAPIObject(QAPIDescription):
)
option_spec.update(
{
# Borrowed from the Python domain:
"module": directives.unchanged, # Override contextual module name
# Context overrides:
"namespace": directives.unchanged,
"module": directives.unchanged,
# These are QAPI originals:
"since": directives.unchanged,
"ifcond": directives.unchanged,
@ -308,12 +354,15 @@ class QAPIObject(QAPIDescription):
As such, the only argument here is "sig", which is just the QAPI
definition name.
"""
modname = self.options.get(
"module", self.env.ref_context.get("qapi:module")
)
# No module or domain info allowed in the signature!
assert ":" not in sig
assert "." not in sig
signode["fullname"] = sig
namespace, modname = self._get_context()
signode["fullname"] = self._get_fqn(sig)
signode["namespace"] = namespace
signode["module"] = modname
sig_prefix = self.get_signature_prefix()
if sig_prefix:
signode += addnodes.desc_annotation(
@ -601,6 +650,17 @@ class QAPIModule(QAPIDescription):
return ret
class QAPINamespace(SphinxDirective):
has_content = False
required_arguments = 1
def run(self) -> List[Node]:
namespace = self.arguments[0].strip()
self.env.ref_context["qapi:namespace"] = namespace
return []
class QAPIIndex(Index):
"""
Index subclass to provide the QAPI definition index.
@ -611,6 +671,7 @@ class QAPIIndex(Index):
name = "index"
localname = _("QAPI Index")
shortname = _("QAPI Index")
namespace = ""
def generate(
self,
@ -620,25 +681,20 @@ class QAPIIndex(Index):
content: Dict[str, List[IndexEntry]] = {}
collapse = False
# list of all object (name, ObjectEntry) pairs, sorted by name
# (ignoring the module)
objects = sorted(
self.domain.objects.items(),
key=lambda x: x[0].split(".")[-1].lower(),
)
for objname, obj in objects:
for objname, obj in self.domain.objects.items():
if docnames and obj.docname not in docnames:
continue
# Strip the module name out:
objname = objname.split(".")[-1]
ns, _mod, name = QAPIDescription.split_fqn(objname)
if self.namespace != ns:
continue
# Add an alphabetical entry:
entries = content.setdefault(objname[0].upper(), [])
entries = content.setdefault(name[0].upper(), [])
entries.append(
IndexEntry(
objname, 0, obj.docname, obj.node_id, obj.objtype, "", ""
name, 0, obj.docname, obj.node_id, obj.objtype, "", ""
)
)
@ -646,10 +702,14 @@ class QAPIIndex(Index):
category = obj.objtype.title() + "s"
entries = content.setdefault(category, [])
entries.append(
IndexEntry(objname, 0, obj.docname, obj.node_id, "", "", "")
IndexEntry(name, 0, obj.docname, obj.node_id, "", "", "")
)
# alphabetically sort categories; type names first, ABC entries last.
# Sort entries within each category alphabetically
for category in content:
content[category] = sorted(content[category])
# Sort the categories themselves; type names first, ABC entries last.
sorted_content = sorted(
content.items(),
key=lambda x: (len(x[0]) == 1, x[0]),
@ -682,6 +742,7 @@ class QAPIDomain(Domain):
# Each of these provides a rST directive,
# e.g. .. qapi:module:: block-core
directives = {
"namespace": QAPINamespace,
"module": QAPIModule,
"command": QAPICommand,
"event": QAPIEvent,
@ -721,6 +782,21 @@ class QAPIDomain(Domain):
ret = self.data.setdefault("objects", {})
return ret # type: ignore[no-any-return]
def setup(self) -> None:
namespaces = set(self.env.app.config.qapi_namespaces)
for namespace in namespaces:
new_index: Type[QAPIIndex] = types.new_class(
f"{namespace}Index", bases=(QAPIIndex,)
)
new_index.name = f"{namespace.lower()}-index"
new_index.localname = _(f"{namespace} Index")
new_index.shortname = _(f"{namespace} Index")
new_index.namespace = namespace
self.indices.append(new_index)
super().setup()
def note_object(
self,
name: str,
@ -773,40 +849,44 @@ class QAPIDomain(Domain):
self.objects[fullname] = obj
def find_obj(
self, modname: str, name: str, typ: Optional[str]
) -> list[tuple[str, ObjectEntry]]:
self, namespace: str, modname: str, name: str, typ: Optional[str]
) -> List[Tuple[str, ObjectEntry]]:
"""
Find a QAPI object for "name", perhaps using the given module.
Find a QAPI object for "name", maybe using contextual information.
Returns a list of (name, object entry) tuples.
:param modname: The current module context (if any!)
under which we are searching.
:param name: The name of the x-ref to resolve;
may or may not include a leading module.
:param type: The role name of the x-ref we're resolving, if provided.
(This is absent for "any" lookups.)
:param namespace: The current namespace context (if any!) under
which we are searching.
:param modname: The current module context (if any!) under
which we are searching.
:param name: The name of the x-ref to resolve; may or may not
include leading context.
:param type: The role name of the x-ref we're resolving, if
provided. This is absent for "any" role lookups.
"""
if not name:
return []
names: list[str] = []
matches: list[tuple[str, ObjectEntry]] = []
# ##
# what to search for
# ##
fullname = name
if "." in fullname:
# We're searching for a fully qualified reference;
# ignore the contextual module.
pass
elif modname:
# We're searching for something from somewhere;
# try searching the current module first.
# e.g. :qapi:cmd:`query-block` or `query-block` is being searched.
fullname = f"{modname}.{name}"
parts = list(QAPIDescription.split_fqn(name))
explicit = tuple(bool(x) for x in parts)
# Fill in the blanks where possible:
if namespace and not parts[0]:
parts[0] = namespace
if modname and not parts[1]:
parts[1] = modname
implicit_fqn = ""
if all(parts):
implicit_fqn = f"{parts[0]}:{parts[1]}.{parts[2]}"
if typ is None:
# type isn't specified, this is a generic xref.
# search *all* qapi-specific object types.
# :any: lookup, search everything:
objtypes: List[str] = list(self.object_types)
else:
# type is specified and will be a role (e.g. obj, mod, cmd)
@ -814,25 +894,57 @@ class QAPIDomain(Domain):
# using the QAPIDomain.object_types table.
objtypes = self.objtypes_for_role(typ, [])
if name in self.objects and self.objects[name].objtype in objtypes:
names = [name]
elif (
fullname in self.objects
and self.objects[fullname].objtype in objtypes
):
names = [fullname]
else:
# exact match wasn't found; e.g. we are searching for
# `query-block` from a different (or no) module.
searchname = "." + name
names = [
oname
for oname in self.objects
if oname.endswith(searchname)
and self.objects[oname].objtype in objtypes
]
# ##
# search!
# ##
matches = [(oname, self.objects[oname]) for oname in names]
def _search(needle: str) -> List[str]:
if (
needle
and needle in self.objects
and self.objects[needle].objtype in objtypes
):
return [needle]
return []
if found := _search(name):
# Exact match!
pass
elif found := _search(implicit_fqn):
# Exact match using contextual information to fill in the gaps.
pass
else:
# No exact hits, perform applicable fuzzy searches.
searches = []
esc = tuple(re.escape(s) for s in parts)
# Try searching for ns:*.name or ns:name
if explicit[0] and not explicit[1]:
searches.append(f"^{esc[0]}:([^\\.]+\\.)?{esc[2]}$")
# Try searching for *:module.name or module.name
if explicit[1] and not explicit[0]:
searches.append(f"(^|:){esc[1]}\\.{esc[2]}$")
# Try searching for context-ns:*.name or context-ns:name
if parts[0] and not (explicit[0] or explicit[1]):
searches.append(f"^{esc[0]}:([^\\.]+\\.)?{esc[2]}$")
# Try searching for *:context-mod.name or context-mod.name
if parts[1] and not (explicit[0] or explicit[1]):
searches.append(f"(^|:){esc[1]}\\.{esc[2]}$")
# Try searching for *:name, *.name, or name
if not (explicit[0] or explicit[1]):
searches.append(f"(^|:|\\.){esc[2]}$")
for search in searches:
if found := [
oname
for oname in self.objects
if re.search(search, oname)
and self.objects[oname].objtype in objtypes
]:
break
matches = [(oname, self.objects[oname]) for oname in found]
if len(matches) > 1:
matches = [m for m in matches if not m[1].aliased]
return matches
@ -847,8 +959,9 @@ class QAPIDomain(Domain):
node: pending_xref,
contnode: Element,
) -> nodes.reference | None:
namespace = node.get("qapi:namespace")
modname = node.get("qapi:module")
matches = self.find_obj(modname, target, typ)
matches = self.find_obj(namespace, modname, target, typ)
if not matches:
# Normally, we could pass warn_dangling=True to QAPIXRefRole(),
@ -901,7 +1014,9 @@ class QAPIDomain(Domain):
contnode: Element,
) -> List[Tuple[str, nodes.reference]]:
results: List[Tuple[str, nodes.reference]] = []
matches = self.find_obj(node.get("qapi:module"), target, None)
matches = self.find_obj(
node.get("qapi:namespace"), node.get("qapi:module"), target, None
)
for name, obj in matches:
rolename = self.role_for_objtype(obj.objtype)
assert rolename is not None
@ -921,6 +1036,12 @@ def setup(app: Sphinx) -> Dict[str, Any]:
"env", # Setting impacts parsing phase
types=set,
)
app.add_config_value(
"qapi_namespaces",
set(),
"env",
types=set,
)
app.add_domain(QAPIDomain)
return {

View File

@ -451,6 +451,12 @@ class Transmogrifier:
finally:
self._curr_ent = None
def set_namespace(self, namespace: str, source: str, lineno: int) -> None:
self.add_line_raw(
f".. qapi:namespace:: {namespace}", source, lineno + 1
)
self.ensure_blank_line()
class QAPISchemaGenDepVisitor(QAPISchemaVisitor):
"""A QAPI schema visitor which adds Sphinx dependencies each module
@ -496,6 +502,7 @@ class QAPIDocDirective(NestedDirective):
optional_arguments = 1
option_spec = {
"qapifile": directives.unchanged_required,
"namespace": directives.unchanged,
"transmogrify": directives.flag,
}
has_content = False
@ -510,6 +517,11 @@ class QAPIDocDirective(NestedDirective):
vis = Transmogrifier()
modules = set()
if "namespace" in self.options:
vis.set_namespace(
self.options["namespace"], *self.get_source_info()
)
for doc in schema.docs:
module_source = doc.info.fname
if module_source not in modules:

View File

@ -5913,35 +5913,31 @@
##
# @x-blockdev-change:
#
# Dynamically reconfigure the block driver state graph. It can be
# used to add, remove, insert or replace a graph node. Currently only
# the Quorum driver implements this feature to add or remove its
# child. This is useful to fix a broken quorum child.
# Dynamically reconfigure the block driver state graph.
#
# If @node is specified, it will be inserted under @parent. @child
# may not be specified in this case. If both @parent and @child are
# specified but @node is not, @child will be detached from @parent.
# Currently only supports adding and deleting quorum children. A
# child will be added at the end of the list of children. Its
# contents *must* be consistent with the other childrens' contents.
# Deleting a child that is not last in the list of children is
# problematic, because it "renumbers" the children following it.
#
# @parent: the id or name of the parent node.
#
# @child: the name of a child under the given parent node.
# @child: the name of a child to be deleted. Mutually exclusive with
# @node.
#
# @node: the name of the node that will be added.
# @node: the name of the node to be added. Mutually exclusive with
# @child.
#
# Features:
#
# @unstable: This command is experimental, and its API is not stable.
# It does not support all kinds of operations, all kinds of
# children, nor all block drivers.
# @unstable: This command is experimental.
#
# FIXME Removing children from a quorum node means introducing
# TODO: Removing children from a quorum node means introducing
# gaps in the child indices. This cannot be represented in the
# 'children' list of BlockdevOptionsQuorum, as returned by
# .bdrv_refresh_filename().
#
# Warning: The data in a new quorum child MUST be consistent with
# that of the rest of the array.
#
# Since: 2.7
#
# .. qmp-example::

View File

@ -5,7 +5,7 @@
#
# This document describes all commands currently supported by QMP.
#
# For locating a particular item, please see the `qapi-index`.
# For locating a particular item, please see the `qapi-qmp-index`.
#
# Most of the time their usage is exactly the same as in the user
# Monitor, this means that any other document which also describe

View File

@ -3,6 +3,9 @@
##
# = QEMU guest agent protocol commands and structs
#
# For a concise listing of all commands, events, and types in the QEMU
# guest agent, please consult the `qapi-qga-index`.
##
{ 'pragma': { 'doc-required': true } }

View File

@ -13,6 +13,14 @@
# the array type in the main schema, even if it is unused outside of the
# storage daemon.
##
# = QEMU storage daemon protocol commands and structs
#
# For a concise listing of all commands, events, and types in the QEMU
# storage daemon, please consult the `qapi-qsd-index`.
##
{ 'include': '../../qapi/pragma.json' }
# Documentation generated with qapi-gen.py is in source order, with