qapi: Add feature flags to enum members

This is quite similar to commit 84ab008687 "qapi: Add feature flags to
struct members", only for enums instead of structs.

Special feature flag 'deprecated' is silently ignored there.  This is
okay only because it will be implemented shortly.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-Id: <20211025042405.3762351-3-armbru@redhat.com>
Reviewed-by: John Snow <jsnow@redhat.com>
This commit is contained in:
Markus Armbruster 2021-10-25 06:24:02 +02:00
parent 75ecee7262
commit b6c18755e4
13 changed files with 57 additions and 14 deletions

View File

@ -200,7 +200,9 @@ Syntax::
'*if': COND, '*if': COND,
'*features': FEATURES } '*features': FEATURES }
ENUM-VALUE = STRING ENUM-VALUE = STRING
| { 'name': STRING, '*if': COND } | { 'name': STRING,
'*if': COND,
'*features': FEATURES }
Member 'enum' names the enum type. Member 'enum' names the enum type.
@ -706,8 +708,10 @@ QEMU shows a certain behaviour.
Special features Special features
~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
Feature "deprecated" marks a command, event, or struct member as Feature "deprecated" marks a command, event, enum value, or struct
deprecated. It is not supported elsewhere so far. member as deprecated. It is not supported elsewhere so far.
Interfaces so marked may be withdrawn in future releases in accordance
with QEMU's deprecation policy.
Naming rules and reserved names Naming rules and reserved names
@ -1157,7 +1161,8 @@ and "variants".
"members" is a JSON array describing the object's common members, if "members" is a JSON array describing the object's common members, if
any. Each element is a JSON object with members "name" (the member's any. Each element is a JSON object with members "name" (the member's
name), "type" (the name of its type), and optionally "default". The name), "type" (the name of its type), "features" (a JSON array of
feature strings), and "default". The latter two are optional. The
member is optional if "default" is present. Currently, "default" can member is optional if "default" is present. Currently, "default" can
only have value null. Other values are reserved for future only have value null. Other values are reserved for future
extensions. The "members" array is in no particular order; clients extensions. The "members" array is in no particular order; clients
@ -1234,7 +1239,8 @@ The SchemaInfo for an enumeration type has meta-type "enum" and
variant member "members". variant member "members".
"members" is a JSON array describing the enumeration values. Each "members" is a JSON array describing the enumeration values. Each
element is a JSON object with member "name" (the member's name). The element is a JSON object with member "name" (the member's name), and
optionally "features" (a JSON array of feature strings). The
"members" array is in no particular order; clients must search the "members" array is in no particular order; clients must search the
entire array when learning whether a particular value is supported. entire array when learning whether a particular value is supported.

View File

@ -42,6 +42,8 @@
# with feature 'deprecated'. We may want to extend it to cover # with feature 'deprecated'. We may want to extend it to cover
# semantic aspects, CLI, and experimental features. # semantic aspects, CLI, and experimental features.
# #
# Limitation: not implemented for deprecated enumeration values.
#
# @deprecated-input: how to handle deprecated input (default 'accept') # @deprecated-input: how to handle deprecated input (default 'accept')
# @deprecated-output: how to handle deprecated output (default 'accept') # @deprecated-output: how to handle deprecated output (default 'accept')
# #

View File

@ -167,10 +167,13 @@
# #
# @name: the member's name, as defined in the QAPI schema. # @name: the member's name, as defined in the QAPI schema.
# #
# @features: names of features associated with the member, in no
# particular order.
#
# Since: 6.2 # Since: 6.2
## ##
{ 'struct': 'SchemaInfoEnumMember', { 'struct': 'SchemaInfoEnumMember',
'data': { 'name': 'str' } } 'data': { 'name': 'str', '*features': [ 'str' ] } }
## ##
# @SchemaInfoArray: # @SchemaInfoArray:

View File

@ -472,7 +472,7 @@ def check_enum(expr: _JSONObject, info: QAPISourceInfo) -> None:
for m in members] for m in members]
for member in members: for member in members:
source = "'data' member" source = "'data' member"
check_keys(member, info, source, ['name'], ['if']) check_keys(member, info, source, ['name'], ['if', 'features'])
member_name = member['name'] member_name = member['name']
check_name_is_str(member_name, info, source) check_name_is_str(member_name, info, source)
source = "%s '%s'" % (source, member_name) source = "%s '%s'" % (source, member_name)
@ -483,6 +483,7 @@ def check_enum(expr: _JSONObject, info: QAPISourceInfo) -> None:
permit_upper=permissive, permit_upper=permissive,
permit_underscore=permissive) permit_underscore=permissive)
check_if(member, info, source) check_if(member, info, source)
check_features(member.get('features'), info)
def check_struct(expr: _JSONObject, info: QAPISourceInfo) -> None: def check_struct(expr: _JSONObject, info: QAPISourceInfo) -> None:

View File

@ -275,12 +275,13 @@ const QLitObject %(c_name)s = %(c_string)s;
obj['features'] = self._gen_features(features) obj['features'] = self._gen_features(features)
self._trees.append(Annotated(obj, ifcond, comment)) self._trees.append(Annotated(obj, ifcond, comment))
@staticmethod def _gen_enum_member(self, member: QAPISchemaEnumMember
def _gen_enum_member(member: QAPISchemaEnumMember
) -> Annotated[SchemaInfoEnumMember]: ) -> Annotated[SchemaInfoEnumMember]:
obj: SchemaInfoEnumMember = { obj: SchemaInfoEnumMember = {
'name': member.name, 'name': member.name,
} }
if member.features:
obj['features'] = self._gen_features(member.features)
return Annotated(obj, member.ifcond) return Annotated(obj, member.ifcond)
def _gen_object_member(self, member: QAPISchemaObjectTypeMember def _gen_object_member(self, member: QAPISchemaObjectTypeMember

View File

@ -708,6 +708,19 @@ class QAPISchemaMember:
class QAPISchemaEnumMember(QAPISchemaMember): class QAPISchemaEnumMember(QAPISchemaMember):
role = 'value' role = 'value'
def __init__(self, name, info, ifcond=None, features=None):
super().__init__(name, info, ifcond)
for f in features or []:
assert isinstance(f, QAPISchemaFeature)
f.set_defined_in(name)
self.features = features or []
def connect_doc(self, doc):
super().connect_doc(doc)
if doc:
for f in self.features:
doc.connect_feature(f)
class QAPISchemaFeature(QAPISchemaMember): class QAPISchemaFeature(QAPISchemaMember):
role = 'feature' role = 'feature'
@ -980,9 +993,14 @@ class QAPISchema:
QAPISchemaIfCond(f.get('if'))) QAPISchemaIfCond(f.get('if')))
for f in features] for f in features]
def _make_enum_member(self, name, ifcond, features, info):
return QAPISchemaEnumMember(name, info,
QAPISchemaIfCond(ifcond),
self._make_features(features, info))
def _make_enum_members(self, values, info): def _make_enum_members(self, values, info):
return [QAPISchemaEnumMember(v['name'], info, return [self._make_enum_member(v['name'], v.get('if'),
QAPISchemaIfCond(v.get('if'))) v.get('features'), info)
for v in values] for v in values]
def _make_array_type(self, element_type, info): def _make_array_type(self, element_type, info):

View File

@ -58,11 +58,14 @@
# #
# Features: # Features:
# @enum-feat: Also _one_ {and only} # @enum-feat: Also _one_ {and only}
# @enum-member-feat: a member feature
# #
# @two is undocumented # @two is undocumented
## ##
{ 'enum': 'Enum', { 'enum': 'Enum',
'data': [ { 'name': 'one', 'if': 'IFONE' }, 'two' ], 'data': [ { 'name': 'one', 'if': 'IFONE',
'features': [ 'enum-member-feat' ] },
'two' ],
'features': [ 'enum-feat' ], 'features': [ 'enum-feat' ],
'if': 'IFCOND' } 'if': 'IFCOND' }

View File

@ -13,6 +13,7 @@ module doc-good.json
enum Enum enum Enum
member one member one
if IFONE if IFONE
feature enum-member-feat
member two member two
if IFCOND if IFCOND
feature enum-feat feature enum-feat
@ -108,6 +109,8 @@ The _one_ {and only}
feature=enum-feat feature=enum-feat
Also _one_ {and only} Also _one_ {and only}
feature=enum-member-feat
a member feature
section=None section=None
@two is undocumented @two is undocumented
doc symbol=Base doc symbol=Base

View File

@ -56,6 +56,9 @@ Features
"enum-feat" "enum-feat"
Also _one_ {and only} Also _one_ {and only}
"enum-member-feat"
a member feature
"two" is undocumented "two" is undocumented

View File

@ -1,3 +1,3 @@
enum-dict-member-unknown.json: In enum 'MyEnum': enum-dict-member-unknown.json: In enum 'MyEnum':
enum-dict-member-unknown.json:2: 'data' member has unknown key 'bad-key' enum-dict-member-unknown.json:2: 'data' member has unknown key 'bad-key'
Valid keys are 'if', 'name'. Valid keys are 'features', 'if', 'name'.

View File

@ -301,7 +301,8 @@
'TEST_IF_COND_2'] } } ] } 'TEST_IF_COND_2'] } } ] }
{ 'enum': 'FeatureEnum1', { 'enum': 'FeatureEnum1',
'data': [ 'eins', 'zwei', 'drei' ], 'data': [ 'eins', 'zwei',
{ 'name': 'drei', 'features': [ 'deprecated' ] } ],
'features': [ 'feature1' ] } 'features': [ 'feature1' ] }
{ 'union': 'FeatureUnion1', { 'union': 'FeatureUnion1',

View File

@ -341,6 +341,7 @@ enum FeatureEnum1
member eins member eins
member zwei member zwei
member drei member drei
feature deprecated
feature feature1 feature feature1
object q_obj_FeatureUnion1-base object q_obj_FeatureUnion1-base
member tag: FeatureEnum1 optional=False member tag: FeatureEnum1 optional=False

View File

@ -37,6 +37,7 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
for m in members: for m in members:
print(' member %s' % m.name) print(' member %s' % m.name)
self._print_if(m.ifcond, indent=8) self._print_if(m.ifcond, indent=8)
self._print_features(m.features, indent=8)
self._print_if(ifcond) self._print_if(ifcond)
self._print_features(features) self._print_features(features)