mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-02 15:23:53 -06:00
QAPI patches for 2019-06-12
-----BEGIN PGP SIGNATURE----- iQJGBAABCAAwFiEENUvIs9frKmtoZ05fOHC0AOuRhlMFAl0BLAISHGFybWJydUBy ZWRoYXQuY29tAAoJEDhwtADrkYZTlRMQAKLpFwk4kFlqYm0KHqBtdjn57Z756Boi 9Uu/pDtet3+tpryMNcsM3lQ6uZCl35MY1ssYGCDc6JZVnLKgsN8EyO2MKnjAhogj hZWWhuJdR6vHGqgK+IyIOCgfIG26HS9+ejJIWJ4qm0sCAtmNEKjKRqJNPnSxbRHc wm1QnxFGikhKe74luDUk5zISlDFY1CILSk7W6K0nbdq6ByrMkq0dcjSLSpwWmALQ KSpWVkUz9CKxJ1dZn2iQ2af4VVKsL8xbl4R5nc5RInKFJ2W3fpVJqsXbEcZC6kiT WntZivbthbXXwc3I4IODs49Vu6PIt6OmH8vFmcWGt5hQB89ErhndVxtfCA11Qnml obhsQD+3AR4vncAJo+Y6yhbZQ0Fj2fy7xOrdPNXMdcXyLx9v7oOI66oXUpy4svT2 TR0AK+8wipt8BihId6+YynC6I5hOMWDDjKJwCnWZ20rnnn/K+oLmG2BYpYpf0CmS 3EE1SbdB/8eIhO0tc5Vyn3YegGkuAz0x0q3gGBTXvvk5ax0SffjXvgVLqb+aWcl4 srfm5RwbWYvB+vynxCZvrPNikQWEqcOcIzBTvbpdkpsBpfX2pLBaYvHv91F960vR cjbo/CU+6Z4GMCHK43RXDAoZQmbjCBuqYer/+JL9Of2w7p5SnUIrBXgnKcg/SxEg VGxVLQmLYieP =YEV2 -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2019-06-12' into staging QAPI patches for 2019-06-12 # gpg: Signature made Wed 12 Jun 2019 17:44:50 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-2019-06-12: qapi: Simplify how QAPIDoc implements its state machine file-posix: Add dynamic-auto-read-only QAPI feature qapi: Allow documentation for features qapi: Disentangle QAPIDoc code tests/qapi-schema: Error case tests for features in structs tests/qapi-schema: Test for good feature lists in structs qapi: Add feature flags to struct types block/gluster: update .help of BLOCK_OPT_PREALLOC option block/file-posix: update .help of BLOCK_OPT_PREALLOC option qapi/block-core: update documentation of preallocation parameter qdev: Delete unused LostTickPolicy "merge" Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
4747524f9f
42 changed files with 447 additions and 59 deletions
|
@ -102,6 +102,24 @@ class QAPISemError(QAPIError):
|
|||
|
||||
|
||||
class QAPIDoc(object):
|
||||
"""
|
||||
A documentation comment block, either expression or free-form
|
||||
|
||||
Expression documentation blocks consist of
|
||||
|
||||
* a body section: one line naming the expression, followed by an
|
||||
overview (any number of lines)
|
||||
|
||||
* argument sections: a description of each argument (for commands
|
||||
and events) or member (for structs, unions and alternates)
|
||||
|
||||
* features sections: a description of each feature flag
|
||||
|
||||
* additional (non-argument) sections, possibly tagged
|
||||
|
||||
Free-form documentation blocks consist only of a body section.
|
||||
"""
|
||||
|
||||
class Section(object):
|
||||
def __init__(self, name=None):
|
||||
# optional section name (argument/member or section name)
|
||||
|
@ -131,10 +149,12 @@ class QAPIDoc(object):
|
|||
self.body = QAPIDoc.Section()
|
||||
# dict mapping parameter name to ArgSection
|
||||
self.args = OrderedDict()
|
||||
self.features = OrderedDict()
|
||||
# a list of Section
|
||||
self.sections = []
|
||||
# the current section
|
||||
self._section = self.body
|
||||
self._append_line = self._append_body_line
|
||||
|
||||
def has_section(self, name):
|
||||
"""Return True if we have a section with this name."""
|
||||
|
@ -144,7 +164,16 @@ class QAPIDoc(object):
|
|||
return False
|
||||
|
||||
def append(self, line):
|
||||
"""Parse a comment line and add it to the documentation."""
|
||||
"""
|
||||
Parse a comment line and add it to the documentation.
|
||||
|
||||
The way that the line is dealt with depends on which part of
|
||||
the documentation we're parsing right now:
|
||||
* The body section: ._append_line is ._append_body_line
|
||||
* An argument section: ._append_line is ._append_args_line
|
||||
* A features section: ._append_line is ._append_features_line
|
||||
* An additional section: ._append_line is ._append_various_line
|
||||
"""
|
||||
line = line[1:]
|
||||
if not line:
|
||||
self._append_freeform(line)
|
||||
|
@ -153,54 +182,155 @@ class QAPIDoc(object):
|
|||
if line[0] != ' ':
|
||||
raise QAPIParseError(self._parser, "Missing space after #")
|
||||
line = line[1:]
|
||||
self._append_line(line)
|
||||
|
||||
def end_comment(self):
|
||||
self._end_section()
|
||||
|
||||
@staticmethod
|
||||
def _is_section_tag(name):
|
||||
return name in ('Returns:', 'Since:',
|
||||
# those are often singular or plural
|
||||
'Note:', 'Notes:',
|
||||
'Example:', 'Examples:',
|
||||
'TODO:')
|
||||
|
||||
def _append_body_line(self, line):
|
||||
"""
|
||||
Process a line of documentation text in the body section.
|
||||
|
||||
If this a symbol line and it is the section's first line, this
|
||||
is an expression documentation block for that symbol.
|
||||
|
||||
If it's an expression documentation block, another symbol line
|
||||
begins the argument section for the argument named by it, and
|
||||
a section tag begins an additional section. Start that
|
||||
section and append the line to it.
|
||||
|
||||
Else, append the line to the current section.
|
||||
"""
|
||||
name = line.split(' ', 1)[0]
|
||||
# FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
|
||||
# recognized, and get silently treated as ordinary text
|
||||
if self.symbol:
|
||||
self._append_symbol_line(line)
|
||||
elif not self.body.text and line.startswith('@'):
|
||||
if not self.symbol and not self.body.text and line.startswith('@'):
|
||||
if not line.endswith(':'):
|
||||
raise QAPIParseError(self._parser, "Line should end with :")
|
||||
self.symbol = line[1:-1]
|
||||
# FIXME invalid names other than the empty string aren't flagged
|
||||
if not self.symbol:
|
||||
raise QAPIParseError(self._parser, "Invalid name")
|
||||
elif self.symbol:
|
||||
# This is an expression documentation block
|
||||
if name.startswith('@') and name.endswith(':'):
|
||||
self._append_line = self._append_args_line
|
||||
self._append_args_line(line)
|
||||
elif line == 'Features:':
|
||||
self._append_line = self._append_features_line
|
||||
elif self._is_section_tag(name):
|
||||
self._append_line = self._append_various_line
|
||||
self._append_various_line(line)
|
||||
else:
|
||||
self._append_freeform(line.strip())
|
||||
else:
|
||||
self._append_freeform(line)
|
||||
# This is a free-form documentation block
|
||||
self._append_freeform(line.strip())
|
||||
|
||||
def end_comment(self):
|
||||
self._end_section()
|
||||
def _append_args_line(self, line):
|
||||
"""
|
||||
Process a line of documentation text in an argument section.
|
||||
|
||||
def _append_symbol_line(self, line):
|
||||
A symbol line begins the next argument section, a section tag
|
||||
section or a non-indented line after a blank line begins an
|
||||
additional section. Start that section and append the line to
|
||||
it.
|
||||
|
||||
Else, append the line to the current section.
|
||||
|
||||
"""
|
||||
name = line.split(' ', 1)[0]
|
||||
|
||||
if name.startswith('@') and name.endswith(':'):
|
||||
line = line[len(name)+1:]
|
||||
self._start_args_section(name[1:-1])
|
||||
elif name in ('Returns:', 'Since:',
|
||||
# those are often singular or plural
|
||||
'Note:', 'Notes:',
|
||||
'Example:', 'Examples:',
|
||||
'TODO:'):
|
||||
elif self._is_section_tag(name):
|
||||
self._append_line = self._append_various_line
|
||||
self._append_various_line(line)
|
||||
return
|
||||
elif (self._section.text.endswith('\n\n')
|
||||
and line and not line[0].isspace()):
|
||||
if line == 'Features:':
|
||||
self._append_line = self._append_features_line
|
||||
else:
|
||||
self._start_section()
|
||||
self._append_line = self._append_various_line
|
||||
self._append_various_line(line)
|
||||
return
|
||||
|
||||
self._append_freeform(line.strip())
|
||||
|
||||
def _append_features_line(self, line):
|
||||
name = line.split(' ', 1)[0]
|
||||
|
||||
if name.startswith('@') and name.endswith(':'):
|
||||
line = line[len(name)+1:]
|
||||
self._start_features_section(name[1:-1])
|
||||
elif self._is_section_tag(name):
|
||||
self._append_line = self._append_various_line
|
||||
self._append_various_line(line)
|
||||
return
|
||||
elif (self._section.text.endswith('\n\n')
|
||||
and line and not line[0].isspace()):
|
||||
self._start_section()
|
||||
self._append_line = self._append_various_line
|
||||
self._append_various_line(line)
|
||||
return
|
||||
|
||||
self._append_freeform(line.strip())
|
||||
|
||||
def _append_various_line(self, line):
|
||||
"""
|
||||
Process a line of documentation text in an additional section.
|
||||
|
||||
A symbol line is an error.
|
||||
|
||||
A section tag begins an additional section. Start that
|
||||
section and append the line to it.
|
||||
|
||||
Else, append the line to the current section.
|
||||
"""
|
||||
name = line.split(' ', 1)[0]
|
||||
|
||||
if name.startswith('@') and name.endswith(':'):
|
||||
raise QAPIParseError(self._parser,
|
||||
"'%s' can't follow '%s' section"
|
||||
% (name, self.sections[0].name))
|
||||
elif self._is_section_tag(name):
|
||||
line = line[len(name)+1:]
|
||||
self._start_section(name[:-1])
|
||||
|
||||
if (not self._section.name or
|
||||
not self._section.name.startswith('Example')):
|
||||
line = line.strip()
|
||||
|
||||
self._append_freeform(line)
|
||||
|
||||
def _start_args_section(self, name):
|
||||
def _start_symbol_section(self, symbols_dict, name):
|
||||
# FIXME invalid names other than the empty string aren't flagged
|
||||
if not name:
|
||||
raise QAPIParseError(self._parser, "Invalid parameter name")
|
||||
if name in self.args:
|
||||
if name in symbols_dict:
|
||||
raise QAPIParseError(self._parser,
|
||||
"'%s' parameter name duplicated" % name)
|
||||
if self.sections:
|
||||
raise QAPIParseError(self._parser,
|
||||
"'@%s:' can't follow '%s' section"
|
||||
% (name, self.sections[0].name))
|
||||
assert not self.sections
|
||||
self._end_section()
|
||||
self._section = QAPIDoc.ArgSection(name)
|
||||
self.args[name] = self._section
|
||||
symbols_dict[name] = self._section
|
||||
|
||||
def _start_args_section(self, name):
|
||||
self._start_symbol_section(self.args, name)
|
||||
|
||||
def _start_features_section(self, name):
|
||||
self._start_symbol_section(self.features, name)
|
||||
|
||||
def _start_section(self, name=None):
|
||||
if name in ('Returns', 'Since') and self.has_section(name):
|
||||
|
@ -219,13 +349,6 @@ class QAPIDoc(object):
|
|||
self._section = None
|
||||
|
||||
def _append_freeform(self, line):
|
||||
in_arg = isinstance(self._section, QAPIDoc.ArgSection)
|
||||
if (in_arg and self._section.text.endswith('\n\n')
|
||||
and line and not line[0].isspace()):
|
||||
self._start_section()
|
||||
if (in_arg or not self._section.name
|
||||
or not self._section.name.startswith('Example')):
|
||||
line = line.strip()
|
||||
match = re.match(r'(@\S+:)', line)
|
||||
if match:
|
||||
raise QAPIParseError(self._parser,
|
||||
|
@ -886,12 +1009,26 @@ def check_enum(expr, info):
|
|||
def check_struct(expr, info):
|
||||
name = expr['struct']
|
||||
members = expr['data']
|
||||
features = expr.get('features')
|
||||
|
||||
check_type(info, "'data' for struct '%s'" % name, members,
|
||||
allow_dict=True, allow_optional=True)
|
||||
check_type(info, "'base' for struct '%s'" % name, expr.get('base'),
|
||||
allow_metas=['struct'])
|
||||
|
||||
if features:
|
||||
if not isinstance(features, list):
|
||||
raise QAPISemError(info,
|
||||
"Struct '%s' requires an array for 'features'" %
|
||||
name)
|
||||
for f in features:
|
||||
assert isinstance(f, dict)
|
||||
check_known_keys(info, "feature of struct %s" % name, f,
|
||||
['name'], ['if'])
|
||||
|
||||
check_if(f, info)
|
||||
check_name(info, "Feature of struct %s" % name, f['name'])
|
||||
|
||||
|
||||
def check_known_keys(info, source, keys, required, optional):
|
||||
|
||||
|
@ -948,6 +1085,12 @@ def normalize_members(members):
|
|||
members[key] = {'type': arg}
|
||||
|
||||
|
||||
def normalize_features(features):
|
||||
if isinstance(features, list):
|
||||
features[:] = [f if isinstance(f, dict) else {'name': f}
|
||||
for f in features]
|
||||
|
||||
|
||||
def check_exprs(exprs):
|
||||
global all_names
|
||||
|
||||
|
@ -986,8 +1129,10 @@ def check_exprs(exprs):
|
|||
normalize_members(expr['data'])
|
||||
elif 'struct' in expr:
|
||||
meta = 'struct'
|
||||
check_keys(expr_elem, 'struct', ['data'], ['base', 'if'])
|
||||
check_keys(expr_elem, 'struct', ['data'],
|
||||
['base', 'if', 'features'])
|
||||
normalize_members(expr['data'])
|
||||
normalize_features(expr.get('features'))
|
||||
struct_types[expr[meta]] = expr
|
||||
elif 'command' in expr:
|
||||
meta = 'command'
|
||||
|
@ -1126,10 +1271,12 @@ class QAPISchemaVisitor(object):
|
|||
def visit_array_type(self, name, info, ifcond, element_type):
|
||||
pass
|
||||
|
||||
def visit_object_type(self, name, info, ifcond, base, members, variants):
|
||||
def visit_object_type(self, name, info, ifcond, base, members, variants,
|
||||
features):
|
||||
pass
|
||||
|
||||
def visit_object_type_flat(self, name, info, ifcond, members, variants):
|
||||
def visit_object_type_flat(self, name, info, ifcond, members, variants,
|
||||
features):
|
||||
pass
|
||||
|
||||
def visit_alternate_type(self, name, info, ifcond, variants):
|
||||
|
@ -1290,7 +1437,7 @@ class QAPISchemaArrayType(QAPISchemaType):
|
|||
|
||||
class QAPISchemaObjectType(QAPISchemaType):
|
||||
def __init__(self, name, info, doc, ifcond,
|
||||
base, local_members, variants):
|
||||
base, local_members, variants, features):
|
||||
# struct has local_members, optional base, and no variants
|
||||
# flat union has base, variants, and no local_members
|
||||
# simple union has local_members, variants, and no base
|
||||
|
@ -1302,11 +1449,15 @@ class QAPISchemaObjectType(QAPISchemaType):
|
|||
if variants is not None:
|
||||
assert isinstance(variants, QAPISchemaObjectTypeVariants)
|
||||
variants.set_owner(name)
|
||||
for f in features:
|
||||
assert isinstance(f, QAPISchemaFeature)
|
||||
f.set_owner(name)
|
||||
self._base_name = base
|
||||
self.base = None
|
||||
self.local_members = local_members
|
||||
self.variants = variants
|
||||
self.members = None
|
||||
self.features = features
|
||||
|
||||
def check(self, schema):
|
||||
QAPISchemaType.check(self, schema)
|
||||
|
@ -1332,6 +1483,12 @@ class QAPISchemaObjectType(QAPISchemaType):
|
|||
self.variants.check(schema, seen)
|
||||
assert self.variants.tag_member in self.members
|
||||
self.variants.check_clash(self.info, seen)
|
||||
|
||||
# Features are in a name space separate from members
|
||||
seen = {}
|
||||
for f in self.features:
|
||||
f.check_clash(self.info, seen)
|
||||
|
||||
if self.doc:
|
||||
self.doc.check()
|
||||
|
||||
|
@ -1368,12 +1525,15 @@ class QAPISchemaObjectType(QAPISchemaType):
|
|||
|
||||
def visit(self, visitor):
|
||||
visitor.visit_object_type(self.name, self.info, self.ifcond,
|
||||
self.base, self.local_members, self.variants)
|
||||
self.base, self.local_members, self.variants,
|
||||
self.features)
|
||||
visitor.visit_object_type_flat(self.name, self.info, self.ifcond,
|
||||
self.members, self.variants)
|
||||
self.members, self.variants,
|
||||
self.features)
|
||||
|
||||
|
||||
class QAPISchemaMember(object):
|
||||
""" Represents object members, enum members and features """
|
||||
role = 'member'
|
||||
|
||||
def __init__(self, name, ifcond=None):
|
||||
|
@ -1419,6 +1579,10 @@ class QAPISchemaMember(object):
|
|||
return "'%s' %s" % (self.name, self._pretty_owner())
|
||||
|
||||
|
||||
class QAPISchemaFeature(QAPISchemaMember):
|
||||
role = 'feature'
|
||||
|
||||
|
||||
class QAPISchemaObjectTypeMember(QAPISchemaMember):
|
||||
def __init__(self, name, typ, optional, ifcond=None):
|
||||
QAPISchemaMember.__init__(self, name, ifcond)
|
||||
|
@ -1675,7 +1839,7 @@ class QAPISchema(object):
|
|||
('null', 'null', 'QNull' + pointer_suffix)]:
|
||||
self._def_builtin_type(*t)
|
||||
self.the_empty_object_type = QAPISchemaObjectType(
|
||||
'q_empty', None, None, None, None, [], None)
|
||||
'q_empty', None, None, None, None, [], None, [])
|
||||
self._def_entity(self.the_empty_object_type)
|
||||
|
||||
qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
|
||||
|
@ -1685,6 +1849,9 @@ class QAPISchema(object):
|
|||
self._def_entity(QAPISchemaEnumType('QType', None, None, None,
|
||||
qtype_values, 'QTYPE'))
|
||||
|
||||
def _make_features(self, features):
|
||||
return [QAPISchemaFeature(f['name'], f.get('if')) for f in features]
|
||||
|
||||
def _make_enum_members(self, values):
|
||||
return [QAPISchemaMember(v['name'], v.get('if')) for v in values]
|
||||
|
||||
|
@ -1721,7 +1888,7 @@ class QAPISchema(object):
|
|||
assert ifcond == typ._ifcond # pylint: disable=protected-access
|
||||
else:
|
||||
self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond,
|
||||
None, members, None))
|
||||
None, members, None, []))
|
||||
return name
|
||||
|
||||
def _def_enum_type(self, expr, info, doc):
|
||||
|
@ -1752,9 +1919,11 @@ class QAPISchema(object):
|
|||
base = expr.get('base')
|
||||
data = expr['data']
|
||||
ifcond = expr.get('if')
|
||||
features = expr.get('features', [])
|
||||
self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, base,
|
||||
self._make_members(data, info),
|
||||
None))
|
||||
None,
|
||||
self._make_features(features)))
|
||||
|
||||
def _make_variant(self, case, typ, ifcond):
|
||||
return QAPISchemaObjectTypeVariant(case, typ, ifcond)
|
||||
|
@ -1795,7 +1964,7 @@ class QAPISchema(object):
|
|||
QAPISchemaObjectType(name, info, doc, ifcond, base, members,
|
||||
QAPISchemaObjectTypeVariants(tag_name,
|
||||
tag_member,
|
||||
variants)))
|
||||
variants), []))
|
||||
|
||||
def _def_alternate_type(self, expr, info, doc):
|
||||
name = expr['alternate']
|
||||
|
|
|
@ -182,6 +182,17 @@ def texi_members(doc, what, base, variants, member_func):
|
|||
return '\n@b{%s:}\n@table @asis\n%s@end table\n' % (what, items)
|
||||
|
||||
|
||||
def texi_features(doc):
|
||||
"""Format the table of features"""
|
||||
items = ''
|
||||
for section in doc.features.values():
|
||||
desc = texi_format(section.text)
|
||||
items += '@item @code{%s}\n%s' % (section.name, desc)
|
||||
if not items:
|
||||
return ''
|
||||
return '\n@b{Features:}\n@table @asis\n%s@end table\n' % (items)
|
||||
|
||||
|
||||
def texi_sections(doc, ifcond):
|
||||
"""Format additional sections following arguments"""
|
||||
body = ''
|
||||
|
@ -201,6 +212,7 @@ def texi_entity(doc, what, ifcond, base=None, variants=None,
|
|||
member_func=texi_member):
|
||||
return (texi_body(doc)
|
||||
+ texi_members(doc, what, base, variants, member_func)
|
||||
+ texi_features(doc)
|
||||
+ texi_sections(doc, ifcond))
|
||||
|
||||
|
||||
|
@ -220,7 +232,8 @@ class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor):
|
|||
body=texi_entity(doc, 'Values', ifcond,
|
||||
member_func=texi_enum_value)))
|
||||
|
||||
def visit_object_type(self, name, info, ifcond, base, members, variants):
|
||||
def visit_object_type(self, name, info, ifcond, base, members, variants,
|
||||
features):
|
||||
doc = self.cur_doc
|
||||
if base and base.is_implicit():
|
||||
base = None
|
||||
|
|
|
@ -188,11 +188,15 @@ const QLitObject %(c_name)s = %(c_string)s;
|
|||
self._gen_qlit('[' + element + ']', 'array', {'element-type': element},
|
||||
ifcond)
|
||||
|
||||
def visit_object_type_flat(self, name, info, ifcond, members, variants):
|
||||
def visit_object_type_flat(self, name, info, ifcond, members, variants,
|
||||
features):
|
||||
obj = {'members': [self._gen_member(m) for m in members]}
|
||||
if variants:
|
||||
obj.update(self._gen_variants(variants.tag_member.name,
|
||||
variants.variants))
|
||||
if features:
|
||||
obj['features'] = [(f.name, {'if': f.ifcond}) for f in features]
|
||||
|
||||
self._gen_qlit(name, 'object', obj, ifcond)
|
||||
|
||||
def visit_alternate_type(self, name, info, ifcond, variants):
|
||||
|
|
|
@ -227,7 +227,8 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor):
|
|||
self._genh.add(gen_array(name, element_type))
|
||||
self._gen_type_cleanup(name)
|
||||
|
||||
def visit_object_type(self, name, info, ifcond, base, members, variants):
|
||||
def visit_object_type(self, name, info, ifcond, base, members, variants,
|
||||
features):
|
||||
# Nothing to do for the special empty builtin
|
||||
if name == 'q_empty':
|
||||
return
|
||||
|
|
|
@ -324,7 +324,8 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor):
|
|||
self._genh.add(gen_visit_decl(name))
|
||||
self._genc.add(gen_visit_list(name, element_type))
|
||||
|
||||
def visit_object_type(self, name, info, ifcond, base, members, variants):
|
||||
def visit_object_type(self, name, info, ifcond, base, members, variants,
|
||||
features):
|
||||
# Nothing to do for the special empty builtin
|
||||
if name == 'q_empty':
|
||||
return
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue