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:
commit
569346ad0a
@ -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".
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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")
|
||||||
|
@ -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]
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user