QAPI patches patches for 2023-02-23

-----BEGIN PGP SIGNATURE-----
 
 iQJGBAABCAAwFiEENUvIs9frKmtoZ05fOHC0AOuRhlMFAmP3VikSHGFybWJydUBy
 ZWRoYXQuY29tAAoJEDhwtADrkYZTf98P/1tf2tPnWL0QpGXqGOq/iy2cFhcoco06
 30t4JzTZGMZv8aUBRzlnhNgp+C7uMnXxuO7DeVN/K8VCvRfGXYz1HYFJ0NWhhMz6
 RULvVncJ7m9vFykmu3iibxvjyH6uj0R5xJ8ZNIrySLTCu+58voDF/IbZ0ep3v5nX
 1AV1ljL9taxg2SrQ53Whbet9zfgXVFnV5wLKkLOqLGvviO2OBPG7rrtQaEX2jrsa
 SdTiOdBk1IMvG3FT6cVx3bM3kQd15UwfcJsdIYpB7QBZNoqgiyfMPsNr8HzpZlJn
 KOe3qWVFWHGMWY4MtQ1j9Ph44RPrJybvPQRMDNB3CiDYEtBWsth0fZxhw9T/tKca
 5KgJaxecB3UsXFUBWhmvhkw+hwG+cDWHtiYZSb9AX4cqvPid1UdLnSQFWgHFGX+2
 ok0Q7gy9jYEpteVbIM8kQG0TF7xnZlv99uDK8b4MAH33roXwy70vffxpRGnngNyH
 IcLvzmDqRlrlzdvUi8Uro22VmUAUqSQKxKYt9yBJcEUV9NLi8E6g+Hcrvt7YNF9V
 jcVub4aIawEZCvnPCpOgzHD9p26ofwb2WQ245/5kzMUVi2pBYsHH6hJj7WdMPixS
 r24Ykgo4sxujW4pVy45lXzpA8uKWELCp9iKUOO6hvdoJEybVDMj9zcVn70cJgDrE
 RUle5av0n8XR
 =XV0D
 -----END PGP SIGNATURE-----

Merge tag 'pull-qapi-2023-02-23' of https://repo.or.cz/qemu/armbru into staging

QAPI patches patches for 2023-02-23

# -----BEGIN PGP SIGNATURE-----
#
# iQJGBAABCAAwFiEENUvIs9frKmtoZ05fOHC0AOuRhlMFAmP3VikSHGFybWJydUBy
# ZWRoYXQuY29tAAoJEDhwtADrkYZTf98P/1tf2tPnWL0QpGXqGOq/iy2cFhcoco06
# 30t4JzTZGMZv8aUBRzlnhNgp+C7uMnXxuO7DeVN/K8VCvRfGXYz1HYFJ0NWhhMz6
# RULvVncJ7m9vFykmu3iibxvjyH6uj0R5xJ8ZNIrySLTCu+58voDF/IbZ0ep3v5nX
# 1AV1ljL9taxg2SrQ53Whbet9zfgXVFnV5wLKkLOqLGvviO2OBPG7rrtQaEX2jrsa
# SdTiOdBk1IMvG3FT6cVx3bM3kQd15UwfcJsdIYpB7QBZNoqgiyfMPsNr8HzpZlJn
# KOe3qWVFWHGMWY4MtQ1j9Ph44RPrJybvPQRMDNB3CiDYEtBWsth0fZxhw9T/tKca
# 5KgJaxecB3UsXFUBWhmvhkw+hwG+cDWHtiYZSb9AX4cqvPid1UdLnSQFWgHFGX+2
# ok0Q7gy9jYEpteVbIM8kQG0TF7xnZlv99uDK8b4MAH33roXwy70vffxpRGnngNyH
# IcLvzmDqRlrlzdvUi8Uro22VmUAUqSQKxKYt9yBJcEUV9NLi8E6g+Hcrvt7YNF9V
# jcVub4aIawEZCvnPCpOgzHD9p26ofwb2WQ245/5kzMUVi2pBYsHH6hJj7WdMPixS
# r24Ykgo4sxujW4pVy45lXzpA8uKWELCp9iKUOO6hvdoJEybVDMj9zcVn70cJgDrE
# RUle5av0n8XR
# =XV0D
# -----END PGP SIGNATURE-----
# gpg: Signature made Thu 23 Feb 2023 12:03:53 GMT
# 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-2023-02-23' of https://repo.or.cz/qemu/armbru:
  qapi: remove JSON value FIXME
  qapi: remove _JSONObject
  qapi/parser: add QAPIExpression type
  qapi: Add minor typing workaround for 3.6
  qapi: update pylint configuration
  qapi: Update flake8 config
  docs/devel/qapi-code-gen: Fix a missing 'may', clarify SchemaInfo
  docs/devel/qapi-code-gen: Belatedly update features documentation

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2023-02-24 12:47:33 +00:00
commit 569346ad0a
6 changed files with 111 additions and 122 deletions

View File

@ -685,9 +685,10 @@ change in the QMP syntax (usually by allowing values or operations
that previously resulted in an error). QMP clients may still need to that previously resulted in an error). QMP clients may still need to
know whether the extension is available. know whether the extension is available.
For this purpose, a list of features can be specified for a command or For this purpose, a list of features can be specified for definitions,
struct type. Each list member can either be ``{ 'name': STRING, '*if': enumeration values, and struct members. Each feature list member can
COND }``, or STRING, which is shorthand for ``{ 'name': STRING }``. either be ``{ 'name': STRING, '*if': COND }``, or STRING, which is
shorthand for ``{ 'name': STRING }``.
The optional 'if' member specifies a conditional. See `Configuring The optional 'if' member specifies a conditional. See `Configuring
the schema`_ below for more on this. the schema`_ below for more on this.
@ -817,8 +818,8 @@ member 'bar' ::
A union's discriminator may not be conditional. A union's discriminator may not be conditional.
Likewise, individual enumeration values be conditional. This requires Likewise, individual enumeration values may be conditional. This
the longhand form of ENUM-VALUE_. requires the longhand form of ENUM-VALUE_.
Example: an enum type with unconditional value 'foo' and conditional Example: an enum type with unconditional value 'foo' and conditional
value 'bar' :: value 'bar' ::
@ -1157,9 +1158,8 @@ Example: the SchemaInfo for EVENT_C from section Events_ ::
Type "q_obj-EVENT_C-arg" is an implicitly defined object type with Type "q_obj-EVENT_C-arg" is an implicitly defined object type with
the two members from the event's definition. the two members from the event's definition.
The SchemaInfo for struct and union types has meta-type "object". The SchemaInfo for struct and union types has meta-type "object" and
variant member "members".
The SchemaInfo for a struct type has variant member "members".
The SchemaInfo for a union type additionally has variant members "tag" The SchemaInfo for a union type additionally has variant members "tag"
and "variants". and "variants".

View File

@ -1,2 +1,3 @@
[flake8] [flake8]
extend-ignore = E722 # Prefer pylint's bare-except checks to flake8's # Prefer pylint's bare-except checks to flake8's
extend-ignore = E722

View File

@ -33,7 +33,6 @@ structures and contextual semantic validation.
import re import re
from typing import ( from typing import (
Collection,
Dict, Dict,
Iterable, Iterable,
List, List,
@ -44,18 +43,10 @@ from typing import (
from .common import c_name from .common import c_name
from .error import QAPISemError from .error import QAPISemError
from .parser import QAPIDoc from .parser import QAPIExpression
from .source import QAPISourceInfo from .source import QAPISourceInfo
# Deserialized JSON objects as returned by the parser.
# The values of this mapping are not necessary to exhaustively type
# here (and also not practical as long as mypy lacks recursive
# types), because the purpose of this module is to interrogate that
# type.
_JSONObject = Dict[str, object]
# See check_name_str(), below. # See check_name_str(), below.
valid_name = re.compile(r'(__[a-z0-9.-]+_)?' valid_name = re.compile(r'(__[a-z0-9.-]+_)?'
r'(x-)?' r'(x-)?'
@ -192,11 +183,11 @@ def check_defn_name_str(name: str, info: QAPISourceInfo, meta: str) -> None:
info, "%s name should not end in 'List'" % meta) info, "%s name should not end in 'List'" % meta)
def check_keys(value: _JSONObject, def check_keys(value: Dict[str, object],
info: QAPISourceInfo, info: QAPISourceInfo,
source: str, source: str,
required: Collection[str], required: List[str],
optional: Collection[str]) -> None: optional: List[str]) -> None:
""" """
Ensure that a dict has a specific set of keys. Ensure that a dict has a specific set of keys.
@ -229,12 +220,11 @@ def check_keys(value: _JSONObject,
pprint(unknown), pprint(allowed))) pprint(unknown), pprint(allowed)))
def check_flags(expr: _JSONObject, info: QAPISourceInfo) -> None: def check_flags(expr: QAPIExpression) -> None:
""" """
Ensure flag members (if present) have valid values. Ensure flag members (if present) have valid values.
:param expr: The expression to validate. :param expr: The expression to validate.
:param info: QAPI schema source file information.
:raise QAPISemError: :raise QAPISemError:
When certain flags have an invalid value, or when When certain flags have an invalid value, or when
@ -243,21 +233,22 @@ def check_flags(expr: _JSONObject, info: QAPISourceInfo) -> None:
for key in ('gen', 'success-response'): for key in ('gen', 'success-response'):
if key in expr and expr[key] is not False: if key in expr and expr[key] is not False:
raise QAPISemError( raise QAPISemError(
info, "flag '%s' may only use false value" % key) expr.info, "flag '%s' may only use false value" % key)
for key in ('boxed', 'allow-oob', 'allow-preconfig', 'coroutine'): for key in ('boxed', 'allow-oob', 'allow-preconfig', 'coroutine'):
if key in expr and expr[key] is not True: if key in expr and expr[key] is not True:
raise QAPISemError( raise QAPISemError(
info, "flag '%s' may only use true value" % key) expr.info, "flag '%s' may only use true value" % key)
if 'allow-oob' in expr and 'coroutine' in expr: if 'allow-oob' in expr and 'coroutine' in expr:
# This is not necessarily a fundamental incompatibility, but # This is not necessarily a fundamental incompatibility, but
# we don't have a use case and the desired semantics isn't # we don't have a use case and the desired semantics isn't
# obvious. The simplest solution is to forbid it until we get # obvious. The simplest solution is to forbid it until we get
# a use case for it. # a use case for it.
raise QAPISemError(info, "flags 'allow-oob' and 'coroutine' " raise QAPISemError(
"are incompatible") expr.info, "flags 'allow-oob' and 'coroutine' are incompatible")
def check_if(expr: _JSONObject, info: QAPISourceInfo, source: str) -> None: def check_if(expr: Dict[str, object],
info: QAPISourceInfo, source: str) -> None:
""" """
Validate the ``if`` member of an object. Validate the ``if`` member of an object.
@ -447,12 +438,11 @@ def check_features(features: Optional[object],
check_if(feat, info, source) check_if(feat, info, source)
def check_enum(expr: _JSONObject, info: QAPISourceInfo) -> None: def check_enum(expr: QAPIExpression) -> None:
""" """
Normalize and validate this expression as an ``enum`` definition. Normalize and validate this expression as an ``enum`` definition.
:param expr: The expression to validate. :param expr: The expression to validate.
:param info: QAPI schema source file information.
:raise QAPISemError: When ``expr`` is not a valid ``enum``. :raise QAPISemError: When ``expr`` is not a valid ``enum``.
:return: None, ``expr`` is normalized in-place as needed. :return: None, ``expr`` is normalized in-place as needed.
@ -460,6 +450,7 @@ def check_enum(expr: _JSONObject, info: QAPISourceInfo) -> None:
name = expr['enum'] name = expr['enum']
members = expr['data'] members = expr['data']
prefix = expr.get('prefix') prefix = expr.get('prefix')
info = expr.info
if not isinstance(members, list): if not isinstance(members, list):
raise QAPISemError(info, "'data' must be an array") raise QAPISemError(info, "'data' must be an array")
@ -486,12 +477,11 @@ def check_enum(expr: _JSONObject, info: QAPISourceInfo) -> None:
check_features(member.get('features'), info) check_features(member.get('features'), info)
def check_struct(expr: _JSONObject, info: QAPISourceInfo) -> None: def check_struct(expr: QAPIExpression) -> None:
""" """
Normalize and validate this expression as a ``struct`` definition. Normalize and validate this expression as a ``struct`` definition.
:param expr: The expression to validate. :param expr: The expression to validate.
:param info: QAPI schema source file information.
:raise QAPISemError: When ``expr`` is not a valid ``struct``. :raise QAPISemError: When ``expr`` is not a valid ``struct``.
:return: None, ``expr`` is normalized in-place as needed. :return: None, ``expr`` is normalized in-place as needed.
@ -499,16 +489,15 @@ def check_struct(expr: _JSONObject, info: QAPISourceInfo) -> None:
name = cast(str, expr['struct']) # Checked in check_exprs name = cast(str, expr['struct']) # Checked in check_exprs
members = expr['data'] members = expr['data']
check_type(members, info, "'data'", allow_dict=name) check_type(members, expr.info, "'data'", allow_dict=name)
check_type(expr.get('base'), info, "'base'") check_type(expr.get('base'), expr.info, "'base'")
def check_union(expr: _JSONObject, info: QAPISourceInfo) -> None: def check_union(expr: QAPIExpression) -> None:
""" """
Normalize and validate this expression as a ``union`` definition. Normalize and validate this expression as a ``union`` definition.
:param expr: The expression to validate. :param expr: The expression to validate.
:param info: QAPI schema source file information.
:raise QAPISemError: when ``expr`` is not a valid ``union``. :raise QAPISemError: when ``expr`` is not a valid ``union``.
:return: None, ``expr`` is normalized in-place as needed. :return: None, ``expr`` is normalized in-place as needed.
@ -517,6 +506,7 @@ def check_union(expr: _JSONObject, info: QAPISourceInfo) -> None:
base = expr['base'] base = expr['base']
discriminator = expr['discriminator'] discriminator = expr['discriminator']
members = expr['data'] members = expr['data']
info = expr.info
check_type(base, info, "'base'", allow_dict=name) check_type(base, info, "'base'", allow_dict=name)
check_name_is_str(discriminator, info, "'discriminator'") check_name_is_str(discriminator, info, "'discriminator'")
@ -531,17 +521,17 @@ def check_union(expr: _JSONObject, info: QAPISourceInfo) -> None:
check_type(value['type'], info, source, allow_array=not base) check_type(value['type'], info, source, allow_array=not base)
def check_alternate(expr: _JSONObject, info: QAPISourceInfo) -> None: def check_alternate(expr: QAPIExpression) -> None:
""" """
Normalize and validate this expression as an ``alternate`` definition. Normalize and validate this expression as an ``alternate`` definition.
:param expr: The expression to validate. :param expr: The expression to validate.
:param info: QAPI schema source file information.
:raise QAPISemError: When ``expr`` is not a valid ``alternate``. :raise QAPISemError: When ``expr`` is not a valid ``alternate``.
:return: None, ``expr`` is normalized in-place as needed. :return: None, ``expr`` is normalized in-place as needed.
""" """
members = expr['data'] members = expr['data']
info = expr.info
if not members: if not members:
raise QAPISemError(info, "'data' must not be empty") raise QAPISemError(info, "'data' must not be empty")
@ -557,12 +547,11 @@ def check_alternate(expr: _JSONObject, info: QAPISourceInfo) -> None:
check_type(value['type'], info, source, allow_array=True) check_type(value['type'], info, source, allow_array=True)
def check_command(expr: _JSONObject, info: QAPISourceInfo) -> None: def check_command(expr: QAPIExpression) -> None:
""" """
Normalize and validate this expression as a ``command`` definition. Normalize and validate this expression as a ``command`` definition.
:param expr: The expression to validate. :param expr: The expression to validate.
:param info: QAPI schema source file information.
:raise QAPISemError: When ``expr`` is not a valid ``command``. :raise QAPISemError: When ``expr`` is not a valid ``command``.
:return: None, ``expr`` is normalized in-place as needed. :return: None, ``expr`` is normalized in-place as needed.
@ -572,17 +561,16 @@ def check_command(expr: _JSONObject, info: QAPISourceInfo) -> None:
boxed = expr.get('boxed', False) boxed = expr.get('boxed', False)
if boxed and args is None: if boxed and args is None:
raise QAPISemError(info, "'boxed': true requires 'data'") raise QAPISemError(expr.info, "'boxed': true requires 'data'")
check_type(args, info, "'data'", allow_dict=not boxed) check_type(args, expr.info, "'data'", allow_dict=not boxed)
check_type(rets, info, "'returns'", allow_array=True) check_type(rets, expr.info, "'returns'", allow_array=True)
def check_event(expr: _JSONObject, info: QAPISourceInfo) -> None: def check_event(expr: QAPIExpression) -> None:
""" """
Normalize and validate this expression as an ``event`` definition. Normalize and validate this expression as an ``event`` definition.
:param expr: The expression to validate. :param expr: The expression to validate.
:param info: QAPI schema source file information.
:raise QAPISemError: When ``expr`` is not a valid ``event``. :raise QAPISemError: When ``expr`` is not a valid ``event``.
:return: None, ``expr`` is normalized in-place as needed. :return: None, ``expr`` is normalized in-place as needed.
@ -591,11 +579,11 @@ def check_event(expr: _JSONObject, info: QAPISourceInfo) -> None:
boxed = expr.get('boxed', False) boxed = expr.get('boxed', False)
if boxed and args is None: if boxed and args is None:
raise QAPISemError(info, "'boxed': true requires 'data'") raise QAPISemError(expr.info, "'boxed': true requires 'data'")
check_type(args, info, "'data'", allow_dict=not boxed) check_type(args, expr.info, "'data'", allow_dict=not boxed)
def check_exprs(exprs: List[_JSONObject]) -> List[_JSONObject]: def check_exprs(exprs: List[QAPIExpression]) -> List[QAPIExpression]:
""" """
Validate and normalize a list of parsed QAPI schema expressions. Validate and normalize a list of parsed QAPI schema expressions.
@ -607,21 +595,9 @@ def check_exprs(exprs: List[_JSONObject]) -> List[_JSONObject]:
:raise QAPISemError: When any expression fails validation. :raise QAPISemError: When any expression fails validation.
:return: The same list of expressions (now modified). :return: The same list of expressions (now modified).
""" """
for expr_elem in exprs: for expr in exprs:
# Expression info = expr.info
assert isinstance(expr_elem['expr'], dict) doc = expr.doc
for key in expr_elem['expr'].keys():
assert isinstance(key, str)
expr: _JSONObject = expr_elem['expr']
# QAPISourceInfo
assert isinstance(expr_elem['info'], QAPISourceInfo)
info: QAPISourceInfo = expr_elem['info']
# Optional[QAPIDoc]
tmp = expr_elem.get('doc')
assert tmp is None or isinstance(tmp, QAPIDoc)
doc: Optional[QAPIDoc] = tmp
if 'include' in expr: if 'include' in expr:
continue continue
@ -653,24 +629,24 @@ def check_exprs(exprs: List[_JSONObject]) -> List[_JSONObject]:
if meta == 'enum': if meta == 'enum':
check_keys(expr, info, meta, check_keys(expr, info, meta,
['enum', 'data'], ['if', 'features', 'prefix']) ['enum', 'data'], ['if', 'features', 'prefix'])
check_enum(expr, info) check_enum(expr)
elif meta == 'union': elif meta == 'union':
check_keys(expr, info, meta, check_keys(expr, info, meta,
['union', 'base', 'discriminator', 'data'], ['union', 'base', 'discriminator', 'data'],
['if', 'features']) ['if', 'features'])
normalize_members(expr.get('base')) normalize_members(expr.get('base'))
normalize_members(expr['data']) normalize_members(expr['data'])
check_union(expr, info) check_union(expr)
elif meta == 'alternate': elif meta == 'alternate':
check_keys(expr, info, meta, check_keys(expr, info, meta,
['alternate', 'data'], ['if', 'features']) ['alternate', 'data'], ['if', 'features'])
normalize_members(expr['data']) normalize_members(expr['data'])
check_alternate(expr, info) check_alternate(expr)
elif meta == 'struct': elif meta == 'struct':
check_keys(expr, info, meta, check_keys(expr, info, meta,
['struct', 'data'], ['base', 'if', 'features']) ['struct', 'data'], ['base', 'if', 'features'])
normalize_members(expr['data']) normalize_members(expr['data'])
check_struct(expr, info) check_struct(expr)
elif meta == 'command': elif meta == 'command':
check_keys(expr, info, meta, check_keys(expr, info, meta,
['command'], ['command'],
@ -678,17 +654,17 @@ def check_exprs(exprs: List[_JSONObject]) -> List[_JSONObject]:
'gen', 'success-response', 'allow-oob', 'gen', 'success-response', 'allow-oob',
'allow-preconfig', 'coroutine']) 'allow-preconfig', 'coroutine'])
normalize_members(expr.get('data')) normalize_members(expr.get('data'))
check_command(expr, info) check_command(expr)
elif meta == 'event': elif meta == 'event':
check_keys(expr, info, meta, check_keys(expr, info, meta,
['event'], ['data', 'boxed', 'if', 'features']) ['event'], ['data', 'boxed', 'if', 'features'])
normalize_members(expr.get('data')) normalize_members(expr.get('data'))
check_event(expr, info) check_event(expr)
else: else:
assert False, 'unexpected meta type' assert False, 'unexpected meta type'
check_if(expr, info, meta) check_if(expr, info, meta)
check_features(expr.get('features'), info) check_features(expr.get('features'), info)
check_flags(expr, info) check_flags(expr)
return exprs return exprs

View File

@ -21,6 +21,7 @@ from typing import (
TYPE_CHECKING, TYPE_CHECKING,
Dict, Dict,
List, List,
Mapping,
Optional, Optional,
Set, Set,
Union, Union,
@ -37,15 +38,19 @@ if TYPE_CHECKING:
from .schema import QAPISchemaFeature, QAPISchemaMember from .schema import QAPISchemaFeature, QAPISchemaMember
#: Represents a single Top Level QAPI schema expression.
TopLevelExpr = Dict[str, object]
# Return value alias for get_expr(). # Return value alias for get_expr().
_ExprValue = Union[List[object], Dict[str, object], str, bool] _ExprValue = Union[List[object], Dict[str, object], str, bool]
# FIXME: Consolidate and centralize definitions for TopLevelExpr,
# _ExprValue, _JSONValue, and _JSONObject; currently scattered across class QAPIExpression(Dict[str, object]):
# several modules. # pylint: disable=too-few-public-methods
def __init__(self,
data: Mapping[str, object],
info: QAPISourceInfo,
doc: Optional['QAPIDoc'] = None):
super().__init__(data)
self.info = info
self.doc: Optional['QAPIDoc'] = doc
class QAPIParseError(QAPISourceError): class QAPIParseError(QAPISourceError):
@ -100,7 +105,7 @@ class QAPISchemaParser:
self.line_pos = 0 self.line_pos = 0
# Parser output: # Parser output:
self.exprs: List[Dict[str, object]] = [] self.exprs: List[QAPIExpression] = []
self.docs: List[QAPIDoc] = [] self.docs: List[QAPIDoc] = []
# Showtime! # Showtime!
@ -147,8 +152,7 @@ class QAPISchemaParser:
"value of 'include' must be a string") "value of 'include' must be a string")
incl_fname = os.path.join(os.path.dirname(self._fname), incl_fname = os.path.join(os.path.dirname(self._fname),
include) include)
self.exprs.append({'expr': {'include': incl_fname}, self._add_expr(OrderedDict({'include': incl_fname}), info)
'info': info})
exprs_include = self._include(include, info, incl_fname, exprs_include = self._include(include, info, incl_fname,
self._included) self._included)
if exprs_include: if exprs_include:
@ -165,17 +169,18 @@ class QAPISchemaParser:
for name, value in pragma.items(): for name, value in pragma.items():
self._pragma(name, value, info) self._pragma(name, value, info)
else: else:
expr_elem = {'expr': expr, if cur_doc and not cur_doc.symbol:
'info': info} raise QAPISemError(
if cur_doc: cur_doc.info, "definition documentation required")
if not cur_doc.symbol: self._add_expr(expr, info, cur_doc)
raise QAPISemError(
cur_doc.info, "definition documentation required")
expr_elem['doc'] = cur_doc
self.exprs.append(expr_elem)
cur_doc = None cur_doc = None
self.reject_expr_doc(cur_doc) self.reject_expr_doc(cur_doc)
def _add_expr(self, expr: Mapping[str, object],
info: QAPISourceInfo,
doc: Optional['QAPIDoc'] = None) -> None:
self.exprs.append(QAPIExpression(expr, info, doc))
@staticmethod @staticmethod
def reject_expr_doc(doc: Optional['QAPIDoc']) -> None: def reject_expr_doc(doc: Optional['QAPIDoc']) -> None:
if doc and doc.symbol: if doc and doc.symbol:
@ -784,7 +789,7 @@ class QAPIDoc:
% feature.name) % feature.name)
self.features[feature.name].connect(feature) self.features[feature.name].connect(feature)
def check_expr(self, expr: TopLevelExpr) -> None: def check_expr(self, expr: QAPIExpression) -> None:
if self.has_section('Returns') and 'command' not in expr: if self.has_section('Returns') and 'command' not in expr:
raise QAPISemError(self.info, raise QAPISemError(self.info,
"'Returns:' is only valid for commands") "'Returns:' is only valid for commands")

View File

@ -23,6 +23,7 @@ disable=fixme,
too-many-statements, too-many-statements,
too-many-instance-attributes, too-many-instance-attributes,
consider-using-f-string, consider-using-f-string,
useless-option-value,
[REPORTS] [REPORTS]

View File

@ -17,7 +17,7 @@
from collections import OrderedDict from collections import OrderedDict
import os import os
import re import re
from typing import Optional from typing import List, Optional
from .common import ( from .common import (
POINTER_SUFFIX, POINTER_SUFFIX,
@ -29,7 +29,7 @@ from .common import (
) )
from .error import QAPIError, QAPISemError, QAPISourceError from .error import QAPIError, QAPISemError, QAPISourceError
from .expr import check_exprs from .expr import check_exprs
from .parser import QAPISchemaParser from .parser import QAPIExpression, QAPISchemaParser
class QAPISchemaIfCond: class QAPISchemaIfCond:
@ -964,10 +964,11 @@ class QAPISchema:
name = self._module_name(fname) name = self._module_name(fname)
return self._module_dict[name] return self._module_dict[name]
def _def_include(self, expr, info, doc): def _def_include(self, expr: QAPIExpression):
include = expr['include'] include = expr['include']
assert doc is None assert expr.doc is None
self._def_entity(QAPISchemaInclude(self._make_module(include), info)) self._def_entity(
QAPISchemaInclude(self._make_module(include), expr.info))
def _def_builtin_type(self, name, json_type, c_type): def _def_builtin_type(self, name, json_type, c_type):
self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type)) self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
@ -1045,14 +1046,15 @@ class QAPISchema:
name, info, None, ifcond, None, None, members, None)) name, info, None, ifcond, None, None, members, None))
return name return name
def _def_enum_type(self, expr, info, doc): def _def_enum_type(self, expr: QAPIExpression):
name = expr['enum'] name = expr['enum']
data = expr['data'] data = expr['data']
prefix = expr.get('prefix') prefix = expr.get('prefix')
ifcond = QAPISchemaIfCond(expr.get('if')) ifcond = QAPISchemaIfCond(expr.get('if'))
info = expr.info
features = self._make_features(expr.get('features'), info) features = self._make_features(expr.get('features'), info)
self._def_entity(QAPISchemaEnumType( self._def_entity(QAPISchemaEnumType(
name, info, doc, ifcond, features, name, info, expr.doc, ifcond, features,
self._make_enum_members(data, info), prefix)) self._make_enum_members(data, info), prefix))
def _make_member(self, name, typ, ifcond, features, info): def _make_member(self, name, typ, ifcond, features, info):
@ -1072,14 +1074,15 @@ class QAPISchema:
value.get('features'), info) value.get('features'), info)
for (key, value) in data.items()] for (key, value) in data.items()]
def _def_struct_type(self, expr, info, doc): def _def_struct_type(self, expr: QAPIExpression):
name = expr['struct'] name = expr['struct']
base = expr.get('base') base = expr.get('base')
data = expr['data'] data = expr['data']
info = expr.info
ifcond = QAPISchemaIfCond(expr.get('if')) ifcond = QAPISchemaIfCond(expr.get('if'))
features = self._make_features(expr.get('features'), info) features = self._make_features(expr.get('features'), info)
self._def_entity(QAPISchemaObjectType( self._def_entity(QAPISchemaObjectType(
name, info, doc, ifcond, features, base, name, info, expr.doc, ifcond, features, base,
self._make_members(data, info), self._make_members(data, info),
None)) None))
@ -1089,11 +1092,13 @@ class QAPISchema:
typ = self._make_array_type(typ[0], info) typ = self._make_array_type(typ[0], info)
return QAPISchemaVariant(case, info, typ, ifcond) return QAPISchemaVariant(case, info, typ, ifcond)
def _def_union_type(self, expr, info, doc): def _def_union_type(self, expr: QAPIExpression):
name = expr['union'] name = expr['union']
base = expr['base'] base = expr['base']
tag_name = expr['discriminator'] tag_name = expr['discriminator']
data = expr['data'] data = expr['data']
assert isinstance(data, dict)
info = expr.info
ifcond = QAPISchemaIfCond(expr.get('if')) ifcond = QAPISchemaIfCond(expr.get('if'))
features = self._make_features(expr.get('features'), info) features = self._make_features(expr.get('features'), info)
if isinstance(base, dict): if isinstance(base, dict):
@ -1105,17 +1110,19 @@ class QAPISchema:
QAPISchemaIfCond(value.get('if')), QAPISchemaIfCond(value.get('if')),
info) info)
for (key, value) in data.items()] for (key, value) in data.items()]
members = [] members: List[QAPISchemaObjectTypeMember] = []
self._def_entity( self._def_entity(
QAPISchemaObjectType(name, info, doc, ifcond, features, QAPISchemaObjectType(name, info, expr.doc, ifcond, features,
base, members, base, members,
QAPISchemaVariants( QAPISchemaVariants(
tag_name, info, None, variants))) tag_name, info, None, variants)))
def _def_alternate_type(self, expr, info, doc): def _def_alternate_type(self, expr: QAPIExpression):
name = expr['alternate'] name = expr['alternate']
data = expr['data'] data = expr['data']
assert isinstance(data, dict)
ifcond = QAPISchemaIfCond(expr.get('if')) ifcond = QAPISchemaIfCond(expr.get('if'))
info = expr.info
features = self._make_features(expr.get('features'), info) features = self._make_features(expr.get('features'), info)
variants = [ variants = [
self._make_variant(key, value['type'], self._make_variant(key, value['type'],
@ -1124,11 +1131,11 @@ class QAPISchema:
for (key, value) in data.items()] for (key, value) in data.items()]
tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False) tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False)
self._def_entity( self._def_entity(
QAPISchemaAlternateType(name, info, doc, ifcond, features, QAPISchemaAlternateType(
QAPISchemaVariants( name, info, expr.doc, ifcond, features,
None, info, tag_member, variants))) QAPISchemaVariants(None, info, tag_member, variants)))
def _def_command(self, expr, info, doc): def _def_command(self, expr: QAPIExpression):
name = expr['command'] name = expr['command']
data = expr.get('data') data = expr.get('data')
rets = expr.get('returns') rets = expr.get('returns')
@ -1139,6 +1146,7 @@ class QAPISchema:
allow_preconfig = expr.get('allow-preconfig', False) allow_preconfig = expr.get('allow-preconfig', False)
coroutine = expr.get('coroutine', False) coroutine = expr.get('coroutine', False)
ifcond = QAPISchemaIfCond(expr.get('if')) ifcond = QAPISchemaIfCond(expr.get('if'))
info = expr.info
features = self._make_features(expr.get('features'), info) features = self._make_features(expr.get('features'), info)
if isinstance(data, OrderedDict): if isinstance(data, OrderedDict):
data = self._make_implicit_object_type( data = self._make_implicit_object_type(
@ -1147,44 +1155,42 @@ class QAPISchema:
if isinstance(rets, list): if isinstance(rets, list):
assert len(rets) == 1 assert len(rets) == 1
rets = self._make_array_type(rets[0], info) rets = self._make_array_type(rets[0], info)
self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, features, self._def_entity(QAPISchemaCommand(name, info, expr.doc, ifcond,
data, rets, features, data, rets,
gen, success_response, gen, success_response,
boxed, allow_oob, allow_preconfig, boxed, allow_oob, allow_preconfig,
coroutine)) coroutine))
def _def_event(self, expr, info, doc): def _def_event(self, expr: QAPIExpression):
name = expr['event'] name = expr['event']
data = expr.get('data') data = expr.get('data')
boxed = expr.get('boxed', False) boxed = expr.get('boxed', False)
ifcond = QAPISchemaIfCond(expr.get('if')) ifcond = QAPISchemaIfCond(expr.get('if'))
info = expr.info
features = self._make_features(expr.get('features'), info) features = self._make_features(expr.get('features'), info)
if isinstance(data, OrderedDict): if isinstance(data, OrderedDict):
data = self._make_implicit_object_type( data = self._make_implicit_object_type(
name, info, ifcond, name, info, ifcond,
'arg', self._make_members(data, info)) 'arg', self._make_members(data, info))
self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, features, self._def_entity(QAPISchemaEvent(name, info, expr.doc, ifcond,
data, boxed)) features, data, boxed))
def _def_exprs(self, exprs): def _def_exprs(self, exprs):
for expr_elem in exprs: for expr in exprs:
expr = expr_elem['expr']
info = expr_elem['info']
doc = expr_elem.get('doc')
if 'enum' in expr: if 'enum' in expr:
self._def_enum_type(expr, info, doc) self._def_enum_type(expr)
elif 'struct' in expr: elif 'struct' in expr:
self._def_struct_type(expr, info, doc) self._def_struct_type(expr)
elif 'union' in expr: elif 'union' in expr:
self._def_union_type(expr, info, doc) self._def_union_type(expr)
elif 'alternate' in expr: elif 'alternate' in expr:
self._def_alternate_type(expr, info, doc) self._def_alternate_type(expr)
elif 'command' in expr: elif 'command' in expr:
self._def_command(expr, info, doc) self._def_command(expr)
elif 'event' in expr: elif 'event' in expr:
self._def_event(expr, info, doc) self._def_event(expr)
elif 'include' in expr: elif 'include' in expr:
self._def_include(expr, info, doc) self._def_include(expr)
else: else:
assert False assert False