qapi: add a dictionary form for TYPE
Wherever a struct/union/alternate/command/event member with NAME: TYPE form is accepted, desugar it to a NAME: { 'type': TYPE } form. This will allow to add new member details, such as 'if' in the following patch to introduce conditionals, or 'default' for default values etc. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-Id: <20181213123724.4866-13-marcandre.lureau@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
This commit is contained in:
parent
7bd2634905
commit
87adbbffd4
@ -588,11 +588,11 @@ def discriminator_find_enum_define(expr):
|
||||
if not base_members:
|
||||
return None
|
||||
|
||||
discriminator_type = base_members.get(discriminator)
|
||||
if not discriminator_type:
|
||||
discriminator_value = base_members.get(discriminator)
|
||||
if not discriminator_value:
|
||||
return None
|
||||
|
||||
return enum_types.get(discriminator_type)
|
||||
return enum_types.get(discriminator_value['type'])
|
||||
|
||||
|
||||
# Names must be letters, numbers, -, and _. They must start with letter,
|
||||
@ -704,8 +704,10 @@ def check_type(info, source, value, allow_array=False,
|
||||
% (source, key))
|
||||
# Todo: allow dictionaries to represent default values of
|
||||
# an optional argument.
|
||||
check_type(info, "Member '%s' of %s" % (key, source), arg,
|
||||
allow_array=True,
|
||||
check_known_keys(info, "member '%s' of %s" % (key, source),
|
||||
arg, ['type'], [])
|
||||
check_type(info, "Member '%s' of %s" % (key, source),
|
||||
arg['type'], allow_array=True,
|
||||
allow_metas=['built-in', 'union', 'alternate', 'struct',
|
||||
'enum'])
|
||||
|
||||
@ -776,13 +778,13 @@ def check_union(expr, info):
|
||||
# member of the base struct.
|
||||
check_name(info, "Discriminator of flat union '%s'" % name,
|
||||
discriminator)
|
||||
discriminator_type = base_members.get(discriminator)
|
||||
if not discriminator_type:
|
||||
discriminator_value = base_members.get(discriminator)
|
||||
if not discriminator_value:
|
||||
raise QAPISemError(info,
|
||||
"Discriminator '%s' is not a member of base "
|
||||
"struct '%s'"
|
||||
% (discriminator, base))
|
||||
enum_define = enum_types.get(discriminator_type)
|
||||
enum_define = enum_types.get(discriminator_value['type'])
|
||||
allow_metas = ['struct']
|
||||
# Do not allow string discriminator
|
||||
if not enum_define:
|
||||
@ -796,9 +798,12 @@ def check_union(expr, info):
|
||||
for (key, value) in members.items():
|
||||
check_name(info, "Member of union '%s'" % name, key)
|
||||
|
||||
check_known_keys(info, "member '%s' of union '%s'" % (key, name),
|
||||
value, ['type'], [])
|
||||
# Each value must name a known type
|
||||
check_type(info, "Member '%s' of union '%s'" % (key, name),
|
||||
value, allow_array=not base, allow_metas=allow_metas)
|
||||
value['type'],
|
||||
allow_array=not base, allow_metas=allow_metas)
|
||||
|
||||
# If the discriminator names an enum type, then all members
|
||||
# of 'data' must also be members of the enum type.
|
||||
@ -822,18 +827,21 @@ def check_alternate(expr, info):
|
||||
"in 'data'" % name)
|
||||
for (key, value) in members.items():
|
||||
check_name(info, "Member of alternate '%s'" % name, key)
|
||||
check_known_keys(info,
|
||||
"member '%s' of alternate '%s'" % (key, name),
|
||||
value, ['type'], [])
|
||||
typ = value['type']
|
||||
|
||||
# Ensure alternates have no type conflicts.
|
||||
check_type(info, "Member '%s' of alternate '%s'" % (key, name),
|
||||
value,
|
||||
check_type(info, "Member '%s' of alternate '%s'" % (key, name), typ,
|
||||
allow_metas=['built-in', 'union', 'struct', 'enum'])
|
||||
qtype = find_alternate_member_qtype(value)
|
||||
qtype = find_alternate_member_qtype(typ)
|
||||
if not qtype:
|
||||
raise QAPISemError(info, "Alternate '%s' member '%s' cannot use "
|
||||
"type '%s'" % (name, key, value))
|
||||
"type '%s'" % (name, key, typ))
|
||||
conflicting = set([qtype])
|
||||
if qtype == 'QTYPE_QSTRING':
|
||||
enum_expr = enum_types.get(value)
|
||||
enum_expr = enum_types.get(typ)
|
||||
if enum_expr:
|
||||
for v in enum_get_names(enum_expr):
|
||||
if v in ['on', 'off']:
|
||||
@ -851,12 +859,6 @@ def check_alternate(expr, info):
|
||||
types_seen[qt] = key
|
||||
|
||||
|
||||
def normalize_enum(expr):
|
||||
if isinstance(expr['data'], list):
|
||||
expr['data'] = [m if isinstance(m, dict) else {'name': m}
|
||||
for m in expr['data']]
|
||||
|
||||
|
||||
def check_enum(expr, info):
|
||||
name = expr['enum']
|
||||
members = expr['data']
|
||||
@ -928,6 +930,20 @@ def check_keys(expr_elem, meta, required, optional=[]):
|
||||
check_if(expr, info)
|
||||
|
||||
|
||||
def normalize_enum(expr):
|
||||
if isinstance(expr['data'], list):
|
||||
expr['data'] = [m if isinstance(m, dict) else {'name': m}
|
||||
for m in expr['data']]
|
||||
|
||||
|
||||
def normalize_members(members):
|
||||
if isinstance(members, OrderedDict):
|
||||
for key, arg in members.items():
|
||||
if isinstance(arg, dict):
|
||||
continue
|
||||
members[key] = {'type': arg}
|
||||
|
||||
|
||||
def check_exprs(exprs):
|
||||
global all_names
|
||||
|
||||
@ -957,22 +973,28 @@ def check_exprs(exprs):
|
||||
meta = 'union'
|
||||
check_keys(expr_elem, 'union', ['data'],
|
||||
['base', 'discriminator', 'if'])
|
||||
normalize_members(expr.get('base'))
|
||||
normalize_members(expr['data'])
|
||||
union_types[expr[meta]] = expr
|
||||
elif 'alternate' in expr:
|
||||
meta = 'alternate'
|
||||
check_keys(expr_elem, 'alternate', ['data'], ['if'])
|
||||
normalize_members(expr['data'])
|
||||
elif 'struct' in expr:
|
||||
meta = 'struct'
|
||||
check_keys(expr_elem, 'struct', ['data'], ['base', 'if'])
|
||||
normalize_members(expr['data'])
|
||||
struct_types[expr[meta]] = expr
|
||||
elif 'command' in expr:
|
||||
meta = 'command'
|
||||
check_keys(expr_elem, 'command', [],
|
||||
['data', 'returns', 'gen', 'success-response',
|
||||
'boxed', 'allow-oob', 'allow-preconfig', 'if'])
|
||||
normalize_members(expr.get('data'))
|
||||
elif 'event' in expr:
|
||||
meta = 'event'
|
||||
check_keys(expr_elem, 'event', [], ['data', 'boxed', 'if'])
|
||||
normalize_members(expr.get('data'))
|
||||
else:
|
||||
raise QAPISemError(expr_elem['info'],
|
||||
"Expression is missing metatype")
|
||||
@ -1716,7 +1738,7 @@ class QAPISchema(object):
|
||||
return QAPISchemaObjectTypeMember(name, typ, optional)
|
||||
|
||||
def _make_members(self, data, info):
|
||||
return [self._make_member(key, value, info)
|
||||
return [self._make_member(key, value['type'], info)
|
||||
for (key, value) in data.items()]
|
||||
|
||||
def _def_struct_type(self, expr, info, doc):
|
||||
@ -1752,11 +1774,11 @@ class QAPISchema(object):
|
||||
name, info, doc, ifcond,
|
||||
'base', self._make_members(base, info))
|
||||
if tag_name:
|
||||
variants = [self._make_variant(key, value)
|
||||
variants = [self._make_variant(key, value['type'])
|
||||
for (key, value) in data.items()]
|
||||
members = []
|
||||
else:
|
||||
variants = [self._make_simple_variant(key, value, info)
|
||||
variants = [self._make_simple_variant(key, value['type'], info)
|
||||
for (key, value) in data.items()]
|
||||
enum = [{'name': v.name} for v in variants]
|
||||
typ = self._make_implicit_enum_type(name, info, ifcond, enum)
|
||||
@ -1772,7 +1794,7 @@ class QAPISchema(object):
|
||||
name = expr['alternate']
|
||||
data = expr['data']
|
||||
ifcond = expr.get('if')
|
||||
variants = [self._make_variant(key, value)
|
||||
variants = [self._make_variant(key, value['type'])
|
||||
for (key, value) in data.items()]
|
||||
tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
|
||||
self._def_entity(
|
||||
|
@ -318,6 +318,7 @@ qapi-schema += alternate-conflict-string.json
|
||||
qapi-schema += alternate-conflict-bool-string.json
|
||||
qapi-schema += alternate-conflict-num-string.json
|
||||
qapi-schema += alternate-empty.json
|
||||
qapi-schema += alternate-invalid-dict.json
|
||||
qapi-schema += alternate-nested.json
|
||||
qapi-schema += alternate-unknown.json
|
||||
qapi-schema += args-alternate.json
|
||||
@ -394,6 +395,7 @@ qapi-schema += escape-too-big.json
|
||||
qapi-schema += escape-too-short.json
|
||||
qapi-schema += event-boxed-empty.json
|
||||
qapi-schema += event-case.json
|
||||
qapi-schema += event-member-invalid-dict.json
|
||||
qapi-schema += event-nest-struct.json
|
||||
qapi-schema += flat-union-array-branch.json
|
||||
qapi-schema += flat-union-bad-base.json
|
||||
@ -403,6 +405,7 @@ qapi-schema += flat-union-base-union.json
|
||||
qapi-schema += flat-union-clash-member.json
|
||||
qapi-schema += flat-union-empty.json
|
||||
qapi-schema += flat-union-inline.json
|
||||
qapi-schema += flat-union-inline-invalid-dict.json
|
||||
qapi-schema += flat-union-int-branch.json
|
||||
qapi-schema += flat-union-invalid-branch-key.json
|
||||
qapi-schema += flat-union-invalid-discriminator.json
|
||||
@ -430,6 +433,7 @@ qapi-schema += missing-comma-list.json
|
||||
qapi-schema += missing-comma-object.json
|
||||
qapi-schema += missing-type.json
|
||||
qapi-schema += nested-struct-data.json
|
||||
qapi-schema += nested-struct-data-invalid-dict.json
|
||||
qapi-schema += non-objects.json
|
||||
qapi-schema += oob-test.json
|
||||
qapi-schema += allow-preconfig-test.json
|
||||
@ -460,6 +464,7 @@ qapi-schema += returns-whitelist.json
|
||||
qapi-schema += struct-base-clash-deep.json
|
||||
qapi-schema += struct-base-clash.json
|
||||
qapi-schema += struct-data-invalid.json
|
||||
qapi-schema += struct-member-invalid-dict.json
|
||||
qapi-schema += struct-member-invalid.json
|
||||
qapi-schema += trailing-comma-list.json
|
||||
qapi-schema += trailing-comma-object.json
|
||||
@ -471,6 +476,7 @@ qapi-schema += unicode-str.json
|
||||
qapi-schema += union-base-empty.json
|
||||
qapi-schema += union-base-no-discriminator.json
|
||||
qapi-schema += union-branch-case.json
|
||||
qapi-schema += union-branch-invalid-dict.json
|
||||
qapi-schema += union-clash-branches.json
|
||||
qapi-schema += union-empty.json
|
||||
qapi-schema += union-invalid-base.json
|
||||
|
1
tests/qapi-schema/alternate-invalid-dict.err
Normal file
1
tests/qapi-schema/alternate-invalid-dict.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/alternate-invalid-dict.json:2: Key 'type' is missing from member 'two' of alternate 'Alt'
|
1
tests/qapi-schema/alternate-invalid-dict.exit
Normal file
1
tests/qapi-schema/alternate-invalid-dict.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
4
tests/qapi-schema/alternate-invalid-dict.json
Normal file
4
tests/qapi-schema/alternate-invalid-dict.json
Normal file
@ -0,0 +1,4 @@
|
||||
# exploded member form must have a 'type'
|
||||
{ 'alternate': 'Alt',
|
||||
'data': { 'one': 'str',
|
||||
'two': { 'if': 'foo' } } }
|
0
tests/qapi-schema/alternate-invalid-dict.out
Normal file
0
tests/qapi-schema/alternate-invalid-dict.out
Normal file
1
tests/qapi-schema/event-member-invalid-dict.err
Normal file
1
tests/qapi-schema/event-member-invalid-dict.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/event-member-invalid-dict.json:1: Key 'type' is missing from member 'a' of 'data' for event 'EVENT_A'
|
1
tests/qapi-schema/event-member-invalid-dict.exit
Normal file
1
tests/qapi-schema/event-member-invalid-dict.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
2
tests/qapi-schema/event-member-invalid-dict.json
Normal file
2
tests/qapi-schema/event-member-invalid-dict.json
Normal file
@ -0,0 +1,2 @@
|
||||
{ 'event': 'EVENT_A',
|
||||
'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } }
|
0
tests/qapi-schema/event-member-invalid-dict.out
Normal file
0
tests/qapi-schema/event-member-invalid-dict.out
Normal file
@ -1,2 +1,2 @@
|
||||
{ 'event': 'EVENT_A',
|
||||
'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } }
|
||||
'data': { 'a' : { 'type' : { 'integer': 'int' } }, 'b' : 'str' } }
|
||||
|
1
tests/qapi-schema/flat-union-inline-invalid-dict.err
Normal file
1
tests/qapi-schema/flat-union-inline-invalid-dict.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/flat-union-inline-invalid-dict.json:7: Key 'type' is missing from member 'value1' of union 'TestUnion'
|
1
tests/qapi-schema/flat-union-inline-invalid-dict.exit
Normal file
1
tests/qapi-schema/flat-union-inline-invalid-dict.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
11
tests/qapi-schema/flat-union-inline-invalid-dict.json
Normal file
11
tests/qapi-schema/flat-union-inline-invalid-dict.json
Normal file
@ -0,0 +1,11 @@
|
||||
# we require branches to be a struct name
|
||||
# TODO: should we allow anonymous inline branch types?
|
||||
{ 'enum': 'TestEnum',
|
||||
'data': [ 'value1', 'value2' ] }
|
||||
{ 'struct': 'Base',
|
||||
'data': { 'enum1': 'TestEnum', 'kind': 'str' } }
|
||||
{ 'union': 'TestUnion',
|
||||
'base': 'Base',
|
||||
'discriminator': 'enum1',
|
||||
'data': { 'value1': { 'string': 'str' },
|
||||
'value2': { 'integer': 'int' } } }
|
@ -7,5 +7,5 @@
|
||||
{ 'union': 'TestUnion',
|
||||
'base': 'Base',
|
||||
'discriminator': 'enum1',
|
||||
'data': { 'value1': { 'string': 'str' },
|
||||
'data': { 'value1': { 'type': {} },
|
||||
'value2': { 'integer': 'int' } } }
|
||||
|
1
tests/qapi-schema/nested-struct-data-invalid-dict.err
Normal file
1
tests/qapi-schema/nested-struct-data-invalid-dict.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/nested-struct-data-invalid-dict.json:2: Key 'type' is missing from member 'a' of 'data' for command 'foo'
|
1
tests/qapi-schema/nested-struct-data-invalid-dict.exit
Normal file
1
tests/qapi-schema/nested-struct-data-invalid-dict.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
3
tests/qapi-schema/nested-struct-data-invalid-dict.json
Normal file
3
tests/qapi-schema/nested-struct-data-invalid-dict.json
Normal file
@ -0,0 +1,3 @@
|
||||
# inline subtypes collide with our desired future use of defaults
|
||||
{ 'command': 'foo',
|
||||
'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } }
|
@ -1,3 +1,3 @@
|
||||
# inline subtypes collide with our desired future use of defaults
|
||||
{ 'command': 'foo',
|
||||
'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } }
|
||||
'data': { 'a' : { 'type': {} }, 'b' : 'str' } }
|
||||
|
@ -11,7 +11,7 @@
|
||||
'guest-sync' ] } }
|
||||
|
||||
{ 'struct': 'TestStruct',
|
||||
'data': { 'integer': 'int', 'boolean': 'bool', 'string': 'str' } }
|
||||
'data': { 'integer': {'type': 'int'}, 'boolean': 'bool', 'string': 'str' } }
|
||||
|
||||
# for testing enums
|
||||
{ 'struct': 'NestedEnumsOne',
|
||||
@ -77,7 +77,7 @@
|
||||
{ 'union': 'UserDefFlatUnion',
|
||||
'base': 'UserDefUnionBase', # intentional forward reference
|
||||
'discriminator': 'enum1',
|
||||
'data': { 'value1' : 'UserDefA',
|
||||
'data': { 'value1' : {'type': 'UserDefA'},
|
||||
'value2' : 'UserDefB',
|
||||
'value3' : 'UserDefB'
|
||||
# 'value4' defaults to empty
|
||||
@ -98,7 +98,7 @@
|
||||
{ 'struct': 'WrapAlternate',
|
||||
'data': { 'alt': 'UserDefAlternate' } }
|
||||
{ 'alternate': 'UserDefAlternate',
|
||||
'data': { 'udfu': 'UserDefFlatUnion', 'e': 'EnumOne', 'i': 'int',
|
||||
'data': { 'udfu': {'type': 'UserDefFlatUnion'}, 'e': 'EnumOne', 'i': 'int',
|
||||
'n': 'null' } }
|
||||
|
||||
{ 'struct': 'UserDefC',
|
||||
@ -134,7 +134,7 @@
|
||||
{ 'command': 'user_def_cmd', 'data': {} }
|
||||
{ 'command': 'user_def_cmd1', 'data': {'ud1a': 'UserDefOne'} }
|
||||
{ 'command': 'user_def_cmd2',
|
||||
'data': {'ud1a': 'UserDefOne', '*ud1b': 'UserDefOne'},
|
||||
'data': {'ud1a': {'type': 'UserDefOne'}, '*ud1b': 'UserDefOne'},
|
||||
'returns': 'UserDefTwo' }
|
||||
|
||||
{ 'command': 'cmd-success-response', 'data': {}, 'success-response': false }
|
||||
@ -166,7 +166,7 @@
|
||||
|
||||
# testing event
|
||||
{ 'struct': 'EventStructOne',
|
||||
'data': { 'struct1': 'UserDefOne', 'string': 'str', '*enum2': 'EnumOne' } }
|
||||
'data': { 'struct1': {'type': 'UserDefOne'}, 'string': 'str', '*enum2': 'EnumOne' } }
|
||||
|
||||
{ 'event': 'EVENT_A' }
|
||||
{ 'event': 'EVENT_B',
|
||||
|
1
tests/qapi-schema/struct-member-invalid-dict.err
Normal file
1
tests/qapi-schema/struct-member-invalid-dict.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/struct-member-invalid-dict.json:2: Key 'type' is missing from member '*a' of 'data' for struct 'foo'
|
1
tests/qapi-schema/struct-member-invalid-dict.exit
Normal file
1
tests/qapi-schema/struct-member-invalid-dict.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
3
tests/qapi-schema/struct-member-invalid-dict.json
Normal file
3
tests/qapi-schema/struct-member-invalid-dict.json
Normal file
@ -0,0 +1,3 @@
|
||||
# Long form of member must have a value member 'type'
|
||||
{ 'struct': 'foo',
|
||||
'data': { '*a': { 'case': 'foo' } } }
|
0
tests/qapi-schema/struct-member-invalid-dict.out
Normal file
0
tests/qapi-schema/struct-member-invalid-dict.out
Normal file
1
tests/qapi-schema/union-branch-invalid-dict.err
Normal file
1
tests/qapi-schema/union-branch-invalid-dict.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/union-branch-invalid-dict.json:2: Key 'type' is missing from member 'integer' of union 'UnionInvalidBranch'
|
1
tests/qapi-schema/union-branch-invalid-dict.exit
Normal file
1
tests/qapi-schema/union-branch-invalid-dict.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
4
tests/qapi-schema/union-branch-invalid-dict.json
Normal file
4
tests/qapi-schema/union-branch-invalid-dict.json
Normal file
@ -0,0 +1,4 @@
|
||||
# Long form of member must have a value member 'type'
|
||||
{ 'union': 'UnionInvalidBranch',
|
||||
'data': { 'integer': { 'if': 'foo'},
|
||||
's8': 'int8' } }
|
0
tests/qapi-schema/union-branch-invalid-dict.out
Normal file
0
tests/qapi-schema/union-branch-invalid-dict.out
Normal file
Loading…
Reference in New Issue
Block a user