Example #1
0
 def check(self, schema):
     QAPISchemaType.check(self, schema)
     self.variants.tag_member.check(schema)
     # Not calling self.variants.check_clash(), because there's nothing
     # to clash with
     self.variants.check(schema, {})
     # Alternate branch names have no relation to the tag enum values;
     # so we have to check for potential name collisions ourselves.
     seen = {}
     types_seen = {}
     for v in self.variants.variants:
         v.check_clash(self.info, seen)
         qtype = v.type.alternate_qtype()
         if not qtype:
             raise QAPISemError(
                 self.info, "%s cannot use %s" %
                 (v.describe(self.info), v.type.describe()))
         conflicting = set([qtype])
         if qtype == 'QTYPE_QSTRING':
             if isinstance(v.type, QAPISchemaEnumType):
                 for m in v.type.members:
                     if m.name in ['on', 'off']:
                         conflicting.add('QTYPE_QBOOL')
                     if re.match(r'[-+0-9.]', m.name):
                         # lazy, could be tightened
                         conflicting.add('QTYPE_QNUM')
             else:
                 conflicting.add('QTYPE_QNUM')
                 conflicting.add('QTYPE_QBOOL')
         for qt in conflicting:
             if qt in types_seen:
                 raise QAPISemError(
                     self.info, "%s can't be distinguished from '%s'" %
                     (v.describe(self.info), types_seen[qt]))
             types_seen[qt] = v.name
Example #2
0
    def check(self, schema):
        QAPISchemaEntity.check(self, schema)
        if self._arg_type_name:
            self.arg_type = schema.resolve_type(self._arg_type_name, self.info,
                                                "command's 'data'")
            if not isinstance(self.arg_type, QAPISchemaObjectType):
                raise QAPISemError(
                    self.info, "command's 'data' cannot take %s" %
                    self.arg_type.describe())
            if self.arg_type.variants and not self.boxed:
                raise QAPISemError(
                    self.info,
                    "command's 'data' can take %s only with 'boxed': true" %
                    self.arg_type.describe())
        if self._ret_type_name:
            self.ret_type = schema.resolve_type(self._ret_type_name, self.info,
                                                "command's 'returns'")
            if self.name not in self.info.pragma.returns_whitelist:
                if not (isinstance(self.ret_type, QAPISchemaObjectType) or
                        (isinstance(self.ret_type, QAPISchemaArrayType)
                         and isinstance(self.ret_type.element_type,
                                        QAPISchemaObjectType))):
                    raise QAPISemError(
                        self.info, "command's 'returns' cannot take %s" %
                        self.ret_type.describe())

        # Features are in a name space separate from members
        seen = {}
        for f in self.features:
            f.check_clash(self.info, seen)
Example #3
0
 def check(self, schema):
     super().check(schema)
     if self._arg_type_name:
         self.arg_type = schema.resolve_type(
             self._arg_type_name, self.info, "command's 'data'")
         if not isinstance(self.arg_type, QAPISchemaObjectType):
             raise QAPISemError(
                 self.info,
                 "command's 'data' cannot take %s"
                 % self.arg_type.describe())
         if self.arg_type.variants and not self.boxed:
             raise QAPISemError(
                 self.info,
                 "command's 'data' can take %s only with 'boxed': true"
                 % self.arg_type.describe())
     if self._ret_type_name:
         self.ret_type = schema.resolve_type(
             self._ret_type_name, self.info, "command's 'returns'")
         if self.name not in self.info.pragma.returns_whitelist:
             typ = self.ret_type
             if isinstance(typ, QAPISchemaArrayType):
                 typ = self.ret_type.element_type
                 assert typ
             if not isinstance(typ, QAPISchemaObjectType):
                 raise QAPISemError(
                     self.info,
                     "command's 'returns' cannot take %s"
                     % self.ret_type.describe())
Example #4
0
def check_flags(expr, info):
    for key in ['gen', 'success-response']:
        if key in expr and expr[key] is not False:
            raise QAPISemError(info,
                               "flag '%s' may only use false value" % key)
    for key in ['boxed', 'allow-oob', 'allow-preconfig']:
        if key in expr and expr[key] is not True:
            raise QAPISemError(info, "flag '%s' may only use true value" % key)
Example #5
0
 def check_if_str(ifcond, info):
     if not isinstance(ifcond, str):
         raise QAPISemError(
             info,
             "'if' condition of %s must be a string or a list of strings" %
             source)
     if ifcond.strip() == '':
         raise QAPISemError(
             info,
             "'if' condition '%s' of %s makes no sense" % (ifcond, source))
Example #6
0
 def check(self, schema):
     QAPISchemaEntity.check(self, schema)
     if self._arg_type_name:
         self.arg_type = schema.resolve_type(self._arg_type_name, self.info,
                                             "event's 'data'")
         if not isinstance(self.arg_type, QAPISchemaObjectType):
             raise QAPISemError(
                 self.info,
                 "event's 'data' cannot take %s" % self.arg_type.describe())
         if self.arg_type.variants and not self.boxed:
             raise QAPISemError(
                 self.info,
                 "event's 'data' can take %s only with 'boxed': true" %
                 self.arg_type.describe())
Example #7
0
 def check_clash(self, info, seen):
     cname = c_name(self.name)
     if cname in seen:
         raise QAPISemError(
             info, "%s collides with %s" %
             (self.describe(info), seen[cname].describe(info)))
     seen[cname] = self
Example #8
0
 def connect_feature(self, feature):
     if feature.name not in self.features:
         raise QAPISemError(
             feature.info,
             "feature '%s' lacks documentation" % feature.name)
         self.features[feature.name] = QAPIDoc.ArgSection(feature.name)
     self.features[feature.name].connect(feature)
Example #9
0
def check_if(expr, info, source):

    def check_if_str(ifcond, info):
        if not isinstance(ifcond, str):
            raise QAPISemError(
                info,
                "'if' condition of %s must be a string or a list of strings"
                % source)
        if ifcond.strip() == '':
            raise QAPISemError(
                info,
                "'if' condition '%s' of %s makes no sense"
                % (ifcond, source))

    ifcond = expr.get('if')
    if ifcond is None:
        return
    if isinstance(ifcond, list):
        if ifcond == []:
            raise QAPISemError(
                info, "'if' condition [] of %s is useless" % source)
        for elt in ifcond:
            check_if_str(elt, info)
    else:
        check_if_str(ifcond, info)
        expr['if'] = [ifcond]
Example #10
0
def check_flags(expr, info):
    for key in ['gen', 'success-response']:
        if key in expr and expr[key] is not False:
            raise QAPISemError(
                info, "flag '%s' may only use false value" % key)
    for key in ['boxed', 'allow-oob', 'allow-preconfig', 'coroutine']:
        if key in expr and expr[key] is not True:
            raise QAPISemError(
                info, "flag '%s' may only use true value" % key)
    if 'allow-oob' in expr and 'coroutine' in expr:
        # This is not necessarily a fundamental incompatibility, but
        # we don't have a use case and the desired semantics isn't
        # obvious.  The simplest solution is to forbid it until we get
        # a use case for it.
        raise QAPISemError(info, "flags 'allow-oob' and 'coroutine' "
                                 "are incompatible")
Example #11
0
def check_event(expr, info):
    args = expr.get('data')
    boxed = expr.get('boxed', False)

    if boxed and args is None:
        raise QAPISemError(info, "'boxed': true requires 'data'")
    check_type(args, info, "'data'", allow_dict=not boxed)
Example #12
0
def check_keys(value, info, source, required, optional):
    def pprint(elems):
        return ', '.join("'" + e + "'" for e in sorted(elems))

    missing = set(required) - set(value)
    if missing:
        raise QAPISemError(
            info, "%s misses key%s %s" %
            (source, 's' if len(missing) > 1 else '', pprint(missing)))
    allowed = set(required + optional)
    unknown = set(value) - allowed
    if unknown:
        raise QAPISemError(
            info, "%s has unknown key%s %s\nValid keys are %s." %
            (source, 's' if len(unknown) > 1 else '', pprint(unknown),
             pprint(allowed)))
Example #13
0
 def check(self):
     bogus = [
         name for name, section in self.args.items() if not section.member
     ]
     if bogus:
         raise QAPISemError(
             self.info, "the following documented members are not in "
             "the declaration: %s" % ", ".join(bogus))
Example #14
0
    def check(self, schema):
        # This calls another type T's .check() exactly when the C
        # struct emitted by gen_object() contains that T's C struct
        # (pointers don't count).
        if self.members is not None:
            # A previous .check() completed: nothing to do
            return
        if self._checked:
            # Recursed: C struct contains itself
            raise QAPISemError(self.info,
                               "object %s contains itself" % self.name)

        QAPISchemaType.check(self, schema)
        assert self._checked and self.members is None

        seen = OrderedDict()
        if self._base_name:
            self.base = schema.resolve_type(self._base_name, self.info,
                                            "'base'")
            if (not isinstance(self.base, QAPISchemaObjectType)
                    or self.base.variants):
                raise QAPISemError(
                    self.info, "'base' requires a struct type, %s isn't" %
                    self.base.describe())
            self.base.check(schema)
            self.base.check_clash(self.info, seen)
        for m in self.local_members:
            m.check(schema)
            m.check_clash(self.info, seen)
            if self.doc:
                self.doc.connect_member(m)
        members = seen.values()

        if self.variants:
            self.variants.check(schema, seen)
            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()

        self.members = members  # mark completed
Example #15
0
 def resolve_type(self, name, info, what):
     typ = self.lookup_type(name)
     if not typ:
         if callable(what):
             what = what(info)
         raise QAPISemError(
             info, "%s uses unknown type '%s'" % (what, name))
     return typ
Example #16
0
 def check(self, schema, seen):
     if not self.tag_member:  # flat union
         self.tag_member = seen.get(c_name(self._tag_name))
         base = "'base'"
         # Pointing to the base type when not implicit would be
         # nice, but we don't know it here
         if not self.tag_member or self._tag_name != self.tag_member.name:
             raise QAPISemError(
                 self.info, "discriminator '%s' is not a member of %s" %
                 (self._tag_name, base))
         # Here we do:
         base_type = schema.lookup_type(self.tag_member.defined_in)
         assert base_type
         if not base_type.is_implicit():
             base = "base type '%s'" % self.tag_member.defined_in
         if not isinstance(self.tag_member.type, QAPISchemaEnumType):
             raise QAPISemError(
                 self.info,
                 "discriminator member '%s' of %s must be of enum type" %
                 (self._tag_name, base))
         if self.tag_member.optional:
             raise QAPISemError(
                 self.info,
                 "discriminator member '%s' of %s must not be optional" %
                 (self._tag_name, base))
         if self.tag_member.ifcond:
             raise QAPISemError(
                 self.info,
                 "discriminator member '%s' of %s must not be conditional" %
                 (self._tag_name, base))
     else:  # simple union
         assert isinstance(self.tag_member.type, QAPISchemaEnumType)
         assert not self.tag_member.optional
         assert self.tag_member.ifcond == []
     if self._tag_name:  # flat union
         # branches that are not explicitly covered get an empty type
         cases = set([v.name for v in self.variants])
         for m in self.tag_member.type.members:
             if m.name not in cases:
                 v = QAPISchemaObjectTypeVariant(m.name, self.info,
                                                 'q_empty', m.ifcond)
                 v.set_defined_in(self.tag_member.defined_in)
                 self.variants.append(v)
     if not self.variants:
         raise QAPISemError(self.info, "union has no branches")
     for v in self.variants:
         v.check(schema)
         # Union names must match enum values; alternate names are
         # checked separately. Use 'seen' to tell the two apart.
         if seen:
             if v.name not in self.tag_member.type.member_names():
                 raise QAPISemError(
                     self.info, "branch '%s' is not a value of %s" %
                     (v.name, self.tag_member.type.describe()))
             if (not isinstance(v.type, QAPISchemaObjectType)
                     or v.type.variants):
                 raise QAPISemError(
                     self.info, "%s cannot use %s" %
                     (v.describe(self.info), v.type.describe()))
             v.type.check(schema)
Example #17
0
 def check_args_section(args, info, what):
     bogus = [
         name for name, section in args.items() if not section.member
     ]
     if bogus:
         raise QAPISemError(
             self.info, "documented member%s '%s' %s not exist" %
             ("s" if len(bogus) > 1 else "", "', '".join(bogus),
              "do" if len(bogus) > 1 else "does"))
Example #18
0
 def _def_entity(self, ent):
     # Only the predefined types are allowed to not have info
     assert ent.info or self._predefining
     self._entity_list.append(ent)
     if ent.name is None:
         return
     # TODO reject names that differ only in '_' vs. '.'  vs. '-',
     # because they're liable to clash in generated C.
     other_ent = self._entity_dict.get(ent.name)
     if other_ent:
         if other_ent.info:
             where = QAPIError(other_ent.info, None, "previous definition")
             raise QAPISemError(
                 ent.info,
                 "'%s' is already defined\n%s" % (ent.name, where))
         raise QAPISemError(
             ent.info, "%s is already defined" % other_ent.describe())
     self._entity_dict[ent.name] = ent
Example #19
0
def check_command(expr, info):
    args = expr.get('data')
    rets = expr.get('returns')
    boxed = expr.get('boxed', False)

    if boxed and args is None:
        raise QAPISemError(info, "'boxed': true requires 'data'")
    check_type(args, info, "'data'", allow_dict=not boxed)
    check_type(rets, info, "'returns'", allow_array=True)
Example #20
0
def check_type(value, info, source, allow_array=False, allow_dict=False):
    if value is None:
        return

    # Array type
    if isinstance(value, list):
        if not allow_array:
            raise QAPISemError(info, "%s cannot be an array" % source)
        if len(value) != 1 or not isinstance(value[0], str):
            raise QAPISemError(
                info, "%s: array type must contain single type name" % source)
        return

    # Type name
    if isinstance(value, str):
        return

    # Anonymous type

    if not allow_dict:
        raise QAPISemError(info, "%s should be a type name" % source)

    if not isinstance(value, OrderedDict):
        raise QAPISemError(info,
                           "%s should be an object or type name" % source)

    permit_upper = allow_dict in info.pragma.name_case_whitelist

    # value is a dictionary, check that each member is okay
    for (key, arg) in value.items():
        key_source = "%s member '%s'" % (source, key)
        check_name_str(key,
                       info,
                       key_source,
                       allow_optional=True,
                       permit_upper=permit_upper)
        if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
            raise QAPISemError(info, "%s uses reserved name" % key_source)
        check_keys(arg, info, key_source, ['type'], ['if', 'features'])
        check_if(arg, info, key_source)
        check_features(arg.get('features'), info)
        check_type(arg['type'], info, key_source, allow_array=True)
Example #21
0
def check_alternate(expr, info):
    members = expr['data']

    if not members:
        raise QAPISemError(info, "'data' must not be empty")
    for (key, value) in members.items():
        source = "'data' member '%s'" % key
        check_name_str(key, info, source)
        check_keys(value, info, source, ['type'], ['if'])
        check_if(value, info, source)
        check_type(value['type'], info, source)
Example #22
0
def check_name_str(name, info, source,
                   allow_optional=False, enum_member=False,
                   permit_upper=False):
    membername = name

    if allow_optional and name.startswith('*'):
        membername = name[1:]
    # Enum members can start with a digit, because the generated C
    # code always prefixes it with the enum name
    if enum_member and membername[0].isdigit():
        membername = 'D' + membername
    # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
    # and 'q_obj_*' implicit type names.
    if not valid_name.match(membername) or \
       c_name(membername, False).startswith('q_'):
        raise QAPISemError(info, "%s has an invalid name" % source)
    if not permit_upper and name.lower() != name:
        raise QAPISemError(
            info, "%s uses uppercase in name" % source)
    assert not membername.startswith('*')
Example #23
0
def check_union(expr, info):
    name = expr['union']
    base = expr.get('base')
    discriminator = expr.get('discriminator')
    members = expr['data']

    if discriminator is None:   # simple union
        if base is not None:
            raise QAPISemError(info, "'base' requires 'discriminator'")
    else:                       # flat union
        check_type(base, info, "'base'", allow_dict=name)
        if not base:
            raise QAPISemError(info, "'discriminator' requires 'base'")
        check_name_is_str(discriminator, info, "'discriminator'")

    for (key, value) in members.items():
        source = "'data' member '%s'" % key
        check_name_str(key, info, source)
        check_keys(value, info, source, ['type'], ['if'])
        check_if(value, info, source)
        check_type(value['type'], info, source, allow_array=not base)
Example #24
0
 def _pragma(self, name, value, info):
     if name == 'doc-required':
         if not isinstance(value, bool):
             raise QAPISemError(info,
                                "pragma 'doc-required' must be boolean")
         info.pragma.doc_required = value
     elif name == 'returns-whitelist':
         if (not isinstance(value, list)
                 or any([not isinstance(elt, str) for elt in value])):
             raise QAPISemError(
                 info, "pragma returns-whitelist must be a list of strings")
         info.pragma.returns_whitelist = value
     elif name == 'name-case-whitelist':
         if (not isinstance(value, list)
                 or any([not isinstance(elt, str) for elt in value])):
             raise QAPISemError(
                 info,
                 "pragma name-case-whitelist must be a list of strings")
         info.pragma.name_case_whitelist = value
     else:
         raise QAPISemError(info, "unknown pragma '%s'" % name)
Example #25
0
def check_enum(expr, info):
    name = expr['enum']
    members = expr['data']
    prefix = expr.get('prefix')

    if not isinstance(members, list):
        raise QAPISemError(info, "'data' must be an array")
    if prefix is not None and not isinstance(prefix, str):
        raise QAPISemError(info, "'prefix' must be a string")

    permit_upper = name in info.pragma.name_case_whitelist

    members[:] = [m if isinstance(m, dict) else {'name': m}
                  for m in members]
    for member in members:
        source = "'data' member"
        check_keys(member, info, source, ['name'], ['if'])
        check_name_is_str(member['name'], info, source)
        source = "%s '%s'" % (source, member['name'])
        check_name_str(member['name'], info, source,
                       enum_member=True, permit_upper=permit_upper)
        check_if(member, info, source)
Example #26
0
def check_features(features, info):
    if features is None:
        return
    if not isinstance(features, list):
        raise QAPISemError(info, "'features' must be an array")
    features[:] = [f if isinstance(f, dict) else {'name': f} for f in features]
    for f in features:
        source = "'features' member"
        assert isinstance(f, dict)
        check_keys(f, info, source, ['name'], ['if'])
        check_name_is_str(f['name'], info, source)
        source = "%s '%s'" % (source, f['name'])
        check_name_str(f['name'], info, source)
        check_if(f, info, source)
Example #27
0
    def _include(self, include, info, incl_fname, previously_included):
        incl_abs_fname = os.path.abspath(incl_fname)
        # catch inclusion cycle
        inf = info
        while inf:
            if incl_abs_fname == os.path.abspath(inf.fname):
                raise QAPISemError(info, "inclusion loop for %s" % include)
            inf = inf.parent

        # skip multiple include of the same file
        if incl_abs_fname in previously_included:
            return None

        return QAPISchemaParser(incl_fname, previously_included, info)
Example #28
0
    def _start_new_heading(self, heading, level):
        """Start a new heading at the specified heading level

        Create a new section whose title is 'heading' and which is placed
        in the docutils node tree as a child of the most recent level-1
        heading. Subsequent document sections (commands, freeform doc chunks,
        etc) will be placed as children of this new heading section.
        """
        if len(self._active_headings) < level:
            raise QAPISemError(
                self._cur_doc.info, 'Level %d subheading found outside a '
                'level %d heading' % (level, level - 1))
        snode = self._make_section(heading)
        self._active_headings[level - 1] += snode
        self._active_headings = self._active_headings[:level]
        self._active_headings.append(snode)
Example #29
0
def check_defn_name_str(name, info, meta):
    check_name_str(name, info, meta, permit_upper=True)
    if name.endswith('Kind') or name.endswith('List'):
        raise QAPISemError(
            info, "%s name should not end in '%s'" % (meta, name[-4:]))
Example #30
0
def check_name_is_str(name, info, source):
    if not isinstance(name, str):
        raise QAPISemError(info, "%s requires a string name" % source)