qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH 13/19] qapi/schema: fix typing for QAPISchemaVariants.tag_mem


From: John Snow
Subject: Re: [PATCH 13/19] qapi/schema: fix typing for QAPISchemaVariants.tag_member
Date: Thu, 1 Feb 2024 15:54:25 -0500

On Wed, Jan 17, 2024 at 5:53 AM Markus Armbruster <armbru@redhat.com> wrote:
>
> Still more...
>

Would you hate me if I suggested that we punt this to after type
checking is applied? i.e. let's do the stupid @property thing for now,
and we'll rebase this proposal and put it in right afterwards.

(Admittedly, it's just easier for me to review the impact on the
typing work if I already have that baseline to work from...)

> diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py
> index 658c288f8f..4a2e62d919 100644
> --- a/docs/sphinx/qapidoc.py
> +++ b/docs/sphinx/qapidoc.py
> @@ -328,7 +328,8 @@ def visit_object_type(self, name, info, ifcond, features,
>                        + self._nodes_for_sections(doc)
>                        + self._nodes_for_if_section(ifcond))
>
> -    def visit_alternate_type(self, name, info, ifcond, features, variants):
> +    def visit_alternate_type(self, name, info, ifcond, features,
> +                             alternatives):
>          doc = self._cur_doc
>          self._add_doc('Alternate',
>                        self._nodes_for_members(doc, 'Members')
> diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py
> index c38df61a6d..e5cea8004e 100644
> --- a/scripts/qapi/introspect.py
> +++ b/scripts/qapi/introspect.py
> @@ -26,6 +26,7 @@
>  from .gen import QAPISchemaMonolithicCVisitor
>  from .schema import (
>      QAPISchema,
> +    QAPISchemaAlternatives,
>      QAPISchemaArrayType,
>      QAPISchemaBuiltinType,
>      QAPISchemaEntity,
> @@ -343,12 +344,12 @@ def visit_object_type_flat(self, name: str, info: 
> Optional[QAPISourceInfo],
>      def visit_alternate_type(self, name: str, info: Optional[QAPISourceInfo],
>                               ifcond: QAPISchemaIfCond,
>                               features: List[QAPISchemaFeature],
> -                             variants: QAPISchemaVariants) -> None:
> +                             alternatives: QAPISchemaAlternatives) -> None:
>          self._gen_tree(
>              name, 'alternate',
>              {'members': [Annotated({'type': self._use_type(m.type)},
>                                     m.ifcond)
> -                         for m in variants.variants]},
> +                         for m in alternatives.variants]},
>              ifcond, features
>          )
>
> diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
> index 0d9a70ab4c..f18aac7199 100644
> --- a/scripts/qapi/schema.py
> +++ b/scripts/qapi/schema.py
> @@ -187,7 +187,8 @@ def visit_object_type_flat(self, name, info, ifcond, 
> features,
>                                 members, variants):
>          pass
>
> -    def visit_alternate_type(self, name, info, ifcond, features, variants):
> +    def visit_alternate_type(self, name, info, ifcond, features,
> +                             alternatives):
>          pass
>
>      def visit_command(self, name, info, ifcond, features,
> @@ -563,8 +564,7 @@ class QAPISchemaAlternateType(QAPISchemaType):
>
>      def __init__(self, name, info, doc, ifcond, features, variants):
>          super().__init__(name, info, doc, ifcond, features)
> -        assert isinstance(variants, QAPISchemaVariants)
> -        assert variants.tag_member
> +        assert isinstance(variants, QAPISchemaAlternatives)
>          variants.set_defined_in(name)
>          variants.tag_member.set_defined_in(self.name)
>          self.variants = variants
> @@ -625,19 +625,12 @@ def visit(self, visitor):
>              self.name, self.info, self.ifcond, self.features, self.variants)
>
>
> -class QAPISchemaVariants:
> -    def __init__(self, tag_name, info, tag_member, variants):
> -        # Unions pass tag_name but not tag_member.
> -        # Alternates pass tag_member but not tag_name.
> -        # After check(), tag_member is always set.
> -        assert bool(tag_member) != bool(tag_name)
> -        assert (isinstance(tag_name, str) or
> -                isinstance(tag_member, QAPISchemaObjectTypeMember))
> +class QAPISchemaVariantsBase:
> +    def __init__(self, info, variants):
>          for v in variants:
>              assert isinstance(v, QAPISchemaVariant)
> -        self._tag_name = tag_name
>          self.info = info
> -        self.tag_member = tag_member
> +        self.tag_member = None
>          self.variants = variants
>
>      def set_defined_in(self, name):
> @@ -645,66 +638,68 @@ def set_defined_in(self, name):
>              v.set_defined_in(name)
>
>      def check(self, schema, seen):
> -        if self._tag_name:      # 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.resolve_type(self.tag_member.defined_in)
> -            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.is_present():
> -                raise QAPISemError(
> -                    self.info,
> -                    "discriminator member '%s' of %s must not be conditional"
> -                    % (self._tag_name, base))
> -        else:                   # alternate
> -            assert isinstance(self.tag_member.type, QAPISchemaEnumType)
> -            assert not self.tag_member.optional
> -            assert not self.tag_member.ifcond.is_present()
> -        if self._tag_name:      # union
> -            # branches that are not explicitly covered get an empty type
> -            cases = {v.name for v in self.variants}
> -            for m in self.tag_member.type.members:
> -                if m.name not in cases:
> -                    v = QAPISchemaVariant(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):
> -                    raise QAPISemError(
> -                        self.info,
> -                        "%s cannot use %s"
> -                        % (v.describe(self.info), v.type.describe()))
> -                v.type.check(schema)
> +
> +
> +class QAPISchemaVariants(QAPISchemaVariantsBase):
> +    def __init__(self, info, variants, tag_name):
> +        assert isinstance(tag_name, str)
> +        super().__init__(info, variants)
> +        self._tag_name = tag_name
> +
> +    def check(self, schema, seen):
> +        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.resolve_type(self.tag_member.defined_in)
> +        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.is_present():
> +            raise QAPISemError(
> +                self.info,
> +                "discriminator member '%s' of %s must not be conditional"
> +                % (self._tag_name, base))
> +        # branches that are not explicitly covered get an empty type
> +        cases = {v.name for v in self.variants}
> +        for m in self.tag_member.type.members:
> +            if m.name not in cases:
> +                v = QAPISchemaVariant(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")
> +        super().check(schema, seen)
> +        for v in self.variants:
> +            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):
> +                raise QAPISemError(
> +                    self.info,
> +                    "%s cannot use %s"
> +                    % (v.describe(self.info), v.type.describe()))
> +            v.type.check(schema)
>
>      def check_clash(self, info, seen):
>          for v in self.variants:
> @@ -713,6 +708,19 @@ def check_clash(self, info, seen):
>              v.type.check_clash(info, dict(seen))
>
>
> +class QAPISchemaAlternatives(QAPISchemaVariantsBase):
> +    def __init__(self, info, variants, tag_member):
> +        assert isinstance(tag_member, QAPISchemaObjectTypeMember)
> +        super().__init__(info, variants)
> +        self.tag_member = tag_member
> +
> +    def check(self, schema, seen):
> +        super().check(schema, seen)
> +        assert isinstance(self.tag_member.type, QAPISchemaEnumType)
> +        assert not self.tag_member.optional
> +        assert not self.tag_member.ifcond.is_present()
> +
> +
>  class QAPISchemaMember:
>      """ Represents object members, enum members and features """
>      role = 'member'
> @@ -1184,7 +1192,7 @@ def _def_union_type(self, expr: QAPIExpression):
>              QAPISchemaObjectType(name, info, expr.doc, ifcond, features,
>                                   base, members,
>                                   QAPISchemaVariants(
> -                                     tag_name, info, None, variants)))
> +                                     info, variants, tag_name)))
>
>      def _def_alternate_type(self, expr: QAPIExpression):
>          name = expr['alternate']
> @@ -1202,7 +1210,7 @@ def _def_alternate_type(self, expr: QAPIExpression):
>          self._def_definition(
>              QAPISchemaAlternateType(
>                  name, info, expr.doc, ifcond, features,
> -                QAPISchemaVariants(None, info, tag_member, variants)))
> +                QAPISchemaAlternatives(info, variants, tag_member)))
>
>      def _def_command(self, expr: QAPIExpression):
>          name = expr['command']
> diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py
> index c39d054d2c..05da30b855 100644
> --- a/scripts/qapi/types.py
> +++ b/scripts/qapi/types.py
> @@ -23,6 +23,7 @@
>  )
>  from .schema import (
>      QAPISchema,
> +    QAPISchemaAlternatives,
>      QAPISchemaEnumMember,
>      QAPISchemaFeature,
>      QAPISchemaIfCond,
> @@ -369,11 +370,11 @@ def visit_alternate_type(self,
>                               info: Optional[QAPISourceInfo],
>                               ifcond: QAPISchemaIfCond,
>                               features: List[QAPISchemaFeature],
> -                             variants: QAPISchemaVariants) -> None:
> +                             alternatives: QAPISchemaAlternatives) -> None:
>          with ifcontext(ifcond, self._genh):
>              self._genh.preamble_add(gen_fwd_object_or_array(name))
>          self._genh.add(gen_object(name, ifcond, None,
> -                                  [variants.tag_member], variants))
> +                                  [alternatives.tag_member], alternatives))
>          with ifcontext(ifcond, self._genh, self._genc):
>              self._gen_type_cleanup(name)
>
> diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py
> index c56ea4d724..725bfcef50 100644
> --- a/scripts/qapi/visit.py
> +++ b/scripts/qapi/visit.py
> @@ -28,6 +28,7 @@
>  )
>  from .schema import (
>      QAPISchema,
> +    QAPISchemaAlternatives,
>      QAPISchemaEnumMember,
>      QAPISchemaEnumType,
>      QAPISchemaFeature,
> @@ -222,7 +223,8 @@ def gen_visit_enum(name: str) -> str:
>                   c_name=c_name(name))
>
>
> -def gen_visit_alternate(name: str, variants: QAPISchemaVariants) -> str:
> +def gen_visit_alternate(name: str,
> +                        alternatives: QAPISchemaAlternatives) -> str:
>      ret = mcgen('''
>
>  bool visit_type_%(c_name)s(Visitor *v, const char *name,
> @@ -244,7 +246,7 @@ def gen_visit_alternate(name: str, variants: 
> QAPISchemaVariants) -> str:
>  ''',
>                  c_name=c_name(name))
>
> -    for var in variants.variants:
> +    for var in alternatives.variants:
>          ret += var.ifcond.gen_if()
>          ret += mcgen('''
>      case %(case)s:
> @@ -414,10 +416,10 @@ def visit_alternate_type(self,
>                               info: Optional[QAPISourceInfo],
>                               ifcond: QAPISchemaIfCond,
>                               features: List[QAPISchemaFeature],
> -                             variants: QAPISchemaVariants) -> None:
> +                             alternatives: QAPISchemaAlternatives) -> None:
>          with ifcontext(ifcond, self._genh, self._genc):
>              self._genh.add(gen_visit_decl(name))
> -            self._genc.add(gen_visit_alternate(name, variants))
> +            self._genc.add(gen_visit_alternate(name, alternatives))
>
>
>  def gen_visit(schema: QAPISchema,
> diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
> index 14f7b62a44..b66ceb81b8 100755
> --- a/tests/qapi-schema/test-qapi.py
> +++ b/tests/qapi-schema/test-qapi.py
> @@ -61,9 +61,10 @@ def visit_object_type(self, name, info, ifcond, features,
>          self._print_if(ifcond)
>          self._print_features(features)
>
> -    def visit_alternate_type(self, name, info, ifcond, features, variants):
> +    def visit_alternate_type(self, name, info, ifcond, features,
> +                             alternatives):
>          print('alternate %s' % name)
> -        self._print_variants(variants)
> +        self._print_variants(alternatives)
>          self._print_if(ifcond)
>          self._print_features(features)
>
>




reply via email to

[Prev in Thread] Current Thread [Next in Thread]