All of lore.kernel.org
 help / color / mirror / Atom feed
From: Markus Armbruster <armbru@redhat.com>
To: "Marc-André Lureau" <marcandre.lureau@gmail.com>
Cc: QEMU <qemu-devel@nongnu.org>, Michael Roth <mdroth@linux.vnet.ibm.com>
Subject: Re: [Qemu-devel] [PATCH 07/26] qapi: add 'if' condition on top-level schema elements
Date: Tue, 22 Aug 2017 18:52:19 +0200	[thread overview]
Message-ID: <87h8wzfsu4.fsf@dusky.pond.sub.org> (raw)
In-Reply-To: <CAJ+F1CKT8MXVSP+UTu6C50Ue18GhiuXAE9rCQbTiB54iw6_YPA@mail.gmail.com> (=?utf-8?Q?=22Marc-Andr=C3=A9?= Lureau"'s message of "Tue, 22 Aug 2017 13:17:55 +0200")

Marc-André Lureau <marcandre.lureau@gmail.com> writes:

> Hi
>
> On Wed, Aug 16, 2017 at 5:43 PM, Markus Armbruster <armbru@redhat.com> wrote:
>> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>>
>>> Add 'if' c-preprocessor condition on top-level schema elements:
>>> struct, enum, union, alternate, command, event.
>>
>> An example would be useful here.  Your cover letter has nice ones, would
>> be a shame not to preserve them for posterity in the commit log.
>>
>
> ok
>
>>> Variants objects types are created outside of #if blocks, since they
>>
>> What are "variants objects types"?
>>
>
> I think I meant implicit objects types, that may be created by variant.
>
>>> may be shared by various types. Even if unused, this shouldn't be an
>>> issue, since those are internal types.
>>>
>>> Note that there is no checks in qapi scripts to verify that elements
>>
>> there are
>>
>>> have compatible conditions (ex: if-struct used by a if-foo
>>> command). This may thus fail at C build time if they don't share the
>>> same subset of conditions.
>>
>> Fair enough.
>>
>>> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>>> ---
>>>  scripts/qapi.py                         | 153 +++++++++++++++++++++++++-------
>>>  scripts/qapi-commands.py                |   4 +-
>>>  scripts/qapi-event.py                   |   4 +-
>>>  scripts/qapi-introspect.py              |  46 ++++++----
>>>  scripts/qapi-types.py                   |  51 +++++++----
>>>  scripts/qapi-visit.py                   |  13 ++-
>>>  scripts/qapi2texi.py                    |  10 +--
>>>  tests/Makefile.include                  |   1 +
>>>  tests/qapi-schema/bad-if.err            |   1 +
>>>  tests/qapi-schema/bad-if.exit           |   1 +
>>>  tests/qapi-schema/bad-if.json           |   3 +
>>>  tests/qapi-schema/bad-if.out            |   0
>>>  tests/qapi-schema/qapi-schema-test.json |  20 +++++
>>>  tests/qapi-schema/qapi-schema-test.out  |  31 +++++++
>>>  tests/qapi-schema/test-qapi.py          |  21 +++--
>>>  15 files changed, 272 insertions(+), 87 deletions(-)
>>>  create mode 100644 tests/qapi-schema/bad-if.err
>>>  create mode 100644 tests/qapi-schema/bad-if.exit
>>>  create mode 100644 tests/qapi-schema/bad-if.json
>>>  create mode 100644 tests/qapi-schema/bad-if.out
>>>
>>> diff --git a/scripts/qapi.py b/scripts/qapi.py
>>> index 4ecc19e944..79ba1e93da 100644
>>> --- a/scripts/qapi.py
>>> +++ b/scripts/qapi.py
>>> @@ -639,6 +639,16 @@ def add_name(name, info, meta, implicit=False):
>>>      all_names[name] = meta
>>>
>>>
>>> +def check_if(expr, info):
>>> +    ifcond = expr.get('if')
>>> +    if not ifcond or isinstance(ifcond, str):
>>> +        return
>>> +    if (not isinstance(ifcond, list) or
>>> +        any([not isinstance(elt, str) for elt in ifcond])):
>>> +        raise QAPISemError(info, "'if' condition requires a string or "
>>> +                           "a list of string")
>>
>> Wait a second!  What's this list business?  The commit message doesn't
>> say.
>
> Updated commit message, and documented in docs/devel/qapi-code-gen.txt
>
>>
>> Also, pep8 gripes:
>>
>>     scripts/qapi.py:647:9: E129 visually indented line with same indent as next logical line
>>
>
> fixed
>
>>> +
>>> +
>>>  def check_type(info, source, value, allow_array=False,
>>>                 allow_dict=False, allow_optional=False,
>>>                 allow_metas=[]):
>>> @@ -865,6 +875,7 @@ def check_keys(expr_elem, meta, required, optional=[]):
>>>      expr = expr_elem['expr']
>>>      info = expr_elem['info']
>>>      name = expr[meta]
>>> +    optional.append('if')
>>
>> Caution!
>>
>>     $ python
>>     Python 2.7.13 (default, May 10 2017, 20:04:36)
>>     >>> def surprise(arg=[]):
>>     ...     arg.append('if')
>>     ...     return arg
>>     ...
>>     >>> surprise()
>>     ['if']
>>     >>> surprise()
>>     ['if', 'if']
>>     >>> surprise()
>>     ['if', 'if', 'if']
>>
>> Never modify an argument that has list or dictionary default value.  To
>> avoid the temptation, never use such defaul values.
>
> Oops! Without bad consequences here, but fixed anyway.
>
>>
>>>      if not isinstance(name, str):
>>>          raise QAPISemError(info, "'%s' key must have a string value" % meta)
>>>      required = required + [meta]
>>> @@ -880,6 +891,8 @@ def check_keys(expr_elem, meta, required, optional=[]):
>>>              raise QAPISemError(info,
>>>                                 "'%s' of %s '%s' should only use true value"
>>>                                 % (key, meta, name))
>>> +        if key == 'if':
>>> +            check_if(expr, info)
>>>      for key in required:
>>>          if key not in expr:
>>>              raise QAPISemError(info, "Key '%s' is missing from %s '%s'"
>>> @@ -989,6 +1002,10 @@ class QAPISchemaEntity(object):
>>>          # such place).
>>>          self.info = info
>>>          self.doc = doc
>>> +        self.ifcond = None
>>> +
>>> +    def set_ifcond(self, ifcond):
>>> +        self.ifcond = ifcond
>>
>> @ifcond is an awkward name, but I don't have better ideas.
>
> Yeah, I got used to it now ;)
>
>>
>>>
>>>      def c_name(self):
>>>          return c_name(self.name)
>>> @@ -1017,26 +1034,26 @@ class QAPISchemaVisitor(object):
>>>      def visit_builtin_type(self, name, info, json_type):
>>>          pass
>>>
>>> -    def visit_enum_type(self, name, info, values, prefix):
>>> +    def visit_enum_type(self, name, info, values, prefix, ifcond):
>>>          pass
>>>
>>> -    def visit_array_type(self, name, info, element_type):
>>> +    def visit_array_type(self, name, info, element_type, ifcond):
>>>          pass
>>>
>>> -    def visit_object_type(self, name, info, base, members, variants):
>>> +    def visit_object_type(self, name, info, base, members, variants, ifcond):
>>>          pass
>>>
>>> -    def visit_object_type_flat(self, name, info, members, variants):
>>> +    def visit_object_type_flat(self, name, info, members, variants, ifcond):
>>>          pass
>>>
>>> -    def visit_alternate_type(self, name, info, variants):
>>> +    def visit_alternate_type(self, name, info, variants, ifcond):
>>>          pass
>>>
>>>      def visit_command(self, name, info, arg_type, ret_type,
>>> -                      gen, success_response, boxed):
>>> +                      gen, success_response, boxed, ifcond):
>>>          pass
>>>
>>> -    def visit_event(self, name, info, arg_type, boxed):
>>> +    def visit_event(self, name, info, arg_type, boxed, ifcond):
>>>          pass
>>>
>>>
>>> @@ -1136,7 +1153,7 @@ class QAPISchemaEnumType(QAPISchemaType):
>>>
>>>      def visit(self, visitor):
>>>          visitor.visit_enum_type(self.name, self.info,
>>> -                                self.member_names(), self.prefix)
>>> +                                self.member_names(), self.prefix, self.ifcond)
>>>
>>>
>>>  class QAPISchemaArrayType(QAPISchemaType):
>>> @@ -1149,6 +1166,7 @@ class QAPISchemaArrayType(QAPISchemaType):
>>>      def check(self, schema):
>>>          self.element_type = schema.lookup_type(self._element_type_name)
>>>          assert self.element_type
>>> +        self.ifcond = self.element_type.ifcond
>>>
>>>      def is_implicit(self):
>>>          return True
>>> @@ -1166,7 +1184,8 @@ class QAPISchemaArrayType(QAPISchemaType):
>>>          return 'array of ' + elt_doc_type
>>>
>>>      def visit(self, visitor):
>>> -        visitor.visit_array_type(self.name, self.info, self.element_type)
>>> +        visitor.visit_array_type(self.name, self.info, self.element_type,
>>> +                                 self.ifcond)
>>>
>>>
>>>  class QAPISchemaObjectType(QAPISchemaType):
>>> @@ -1247,9 +1266,10 @@ class QAPISchemaObjectType(QAPISchemaType):
>>>
>>>      def visit(self, visitor):
>>>          visitor.visit_object_type(self.name, self.info,
>>> -                                  self.base, self.local_members, self.variants)
>>> +                                  self.base, self.local_members, self.variants,
>>> +                                  self.ifcond)
>>>          visitor.visit_object_type_flat(self.name, self.info,
>>> -                                       self.members, self.variants)
>>> +                                       self.members, self.variants, self.ifcond)
>>
>> You break the line before self.ifcond almost everywhere, and when you
>> don't, the line gets long.  This one goes over the limit:
>>
>>     scripts/qapi.py:1285:80: E501 line too long (80 > 79 characters)
>>
>> Suggest to break it before self.ifcond everywhere.
>>
>
> ok
>
>>>
>>>
>>>  class QAPISchemaMember(object):
>>> @@ -1392,7 +1412,8 @@ class QAPISchemaAlternateType(QAPISchemaType):
>>>          return 'value'
>>>
>>>      def visit(self, visitor):
>>> -        visitor.visit_alternate_type(self.name, self.info, self.variants)
>>> +        visitor.visit_alternate_type(self.name, self.info,
>>> +                                     self.variants, self.ifcond)
>>>
>>>      def is_empty(self):
>>>          return False
>>> @@ -1434,7 +1455,8 @@ class QAPISchemaCommand(QAPISchemaEntity):
>>>      def visit(self, visitor):
>>>          visitor.visit_command(self.name, self.info,
>>>                                self.arg_type, self.ret_type,
>>> -                              self.gen, self.success_response, self.boxed)
>>> +                              self.gen, self.success_response, self.boxed,
>>> +                              self.ifcond)
>>>
>>>
>>>  class QAPISchemaEvent(QAPISchemaEntity):
>>> @@ -1462,7 +1484,8 @@ class QAPISchemaEvent(QAPISchemaEntity):
>>>              raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
>>>
>>>      def visit(self, visitor):
>>> -        visitor.visit_event(self.name, self.info, self.arg_type, self.boxed)
>>> +        visitor.visit_event(self.name, self.info, self.arg_type, self.boxed,
>>> +                            self.ifcond)
>>>
>>>
>>>  class QAPISchema(object):
>>> @@ -1481,11 +1504,12 @@ class QAPISchema(object):
>>>              print >>sys.stderr, err
>>>              exit(1)
>>>
>>> -    def _def_entity(self, ent):
>>> +    def _def_entity(self, ent, ifcond=None):
>>>          # Only the predefined types are allowed to not have info
>>>          assert ent.info or self._predefining
>>>          assert ent.name not in self._entity_dict
>>>          self._entity_dict[ent.name] = ent
>>> +        ent.set_ifcond(ifcond)
>>
>> .set_ifcond(None) does the right thing.
>>
>> However:
>>
>>>
>>>      def lookup_entity(self, name, typ=None):
>>>          ent = self._entity_dict.get(name)
>>> @@ -1534,11 +1558,11 @@ class QAPISchema(object):
>>>      def _make_enum_members(self, values):
>>>          return [QAPISchemaMember(v) for v in values]
>>>
>>> -    def _make_implicit_enum_type(self, name, info, values):
>>> +    def _make_implicit_enum_type(self, name, info, values, ifcond):
>>>          # See also QAPISchemaObjectTypeMember._pretty_owner()
>>>          name = name + 'Kind'   # Use namespace reserved by add_name()
>>>          self._def_entity(QAPISchemaEnumType(
>>> -            name, info, None, self._make_enum_members(values), None))
>>> +            name, info, None, self._make_enum_members(values), None), ifcond)
>>>          return name
>>
>> Why is ifcond not a constructor argument like name, info, and so forth?
>> What makes it special?
>>
>
> I think it was easier that way (builder pattern), but it make sense as
> constructor too. Changed
>
>>>
>>>      def _make_array_type(self, element_type, info):
>>> @@ -1547,22 +1571,26 @@ class QAPISchema(object):
>>>              self._def_entity(QAPISchemaArrayType(name, info, element_type))
>>>          return name
>>>
>>> -    def _make_implicit_object_type(self, name, info, doc, role, members):
>>> +    def _make_implicit_object_type(self, name, info, doc, role, members,
>>> +                                   ifcond=None):
>>>          if not members:
>>>              return None
>>>          # See also QAPISchemaObjectTypeMember._pretty_owner()
>>>          name = 'q_obj_%s-%s' % (name, role)
>>> -        if not self.lookup_entity(name, QAPISchemaObjectType):
>>> +        if self.lookup_entity(name, QAPISchemaObjectType):
>>> +            assert ifcond is None
>>> +        else:
>>>              self._def_entity(QAPISchemaObjectType(name, info, doc, None,
>>> -                                                  members, None))
>>> +                                                  members, None), ifcond)
>>>          return name
>>
>> Hmm, this smells like it might be the "variants objects types" mentioned
>> in the commit message.
>>
>> Types made with _make_implicit_object_type():
>>
>> * The wrapper around "simple" union members, by _make_simple_variant()
>>
>> * A flat union's base type when it's implicit, by _def_union_type()
>>
>> * A command's argument type when it's implicit, by _def_command()
>>
>> * An event's argument type when it's implicit, by _def_event()
>>
>> Only the first one can be used more than once, namely when a type occurs
>> in more than one simple union.  The "correct" ifcond is the disjunction
>> of all its user's ifconds.  You make it use the *first* ifcond instead.
>> Wrong: breaks when one of the other simple unions has a condition that
>> is true when the first one's is false.
>>
>> Your commit message suggests you intended to make it unconditional
>> instead.  That would work: the worst that can happen is compiling a few
>> q_obj_FOO_wrapper typedefs and visit_type_q_FOO_wrapper() functions that
>> aren't actually used.  Tolerable, in particular since I hope to get rid
>> of "simple" unions some day.
>>
>> Sadly, it would prevent us from making the visit functions for implicit
>> types static, because unused static functions trigger warnings.  Let's
>> not worry about that now.
>>
>> Generating the disjunction of all conditions wouldn't be terribly hard.
>> I'm not asking for it, though.
>
> I suggest to tackle it as follow-up then. Added a FIXME
>
>
>>
>> You assert that implicit types are unconditional from the second use on.
>> I guess you mean to assert the ones used more than once are
>> unconditional:
>>
>>            typ = self.lookup_entity(name, QAPISchemaObjectType)
>>            if typ:
>>                assert ifcond is None and typ.ifcond is None
>>
>> But what you *should* assert is that the conditions are the same:
>>
>>            typ = self.lookup_entity(name, QAPISchemaObjectType)
>>            if typ:
>>                assert ifcond == typ.ifcond
>>
>>>
>
> ok
>
>>>      def _def_enum_type(self, expr, info, doc):
>>>          name = expr['enum']
>>>          data = expr['data']
>>>          prefix = expr.get('prefix')
>>> -        self._def_entity(QAPISchemaEnumType(
>>> -            name, info, doc, self._make_enum_members(data), prefix))
>>> +        return self._def_entity(QAPISchemaEnumType(
>>> +            name, info, doc, self._make_enum_members(data), prefix),
>>> +                                expr.get('if'))
>>
>> Covers enumeration types.
>>
>> Why return?  The (only) caller throws the value away...
>
> left-over, fixed
>
>>
>>>
>>>      def _make_member(self, name, typ, info):
>>>          optional = False
>>> @@ -1584,7 +1612,8 @@ class QAPISchema(object):
>>>          data = expr['data']
>>>          self._def_entity(QAPISchemaObjectType(name, info, doc, base,
>>>                                                self._make_members(data, info),
>>> -                                              None))
>>> +                                              None),
>>> +                         expr.get('if'))
>>
>> Covers struct types.
>>
>>>
>>>      def _make_variant(self, case, typ):
>>>          return QAPISchemaObjectTypeVariant(case, typ)
>>> @@ -1593,8 +1622,10 @@ class QAPISchema(object):
>>>          if isinstance(typ, list):
>>>              assert len(typ) == 1
>>>              typ = self._make_array_type(typ[0], info)
>>> +        type_entity = self.lookup_type(typ)
>>>          typ = self._make_implicit_object_type(
>>> -            typ, info, None, 'wrapper', [self._make_member('data', typ, info)])
>>> +            typ, info, None, 'wrapper',
>>> +            [self._make_member('data', typ, info)], type_entity.ifcond)
>>>          return QAPISchemaObjectTypeVariant(case, typ)
>>
>> A simple union member's wrapper type inherits its condition from the
>> member type.
>>
>> I think you need to pass None instead of type_entity.ifcond here.
>
> That doesn't work, visitor symbols are missing in generated code. Why
> shouldn't the wrapper share the same condition as the underlying type?

Because it's shared with other simple unions that have the same variant?

>>>
>>>      def _def_union_type(self, expr, info, doc):
>>> @@ -1604,8 +1635,9 @@ class QAPISchema(object):
>>>          tag_name = expr.get('discriminator')
>>>          tag_member = None
>>>          if isinstance(base, dict):
>>> -            base = (self._make_implicit_object_type(
>>> -                name, info, doc, 'base', self._make_members(base, info)))
>>> +            base = self._make_implicit_object_type(
>>> +                name, info, doc, 'base', self._make_members(base, info),
>>> +                expr.get('if'))
>>
>> A flat union's implicit base type inherits its condition from the flat
>> union.  Good.
>>
>>>          if tag_name:
>>>              variants = [self._make_variant(key, value)
>>>                          for (key, value) in data.iteritems()]
>>> @@ -1614,14 +1646,16 @@ class QAPISchema(object):
>>>              variants = [self._make_simple_variant(key, value, info)
>>>                          for (key, value) in data.iteritems()]
>>>              typ = self._make_implicit_enum_type(name, info,
>>> -                                                [v.name for v in variants])
>>> +                                                [v.name for v in variants],
>>> +                                                expr.get('if'))
>>
>> A flat union's implicit enumeration type inherits its condition from the
>> flat union.  Good.
>>
>>>              tag_member = QAPISchemaObjectTypeMember('type', typ, False)
>>>              members = [tag_member]
>>>          self._def_entity(
>>>              QAPISchemaObjectType(name, info, doc, base, members,
>>>                                   QAPISchemaObjectTypeVariants(tag_name,
>>>                                                                tag_member,
>>> -                                                              variants)))
>>> +                                                              variants)),
>>> +            expr.get('if'))
>>
>> Covers union types.
>>
>> Third use of expr.get('if') in this function.  Please put it in a
>> variable.
>>
>> Actually, do that for *all* uses of expr[X] and expr.get(X) in class
>> QAPISchema, because that's how the existing code works.
>
> done
>
>>
>>>
>>>      def _def_alternate_type(self, expr, info, doc):
>>>          name = expr['alternate']
>>> @@ -1633,7 +1667,8 @@ class QAPISchema(object):
>>>              QAPISchemaAlternateType(name, info, doc,
>>>                                      QAPISchemaObjectTypeVariants(None,
>>>                                                                   tag_member,
>>> -                                                                 variants)))
>>> +                                                                 variants)),
>>> +            expr.get('if'))
>>
>> Covers alternate types.
>>
>>>
>>>      def _def_command(self, expr, info, doc):
>>>          name = expr['command']
>>> @@ -1644,12 +1679,14 @@ class QAPISchema(object):
>>>          boxed = expr.get('boxed', False)
>>>          if isinstance(data, OrderedDict):
>>>              data = self._make_implicit_object_type(
>>> -                name, info, doc, 'arg', self._make_members(data, info))
>>> +                name, info, doc, 'arg', self._make_members(data, info),
>>> +                expr.get('if'))
>>
>> A command's implicit argument type inherits its condition from the
>> command.  Good.
>>
>>>          if isinstance(rets, list):
>>>              assert len(rets) == 1
>>>              rets = self._make_array_type(rets[0], info)
>>>          self._def_entity(QAPISchemaCommand(name, info, doc, data, rets,
>>> -                                           gen, success_response, boxed))
>>> +                                           gen, success_response, boxed),
>>> +                         expr.get('if'))
>>
>> Covers commands.
>>
>>>
>>>      def _def_event(self, expr, info, doc):
>>>          name = expr['event']
>>> @@ -1657,8 +1694,10 @@ class QAPISchema(object):
>>>          boxed = expr.get('boxed', False)
>>>          if isinstance(data, OrderedDict):
>>>              data = self._make_implicit_object_type(
>>> -                name, info, doc, 'arg', self._make_members(data, info))
>>> -        self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed))
>>> +                name, info, doc, 'arg', self._make_members(data, info),
>>> +                expr.get('if'))
>>
>> An event's implicit argument type inherits its condition from the
>> command.  Good.
>>
>>> +        self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed),
>>> +                         expr.get('if'))
>>
>> Covers events.  You got them all.  Good.
>>
>>>
>>>      def _def_exprs(self):
>>>          for expr_elem in self.exprs:
>>> @@ -1848,6 +1887,54 @@ def guardend(name):
>>>                   name=guardname(name))
>>>
>>>
>>> +def gen_if(ifcond, func=''):
>>> +    if not ifcond:
>>> +        return ''
>>> +    if isinstance(ifcond, str):
>>> +        ifcond = [ifcond]
>>> +    ret = '\n'
>>> +    for ifc in ifcond:
>>> +        ret += mcgen('#if %(ifcond)s /* %(func)s */\n', ifcond=ifc, func=func)
>>> +    ret += '\n'
>>> +    return ret
>>
>> Please use mcgen() like the existing code:
>>
>
> mcgen() does indent and fails with pre-processor lines. I added a comment

I see.  Comment is good enough for now.

>>            ret += mcgen('''
>>    #if %(ifcond)s /* %(func)s */
>>    ''', ifcond=ifc, func=func)
>>
>> With the default value of @func, we get a useless, ugly comment /* */.
>> If this can happen, please suppress the comment.  Else, drop @func's
>> default value.
>>
>> Lists appear to be conjunctions.  What for?
>
> I added some doc in qapi-code-gen.txt
>
>>
>>> +
>>> +
>>> +def gen_endif(ifcond, func=''):
>>> +    if not ifcond:
>>> +        return ''
>>> +    if isinstance(ifcond, str):
>>> +        ifcond = [ifcond]
>>> +    ret = '\n'
>>> +    for ifc in reversed(ifcond):
>>> +        ret += mcgen('#endif /* %(ifcond)s %(func)s */\n',
>>> +                     ifcond=ifc, func=func)
>>> +    ret += '\n'
>>> +    return ret
>>
>> Likewise.
>>
>>> +
>>> +
>>> +# wrap a method to add #if / #endif to generated code
>>> +# self must have 'if_members' listing the attributes to wrap
>>> +# the last argument of the wrapped function must be the 'ifcond'
>>
>> Start your sentences with a capital letter, and end them with a period,
>> please.
>
> ok
>
>>
>>> +def if_wrap(func):
>>
>> Blank line, please.
>
> ok
>
>>
>>> +    def func_wrapper(self, *args, **kwargs):
>>> +        funcname = self.__class__.__name__ + '.' + func.__name__
>>> +        ifcond = args[-1]
>>> +        save = {}
>>> +        for mem in self.if_members:
>>> +            save[mem] = getattr(self, mem)
>>> +        func(self, *args, **kwargs)
>>> +        for mem, val in save.items():
>>> +            newval = getattr(self, mem)
>>> +            if newval != val:
>>> +                assert newval.startswith(val)
>>> +                newval = newval[len(val):]
>>> +                val += gen_if(ifcond, funcname)
>>
>> Emitting comments pointing to the QAPI schema into the generated code is
>> often helpful.  But this one points to QAPI generator code.  Why is that
>> useful?
>
> That was mostly useful during development, removed
>
>>
>>> +                val += newval
>>> +                val += gen_endif(ifcond, funcname)
>>> +            setattr(self, mem, val)
>>> +
>>> +    return func_wrapper
>>> +
>>
>> pep8 again:
>>
>>     scripts/qapi.py:1955:1: E302 expected 2 blank lines, found 1
>>
>> Peeking ahead: this function is used as a decorator.  Let's help the
>> reader and mention that in the function comment, or by naming the
>> function suitably.  ifcond_decorator?
>
> done
>
>>
>> Messing with the wrapped method's class's attributes is naughty.  Worse,
>> it's hard to understand.  What alternatives have you considered?
>
> Well, I started writing the code that checked if members got code
> added, I had to put some enter()/leave() code everywhere. Then I
> realize this could easily be the job for a decorator. I think the end
> result is pretty neat.

I think "clever" would describe it better than "neat".  Possibly "too
clever".

>                        If you have a better idea, can you do it in a
> follow-up?

I need to complete review before I can tell.

>>
>>>  def gen_enum_lookup(name, values, prefix=None):
>>>      ret = mcgen('''
>>>
[...]

  reply	other threads:[~2017-08-22 16:52 UTC|newest]

Thread overview: 69+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-07-27 15:41 [Qemu-devel] [PATCH 00/26] qapi: add #if pre-processor conditions to generated code Marc-André Lureau
2017-07-27 15:41 ` [Qemu-devel] [PATCH 01/26] qapi: fix type_seen key error Marc-André Lureau
2017-08-15 14:40   ` Markus Armbruster
2017-08-17 23:17     ` Marc-André Lureau
2017-07-27 15:41 ` [Qemu-devel] [PATCH 02/26] qobject: replace dump_qobject() by qobject_to_string() Marc-André Lureau
2017-08-16  9:02   ` Markus Armbruster
2017-07-27 15:41 ` [Qemu-devel] [PATCH 03/26] qboject: add literal qobject type Marc-André Lureau
2017-08-16  8:59   ` Markus Armbruster
2017-08-22 11:16     ` Marc-André Lureau
2017-07-27 15:41 ` [Qemu-devel] [PATCH 04/26] qapi: generate a literal qobject for introspection Marc-André Lureau
2017-08-16 10:21   ` Markus Armbruster
2017-08-22 11:17     ` Marc-André Lureau
2017-08-17 11:48   ` Markus Armbruster
2017-07-27 15:41 ` [Qemu-devel] [PATCH 05/26] visitor: pass size of strings array to enum visitor Marc-André Lureau
2017-08-16 12:54   ` Markus Armbruster
2017-08-22 11:17     ` Marc-André Lureau
2017-07-27 15:41 ` [Qemu-devel] [PATCH 06/26] qapi2texi: minor python code simplification Marc-André Lureau
2017-08-16 12:55   ` Markus Armbruster
2017-07-27 15:41 ` [Qemu-devel] [PATCH 07/26] qapi: add 'if' condition on top-level schema elements Marc-André Lureau
2017-08-16 15:43   ` Markus Armbruster
2017-08-17  5:50     ` Markus Armbruster
2017-08-22 11:17     ` Marc-André Lureau
2017-08-22 16:52       ` Markus Armbruster [this message]
2017-08-23 12:45         ` Eduardo Habkost
2017-08-17 11:51   ` Markus Armbruster
2017-07-27 15:41 ` [Qemu-devel] [PATCH 08/26] qapi: add 'if' condition on enum member values Marc-André Lureau
2017-07-27 15:41 ` [Qemu-devel] [PATCH 09/26] qapi: add 'if' condition on struct member Marc-André Lureau
2017-07-27 15:41 ` [Qemu-devel] [PATCH 10/26] qapi: add 'if' condition on union variant Marc-André Lureau
2017-07-27 15:41 ` [Qemu-devel] [PATCH 11/26] qapi: add 'if' condition on alternate variant Marc-André Lureau
2017-07-27 15:41 ` [Qemu-devel] [PATCH 12/26] qapi2texi: add 'If:' section to generated documentation Marc-André Lureau
2017-07-27 15:41 ` [Qemu-devel] [PATCH 13/26] qapi2texi: add 'If:' condition to enum values Marc-André Lureau
2017-07-27 15:41 ` [Qemu-devel] [PATCH 14/26] qapi2texi: add 'If:' condition to struct members Marc-André Lureau
2017-07-27 15:41 ` [Qemu-devel] [PATCH 15/26] qapi2texi: add condition to variants Marc-André Lureau
2017-07-27 15:41 ` [Qemu-devel] [PATCH 16/26] qapi: add conditions to VNC type/commands/events on the schema Marc-André Lureau
2017-07-28 19:00   ` Dr. David Alan Gilbert
2017-08-17  6:32     ` Markus Armbruster
2017-08-17  9:33       ` Dr. David Alan Gilbert
2017-08-17  7:04   ` Markus Armbruster
2017-08-17  8:56     ` Markus Armbruster
2017-08-23 15:07       ` Gerd Hoffmann
2017-08-23 17:35         ` Eduardo Habkost
2017-08-23 15:09     ` Gerd Hoffmann
2017-08-29 10:42     ` Daniel P. Berrange
2017-08-29 10:46       ` Marc-André Lureau
2017-07-27 15:41 ` [Qemu-devel] [PATCH 17/26] qapi: add conditions to SPICE " Marc-André Lureau
2017-08-17  8:10   ` Markus Armbruster
2017-08-17  8:43     ` Markus Armbruster
2017-07-27 15:41 ` [Qemu-devel] [PATCH 18/26] qapi: add conditions to REPLICATION type/commands " Marc-André Lureau
2017-08-17  9:16   ` Markus Armbruster
2017-08-22 11:18     ` Marc-André Lureau
2017-07-27 15:41 ` [Qemu-devel] [PATCH 19/26] build-sys: move qapi variables in qapi.mak Marc-André Lureau
2017-08-17  9:19   ` Markus Armbruster
2017-07-27 15:41 ` [Qemu-devel] [PATCH 20/26] tests/qmp-test: add query-qmp-schema test Marc-André Lureau
2017-07-27 15:41 ` [Qemu-devel] [PATCH 21/26] build-sys: make qemu qapi objects per-target Marc-André Lureau
2017-08-17 11:44   ` Markus Armbruster
2017-08-22 11:18     ` Marc-André Lureau
2017-07-27 15:41 ` [Qemu-devel] [PATCH 22/26] qapi: make rtc-reset-reinjection depend on TARGET_I386 Marc-André Lureau
2017-08-17 11:57   ` Markus Armbruster
2017-08-22 11:18     ` Marc-André Lureau
2017-07-27 15:41 ` [Qemu-devel] [PATCH 23/26] qapi: make s390 commands depend on TARGET_S390X Marc-André Lureau
2017-08-17 12:13   ` Markus Armbruster
2017-07-27 15:41 ` [Qemu-devel] [PATCH 24/26] qapi: make query-gic-capabilities depend on TARGET_ARM Marc-André Lureau
2017-07-27 15:41 ` [Qemu-devel] [PATCH 25/26] qapi: make query-cpu-model-expansion depend on s390 or x86 Marc-André Lureau
2017-07-27 15:41 ` [Qemu-devel] [PATCH 26/26] qapi: make query-cpu-definitions depend on specific targets Marc-André Lureau
2017-08-17 12:30   ` Markus Armbruster
2017-08-17 12:43     ` Marc-André Lureau
2017-08-17 13:55 ` [Qemu-devel] [PATCH 00/26] qapi: add #if pre-processor conditions to generated code Markus Armbruster
2017-08-22 11:22   ` Marc-André Lureau
2017-08-22 16:58     ` Markus Armbruster

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87h8wzfsu4.fsf@dusky.pond.sub.org \
    --to=armbru@redhat.com \
    --cc=marcandre.lureau@gmail.com \
    --cc=mdroth@linux.vnet.ibm.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.