QAPI patches patches for 2021-09-03

-----BEGIN PGP SIGNATURE-----
 
 iQJGBAABCAAwFiEENUvIs9frKmtoZ05fOHC0AOuRhlMFAmEyPVESHGFybWJydUBy
 ZWRoYXQuY29tAAoJEDhwtADrkYZTu9UP+wawlxnMaxG3/3kkCT6g8i1poD+xTRSU
 Z64VK/aAtGy4K0Z/3KQpW2cNeO/bkhrMb8bwB3Nn/jfwqMav0CPVhLsrasXOu78u
 AM1nMVCGfcbqW5oINTdysWAI1z4cgsv8T8g3BOSUWAM4teKDx+Lkme/dSTOgAuE5
 6uixZZ53QUHuhY3K11mryQs/vWAWdzMBwy3Eawge6WEu48DnRH2C2wax+vb79LVa
 P/s7Bo4HKkJcPXfgyyzugd5NQCQ8uo6FIXEt2VHEThQIwHIHZC7lTn+VyzJAVlIO
 OnuayM5/5YTPxTFOrZgHwHZdBcmDwPKzNxvpayZZfh9lLVcqFhLY7SM4WxDQw8hF
 nJ0DCbUYV/JKrjv5ly0s1r7RfeXtAFBWLXWh4xlvuj2p6TdfXb6pjuxLlLdpLYfS
 qkeMteT21JEfE5U108d7AGDJ98HbQD+SXao90c8N6K1MsBh+jfifpgbcfcttOLoX
 +UItu6zOxZvDd/NO9m7JgE8o8Btd1uXK0PAwdK5V8xBf7TBgqk8FIQTPZo5SwYWI
 VUmtxc8f2NvsgLaHglXmouOXM5V2BgluW3iVmq/IIAcJo7qvFdPZnRG/BZ5bVuHG
 f6KQNRNUK3ef5rbMfMIusN7BNeCJr4yGXNr9WRhK5zP6I/AuP21kMrZ25Z+he2yV
 RhKNWF3BbSkv
 =rHX+
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2021-09-03' into staging

QAPI patches patches for 2021-09-03

# gpg: Signature made Fri 03 Sep 2021 16:20:49 BST
# 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

* remotes/armbru/tags/pull-qapi-2021-09-03:
  qapi: Tweak error messages for unknown / conflicting 'if' keys
  qapi: Tweak error messages for missing / conflicting meta-type
  tests/qapi-schema: Hide OrderedDict in test output
  qapi: Use re.fullmatch() where appropriate
  qapi: Use "not COND" instead of "!COND" for generated documentation
  qapi: Avoid redundant parens in code generated for conditionals
  qapi: Factor common recursion out of cgen_ifcond(), docgen_ifcond()
  qapi: Fix C code generation for 'if'
  tests/qapi-schema: Demonstrate broken C code for 'if'
  tests/qapi-schema: Correct two 'if' conditionals
  qapi: Simplify how QAPISchemaIfCond represents "no condition"
  qapi: Simplify QAPISchemaIfCond's interface for generating C
  qapi: Set boolean value correctly in examples

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2021-09-04 19:21:19 +01:00
commit 31ebff513f
19 changed files with 121 additions and 114 deletions

View File

@ -99,7 +99,7 @@
# Example:
#
# -> { "execute": "trace-event-set-state",
# "arguments": { "name": "qemu_memalign", "enable": "true" } }
# "arguments": { "name": "qemu_memalign", "enable": true } }
# <- { "return": {} }
#
##

View File

@ -17,6 +17,7 @@ from typing import (
Dict,
Match,
Optional,
Sequence,
Union,
)
@ -200,33 +201,39 @@ def guardend(name: str) -> str:
name=c_fname(name).upper())
def cgen_ifcond(ifcond: Union[str, Dict[str, Any]]) -> str:
def gen_ifcond(ifcond: Optional[Union[str, Dict[str, Any]]],
cond_fmt: str, not_fmt: str,
all_operator: str, any_operator: str) -> str:
def do_gen(ifcond: Union[str, Dict[str, Any]], need_parens: bool):
if isinstance(ifcond, str):
return cond_fmt % ifcond
assert isinstance(ifcond, dict) and len(ifcond) == 1
if 'not' in ifcond:
return not_fmt % do_gen(ifcond['not'], True)
if 'all' in ifcond:
gen = gen_infix(all_operator, ifcond['all'])
else:
gen = gen_infix(any_operator, ifcond['any'])
if need_parens:
gen = '(' + gen + ')'
return gen
def gen_infix(operator: str, operands: Sequence[Any]) -> str:
return operator.join([do_gen(o, True) for o in operands])
if not ifcond:
return ''
if isinstance(ifcond, str):
return 'defined(' + ifcond + ')'
oper, operands = next(iter(ifcond.items()))
if oper == 'not':
return '!' + cgen_ifcond(operands)
oper = {'all': '&&', 'any': '||'}[oper]
operands = [cgen_ifcond(o) for o in operands]
return '(' + (') ' + oper + ' (').join(operands) + ')'
return do_gen(ifcond, False)
def docgen_ifcond(ifcond: Union[str, Dict[str, Any]]) -> str:
def cgen_ifcond(ifcond: Optional[Union[str, Dict[str, Any]]]) -> str:
return gen_ifcond(ifcond, 'defined(%s)', '!%s', ' && ', ' || ')
def docgen_ifcond(ifcond: Optional[Union[str, Dict[str, Any]]]) -> str:
# TODO Doc generated for conditions needs polish
if not ifcond:
return ''
if isinstance(ifcond, str):
return ifcond
oper, operands = next(iter(ifcond.items()))
if oper == 'not':
return '!' + docgen_ifcond(operands)
oper = {'all': ' and ', 'any': ' or '}[oper]
operands = [docgen_ifcond(o) for o in operands]
return '(' + oper.join(operands) + ')'
return gen_ifcond(ifcond, '%s', 'not %s', ' and ', ' or ')
def gen_if(cond: str) -> str:

View File

@ -275,7 +275,7 @@ def check_if(expr: _JSONObject, info: QAPISourceInfo, source: str) -> None:
def _check_if(cond: Union[str, object]) -> None:
if isinstance(cond, str):
if not re.match(r'^[A-Z][A-Z0-9_]*$', cond):
if not re.fullmatch(r'[A-Z][A-Z0-9_]*', cond):
raise QAPISemError(
info,
"'if' condition '%s' of %s is not a valid identifier"
@ -286,13 +286,12 @@ def check_if(expr: _JSONObject, info: QAPISourceInfo, source: str) -> None:
raise QAPISemError(
info,
"'if' condition of %s must be a string or an object" % source)
check_keys(cond, info, "'if' condition of %s" % source, [],
["all", "any", "not"])
if len(cond) != 1:
raise QAPISemError(
info,
"'if' condition dict of %s must have one key: "
"'all', 'any' or 'not'" % source)
check_keys(cond, info, "'if' condition", [],
["all", "any", "not"])
"'if' condition of %s has conflicting keys" % source)
oper, operands = next(iter(cond.items()))
if not operands:
@ -630,20 +629,15 @@ def check_exprs(exprs: List[_JSONObject]) -> List[_JSONObject]:
if 'include' in expr:
continue
if 'enum' in expr:
meta = 'enum'
elif 'union' in expr:
meta = 'union'
elif 'alternate' in expr:
meta = 'alternate'
elif 'struct' in expr:
meta = 'struct'
elif 'command' in expr:
meta = 'command'
elif 'event' in expr:
meta = 'event'
else:
raise QAPISemError(info, "expression is missing metatype")
metas = expr.keys() & {'enum', 'struct', 'union', 'alternate',
'command', 'event'}
if len(metas) != 1:
raise QAPISemError(
info,
"expression must have exactly one key"
" 'enum', 'struct', 'union', 'alternate',"
" 'command', 'event'")
meta = metas.pop()
check_name_is_str(expr[meta], info, "'%s'" % meta)
name = cast(str, expr[meta])

View File

@ -24,8 +24,6 @@ from typing import (
from .common import (
c_fname,
c_name,
gen_endif,
gen_if,
guardend,
guardstart,
mcgen,
@ -95,9 +93,9 @@ def _wrap_ifcond(ifcond: QAPISchemaIfCond, before: str, after: str) -> str:
if added[0] == '\n':
out += '\n'
added = added[1:]
out += gen_if(ifcond.cgen())
out += ifcond.gen_if()
out += added
out += gen_endif(ifcond.cgen())
out += ifcond.gen_endif()
return out

View File

@ -22,12 +22,7 @@ from typing import (
Union,
)
from .common import (
c_name,
gen_endif,
gen_if,
mcgen,
)
from .common import c_name, mcgen
from .gen import QAPISchemaMonolithicCVisitor
from .schema import (
QAPISchema,
@ -124,10 +119,10 @@ def _tree_to_qlit(obj: JSONValue,
if obj.comment:
ret += indent(level) + f"/* {obj.comment} */\n"
if obj.ifcond.is_present():
ret += gen_if(obj.ifcond.cgen())
ret += obj.ifcond.gen_if()
ret += _tree_to_qlit(obj.value, level)
if obj.ifcond.is_present():
ret += '\n' + gen_endif(obj.ifcond.cgen())
ret += '\n' + obj.ifcond.gen_endif()
return ret
ret = ''

View File

@ -24,6 +24,8 @@ from .common import (
c_name,
cgen_ifcond,
docgen_ifcond,
gen_endif,
gen_if,
)
from .error import QAPIError, QAPISemError, QAPISourceError
from .expr import check_exprs
@ -32,11 +34,17 @@ from .parser import QAPISchemaParser
class QAPISchemaIfCond:
def __init__(self, ifcond=None):
self.ifcond = ifcond or {}
self.ifcond = ifcond
def cgen(self):
def _cgen(self):
return cgen_ifcond(self.ifcond)
def gen_if(self):
return gen_if(self._cgen())
def gen_endif(self):
return gen_endif(self._cgen())
def docgen(self):
return docgen_ifcond(self.ifcond)

View File

@ -15,13 +15,7 @@ This work is licensed under the terms of the GNU GPL, version 2.
from typing import List, Optional
from .common import (
c_enum_const,
c_name,
gen_endif,
gen_if,
mcgen,
)
from .common import c_enum_const, c_name, mcgen
from .gen import QAPISchemaModularCVisitor, ifcontext
from .schema import (
QAPISchema,
@ -51,13 +45,13 @@ const QEnumLookup %(c_name)s_lookup = {
''',
c_name=c_name(name))
for memb in members:
ret += gen_if(memb.ifcond.cgen())
ret += memb.ifcond.gen_if()
index = c_enum_const(name, memb.name, prefix)
ret += mcgen('''
[%(index)s] = "%(name)s",
''',
index=index, name=memb.name)
ret += gen_endif(memb.ifcond.cgen())
ret += memb.ifcond.gen_endif()
ret += mcgen('''
},
@ -81,12 +75,12 @@ typedef enum %(c_name)s {
c_name=c_name(name))
for memb in enum_members:
ret += gen_if(memb.ifcond.cgen())
ret += memb.ifcond.gen_if()
ret += mcgen('''
%(c_enum)s,
''',
c_enum=c_enum_const(name, memb.name, prefix))
ret += gen_endif(memb.ifcond.cgen())
ret += memb.ifcond.gen_endif()
ret += mcgen('''
} %(c_name)s;
@ -126,7 +120,7 @@ struct %(c_name)s {
def gen_struct_members(members: List[QAPISchemaObjectTypeMember]) -> str:
ret = ''
for memb in members:
ret += gen_if(memb.ifcond.cgen())
ret += memb.ifcond.gen_if()
if memb.optional:
ret += mcgen('''
bool has_%(c_name)s;
@ -136,7 +130,7 @@ def gen_struct_members(members: List[QAPISchemaObjectTypeMember]) -> str:
%(c_type)s %(c_name)s;
''',
c_type=memb.type.c_type(), c_name=c_name(memb.name))
ret += gen_endif(memb.ifcond.cgen())
ret += memb.ifcond.gen_endif()
return ret
@ -159,7 +153,7 @@ def gen_object(name: str, ifcond: QAPISchemaIfCond,
ret += mcgen('''
''')
ret += gen_if(ifcond.cgen())
ret += ifcond.gen_if()
ret += mcgen('''
struct %(c_name)s {
''',
@ -193,7 +187,7 @@ struct %(c_name)s {
ret += mcgen('''
};
''')
ret += gen_endif(ifcond.cgen())
ret += ifcond.gen_endif()
return ret
@ -220,13 +214,13 @@ def gen_variants(variants: QAPISchemaVariants) -> str:
for var in variants.variants:
if var.type.name == 'q_empty':
continue
ret += gen_if(var.ifcond.cgen())
ret += var.ifcond.gen_if()
ret += mcgen('''
%(c_type)s %(c_name)s;
''',
c_type=var.type.c_unboxed_type(),
c_name=c_name(var.name))
ret += gen_endif(var.ifcond.cgen())
ret += var.ifcond.gen_endif()
ret += mcgen('''
} u;

View File

@ -18,8 +18,6 @@ from typing import List, Optional
from .common import (
c_enum_const,
c_name,
gen_endif,
gen_if,
indent,
mcgen,
)
@ -79,7 +77,7 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
for memb in members:
deprecated = 'deprecated' in [f.name for f in memb.features]
ret += gen_if(memb.ifcond.cgen())
ret += memb.ifcond.gen_if()
if memb.optional:
ret += mcgen('''
if (visit_optional(v, "%(name)s", &obj->has_%(c_name)s)) {
@ -112,7 +110,7 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
ret += mcgen('''
}
''')
ret += gen_endif(memb.ifcond.cgen())
ret += memb.ifcond.gen_endif()
if variants:
tag_member = variants.tag_member
@ -126,7 +124,7 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
for var in variants.variants:
case_str = c_enum_const(tag_member.type.name, var.name,
tag_member.type.prefix)
ret += gen_if(var.ifcond.cgen())
ret += var.ifcond.gen_if()
if var.type.name == 'q_empty':
# valid variant and nothing to do
ret += mcgen('''
@ -142,7 +140,7 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
case=case_str,
c_type=var.type.c_name(), c_name=c_name(var.name))
ret += gen_endif(var.ifcond.cgen())
ret += var.ifcond.gen_endif()
ret += mcgen('''
default:
abort();
@ -228,7 +226,7 @@ bool visit_type_%(c_name)s(Visitor *v, const char *name,
c_name=c_name(name))
for var in variants.variants:
ret += gen_if(var.ifcond.cgen())
ret += var.ifcond.gen_if()
ret += mcgen('''
case %(case)s:
''',
@ -254,7 +252,7 @@ bool visit_type_%(c_name)s(Visitor *v, const char *name,
ret += mcgen('''
break;
''')
ret += gen_endif(var.ifcond.cgen())
ret += var.ifcond.gen_endif()
ret += mcgen('''
case QTYPE_NONE:

View File

@ -1,3 +1,3 @@
bad-if-key.json: In struct 'TestIfStruct':
bad-if-key.json:2: 'if' condition has unknown key 'value'
bad-if-key.json:2: 'if' condition of struct has unknown key 'value'
Valid keys are 'all', 'any', 'not'.

View File

@ -1,2 +1,2 @@
bad-if-keys.json: In struct 'TestIfStruct':
bad-if-keys.json:2: 'if' condition dict of struct must have one key: 'all', 'any' or 'not'
bad-if-keys.json:2: 'if' condition of struct has conflicting keys

View File

@ -127,7 +127,7 @@
{ 'alternate': 'Alternate',
'features': [ 'alt-feat' ],
'data': { 'i': 'int', 'b': 'bool' },
'if': { 'not': 'IFNOT' } }
'if': { 'not': { 'any': [ 'IFONE', 'IFTWO' ] } } }
##
# == Another subsection

View File

@ -18,7 +18,7 @@ enum Enum
feature enum-feat
object Base
member base1: Enum optional=False
if OrderedDict([('all', ['IFALL1', 'IFALL2'])])
if {'all': ['IFALL1', 'IFALL2']}
object Variant1
member var1: str optional=False
if IFSTR
@ -30,7 +30,7 @@ object Object
tag base1
case one: Variant1
case two: Variant2
if OrderedDict([('any', ['IFONE', 'IFTWO'])])
if {'any': ['IFONE', 'IFTWO']}
feature union-feat1
object q_obj_Variant1-wrapper
member data: Variant1 optional=False
@ -51,7 +51,7 @@ alternate Alternate
tag type
case i: int
case b: bool
if OrderedDict([('not', 'IFNOT')])
if {'not': {'any': ['IFONE', 'IFTWO']}}
feature alt-feat
object q_obj_cmd-arg
member arg1: int optional=False

View File

@ -79,7 +79,7 @@ Members
If
~~
"(IFALL1 and IFALL2)"
"IFALL1 and IFALL2"
"Variant1" (Object)
@ -120,8 +120,8 @@ Members
The members of "Base"
The members of "Variant1" when "base1" is ""one""
The members of "Variant2" when "base1" is ""two"" (**If: **"(IFONE or
IFTWO)")
The members of "Variant2" when "base1" is ""two"" (**If: **"IFONE or
IFTWO")
Features
~~~~~~~~
@ -174,7 +174,7 @@ Features
If
~~
"!IFNOT"
"not (IFONE or IFTWO)"
Another subsection

View File

@ -1,3 +1 @@
double-type.json: In struct 'Bar':
double-type.json:2: struct has unknown key 'command'
Valid keys are 'base', 'data', 'features', 'if', 'struct'.
double-type.json:2: expression must have exactly one key 'enum', 'struct', 'union', 'alternate', 'command', 'event'

View File

@ -1,3 +1,3 @@
enum-if-invalid.json: In enum 'TestIfEnum':
enum-if-invalid.json:2: 'if' condition has unknown key 'val'
enum-if-invalid.json:2: 'if' condition of 'data' member 'bar' has unknown key 'val'
Valid keys are 'all', 'any', 'not'.

View File

@ -1 +1 @@
missing-type.json:2: expression is missing metatype
missing-type.json:2: expression must have exactly one key 'enum', 'struct', 'union', 'alternate', 'command', 'event'

View File

@ -236,7 +236,7 @@
{ 'command': 'test-if-union-cmd',
'data': { 'union-cmd-arg': 'TestIfUnion' },
'if': 'TEST_IF_UNION' }
'if': { 'all': ['TEST_IF_UNION', 'TEST_IF_STRUCT'] } }
{ 'alternate': 'TestIfAlternate', 'data':
{ 'foo': 'int',
@ -245,8 +245,7 @@
{ 'command': 'test-if-alternate-cmd',
'data': { 'alt-cmd-arg': 'TestIfAlternate' },
'if': { 'all': ['TEST_IF_ALT',
{'not': 'TEST_IF_NOT_ALT'}] } }
'if': { 'all': ['TEST_IF_ALT', 'TEST_IF_STRUCT'] } }
{ 'command': 'test-if-cmd',
'data': {
@ -262,6 +261,10 @@
'bar': { 'type': ['TestIfEnum'], 'if': 'TEST_IF_EVT_BAR' } },
'if': { 'all': ['TEST_IF_EVT', 'TEST_IF_STRUCT'] } }
{ 'event': 'TEST_IF_EVENT2', 'data': {},
'if': { 'not': { 'any': [ { 'not': 'TEST_IF_EVT' },
{ 'not': 'TEST_IF_STRUCT' } ] } } }
# test 'features'
{ 'struct': 'FeatureStruct0',

View File

@ -311,40 +311,40 @@ enum TestIfUnionKind
member foo
member bar
if TEST_IF_UNION_BAR
if OrderedDict([('all', ['TEST_IF_UNION', 'TEST_IF_STRUCT'])])
if {'all': ['TEST_IF_UNION', 'TEST_IF_STRUCT']}
object TestIfUnion
member type: TestIfUnionKind optional=False
tag type
case foo: q_obj_TestStruct-wrapper
case bar: q_obj_str-wrapper
if TEST_IF_UNION_BAR
if OrderedDict([('all', ['TEST_IF_UNION', 'TEST_IF_STRUCT'])])
if {'all': ['TEST_IF_UNION', 'TEST_IF_STRUCT']}
object q_obj_test-if-union-cmd-arg
member union-cmd-arg: TestIfUnion optional=False
if TEST_IF_UNION
if {'all': ['TEST_IF_UNION', 'TEST_IF_STRUCT']}
command test-if-union-cmd q_obj_test-if-union-cmd-arg -> None
gen=True success_response=True boxed=False oob=False preconfig=False
if TEST_IF_UNION
if {'all': ['TEST_IF_UNION', 'TEST_IF_STRUCT']}
alternate TestIfAlternate
tag type
case foo: int
case bar: TestStruct
if TEST_IF_ALT_BAR
if OrderedDict([('all', ['TEST_IF_ALT', 'TEST_IF_STRUCT'])])
if {'all': ['TEST_IF_ALT', 'TEST_IF_STRUCT']}
object q_obj_test-if-alternate-cmd-arg
member alt-cmd-arg: TestIfAlternate optional=False
if OrderedDict([('all', ['TEST_IF_ALT', OrderedDict([('not', 'TEST_IF_NOT_ALT')])])])
if {'all': ['TEST_IF_ALT', 'TEST_IF_STRUCT']}
command test-if-alternate-cmd q_obj_test-if-alternate-cmd-arg -> None
gen=True success_response=True boxed=False oob=False preconfig=False
if OrderedDict([('all', ['TEST_IF_ALT', OrderedDict([('not', 'TEST_IF_NOT_ALT')])])])
if {'all': ['TEST_IF_ALT', 'TEST_IF_STRUCT']}
object q_obj_test-if-cmd-arg
member foo: TestIfStruct optional=False
member bar: TestIfEnum optional=False
if TEST_IF_CMD_BAR
if OrderedDict([('all', ['TEST_IF_CMD', 'TEST_IF_STRUCT'])])
if {'all': ['TEST_IF_CMD', 'TEST_IF_STRUCT']}
command test-if-cmd q_obj_test-if-cmd-arg -> UserDefThree
gen=True success_response=True boxed=False oob=False preconfig=False
if OrderedDict([('all', ['TEST_IF_CMD', 'TEST_IF_STRUCT'])])
if {'all': ['TEST_IF_CMD', 'TEST_IF_STRUCT']}
command test-cmd-return-def-three None -> UserDefThree
gen=True success_response=True boxed=False oob=False preconfig=False
array TestIfEnumList TestIfEnum
@ -353,10 +353,13 @@ object q_obj_TEST_IF_EVENT-arg
member foo: TestIfStruct optional=False
member bar: TestIfEnumList optional=False
if TEST_IF_EVT_BAR
if OrderedDict([('all', ['TEST_IF_EVT', 'TEST_IF_STRUCT'])])
if {'all': ['TEST_IF_EVT', 'TEST_IF_STRUCT']}
event TEST_IF_EVENT q_obj_TEST_IF_EVENT-arg
boxed=False
if OrderedDict([('all', ['TEST_IF_EVT', 'TEST_IF_STRUCT'])])
if {'all': ['TEST_IF_EVT', 'TEST_IF_STRUCT']}
event TEST_IF_EVENT2 None
boxed=False
if {'not': {'any': [{'not': 'TEST_IF_EVT'}, {'not': 'TEST_IF_STRUCT'}]}}
object FeatureStruct0
member foo: int optional=False
object FeatureStruct1
@ -389,11 +392,11 @@ object CondFeatureStruct2
object CondFeatureStruct3
member foo: int optional=False
feature feature1
if OrderedDict([('all', ['TEST_IF_COND_1', 'TEST_IF_COND_2'])])
if {'all': ['TEST_IF_COND_1', 'TEST_IF_COND_2']}
object CondFeatureStruct4
member foo: int optional=False
feature feature1
if OrderedDict([('any', ['TEST_IF_COND_1', 'TEST_IF_COND_2'])])
if {'any': ['TEST_IF_COND_1', 'TEST_IF_COND_2']}
enum FeatureEnum1
member eins
member zwei
@ -444,7 +447,7 @@ command test-command-cond-features2 None -> None
command test-command-cond-features3 None -> None
gen=True success_response=True boxed=False oob=False preconfig=False
feature feature1
if OrderedDict([('all', ['TEST_IF_COND_1', 'TEST_IF_COND_2'])])
if {'all': ['TEST_IF_COND_1', 'TEST_IF_COND_2']}
event TEST_EVENT_FEATURES0 FeatureStruct1
boxed=False
event TEST_EVENT_FEATURES1 None

View File

@ -94,8 +94,17 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
@staticmethod
def _print_if(ifcond, indent=4):
# TODO Drop this hack after replacing OrderedDict by plain
# dict (requires Python 3.7)
def _massage(subcond):
if isinstance(subcond, str):
return subcond
if isinstance(subcond, list):
return [_massage(val) for val in subcond]
return {key: _massage(val) for key, val in subcond.items()}
if ifcond.is_present():
print('%sif %s' % (' ' * indent, ifcond.ifcond))
print('%sif %s' % (' ' * indent, _massage(ifcond.ifcond)))
@classmethod
def _print_features(cls, features, indent=4):