diff mbox

[v5,11/46] qapi: Don't use info as witness of implicit object type

Message ID 1442872682-6523-12-git-send-email-eblake@redhat.com
State New
Headers show

Commit Message

Eric Blake Sept. 21, 2015, 9:57 p.m. UTC
A future patch will enable error reporting from the various
check() methods.  But to report an error on an implicit type,
we'll need to associate a location with the type (the same
location as the top-level entity that is causing the creation
of the implicit type), and once we do that, keying off of
whether foo.info exists is no longer a viable way to determine
if foo is an implicit type.

Rename the info member to _info (so that sub-classes can still
use it, but external code should not), add an is_implicit()
method to QAPISchemaObjectType, and adjust the visitor to pass
another parameter about whether the type is implicit.

Signed-off-by: Eric Blake <eblake@redhat.com>
---
 scripts/qapi-types.py          |  4 ++--
 scripts/qapi-visit.py          |  4 ++--
 scripts/qapi.py                | 33 +++++++++++++++++++--------------
 tests/qapi-schema/test-qapi.py |  2 +-
 4 files changed, 24 insertions(+), 19 deletions(-)

Comments

Markus Armbruster Sept. 28, 2015, 12:43 p.m. UTC | #1
Eric Blake <eblake@redhat.com> writes:

> A future patch will enable error reporting from the various
> check() methods.  But to report an error on an implicit type,
> we'll need to associate a location with the type (the same
> location as the top-level entity that is causing the creation
> of the implicit type), and once we do that, keying off of
> whether foo.info exists is no longer a viable way to determine
> if foo is an implicit type.

Ensuring error messages are good even for implicit types could be hard.
But pretty much anything's better than error messages without location
information.

> Rename the info member to _info (so that sub-classes can still
> use it, but external code should not), add an is_implicit()
> method to QAPISchemaObjectType, and adjust the visitor to pass
> another parameter about whether the type is implicit.

I have doubts on the rename.

When you create an stable interface for use in other programs,
religiously hiding instance variables behind accessor methods can pay.
But in a purely internal interface like this one, I don't see the point.

If we run into a case where we want to use a QAPISchemaEntity's info, I
want to write .info and be done with it.  If we rename it to _info now,
we'll rename it back then.

So far, we've used the '_' prefix only for instance variables that are
clearly internal.  Mostly for stuff flowing from __init__() to check().

> Signed-off-by: Eric Blake <eblake@redhat.com>
> ---
>  scripts/qapi-types.py          |  4 ++--
>  scripts/qapi-visit.py          |  4 ++--
>  scripts/qapi.py                | 33 +++++++++++++++++++--------------
>  tests/qapi-schema/test-qapi.py |  2 +-
>  4 files changed, 24 insertions(+), 19 deletions(-)
>
> diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
> index b292682..aa25e03 100644
> --- a/scripts/qapi-types.py
> +++ b/scripts/qapi-types.py
> @@ -253,8 +253,8 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor):
>              self.decl += gen_array(name, element_type)
>              self._gen_type_cleanup(name)
>
> -    def visit_object_type(self, name, info, base, members, variants):
> -        if info:
> +    def visit_object_type(self, name, info, base, members, variants, implicit):

This is now right at the PEP8 line length limit, and the number of
parameters is getting unwieldy, too.  Hmm.

> +        if not implicit:
>              self._fwdecl += gen_fwd_object_or_array(name)
>              if variants:
>                  assert not members      # not implemented
> diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
> index 1f287ba..62a47fa 100644
> --- a/scripts/qapi-visit.py
> +++ b/scripts/qapi-visit.py
> @@ -348,8 +348,8 @@ class QAPISchemaGenVisitVisitor(QAPISchemaVisitor):
>              self.decl += decl
>              self.defn += defn
>
> -    def visit_object_type(self, name, info, base, members, variants):
> -        if info:
> +    def visit_object_type(self, name, info, base, members, variants, implicit):
> +        if not implicit:
>              self.decl += gen_visit_decl(name)
>              if variants:
>                  assert not members      # not implemented
> diff --git a/scripts/qapi.py b/scripts/qapi.py
> index 7275daa..1dc7641 100644
> --- a/scripts/qapi.py
> +++ b/scripts/qapi.py
> @@ -786,7 +786,7 @@ class QAPISchemaEntity(object):
>      def __init__(self, name, info):
>          assert isinstance(name, str)
>          self.name = name
> -        self.info = info
> +        self._info = info
>
>      def c_name(self):
>          return c_name(self.name)
> @@ -814,7 +814,7 @@ class QAPISchemaVisitor(object):
>      def visit_array_type(self, name, info, element_type):
>          pass
>
> -    def visit_object_type(self, name, info, base, members, variants):
> +    def visit_object_type(self, name, info, base, members, variants, implicit):
>          pass
>
>      def visit_object_type_flat(self, name, info, members, variants):
> @@ -877,7 +877,7 @@ class QAPISchemaBuiltinType(QAPISchemaType):
>          return self._json_type_name
>
>      def visit(self, visitor):
> -        visitor.visit_builtin_type(self.name, self.info, self.json_type())
> +        visitor.visit_builtin_type(self.name, self._info, self.json_type())
>
>
>  class QAPISchemaEnumType(QAPISchemaType):
> @@ -903,7 +903,7 @@ class QAPISchemaEnumType(QAPISchemaType):
>          return 'string'
>
>      def visit(self, visitor):
> -        visitor.visit_enum_type(self.name, self.info,
> +        visitor.visit_enum_type(self.name, self._info,
>                                  self.values, self.prefix)
>
>
> @@ -922,7 +922,7 @@ class QAPISchemaArrayType(QAPISchemaType):
>          return 'array'
>
>      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)
>
>
>  class QAPISchemaObjectType(QAPISchemaType):
> @@ -961,21 +961,25 @@ class QAPISchemaObjectType(QAPISchemaType):
>              self.variants.check(schema, members, seen)
>          self.members = members
>
> +    def is_implicit(self):
> +        return self.name[0] == ':'
> +

The predicate could be defined on any QAPISchemaType, or even any
QAPISchemaEntity, but right now we only ever want to test it for
objects.  Okay.

>      def c_name(self):
> -        assert self.info
> +        assert not self.is_implicit()
>          return QAPISchemaType.c_name(self)
>
>      def c_type(self, is_param=False):
> -        assert self.info
> +        assert not self.is_implicit()
>          return QAPISchemaType.c_type(self)
>
>      def json_type(self):
>          return 'object'
>
>      def visit(self, visitor):
> -        visitor.visit_object_type(self.name, self.info,
> -                                  self.base, self.local_members, self.variants)
> -        visitor.visit_object_type_flat(self.name, self.info,
> +        visitor.visit_object_type(self.name, self._info,
> +                                  self.base, self.local_members, self.variants,
> +                                  self.is_implicit())
> +        visitor.visit_object_type_flat(self.name, self._info,
>                                         self.members, self.variants)
>
>
> @@ -1034,7 +1038,8 @@ class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
>      # This function exists to support ugly simple union special cases
>      # TODO get rid of them, and drop the function
>      def simple_union_type(self):
> -        if isinstance(self.type, QAPISchemaObjectType) and not self.type.info:
> +        if isinstance(self.type, QAPISchemaObjectType) and \
> +           self.type.is_implicit():
>              assert len(self.type.members) == 1
>              assert not self.type.variants
>              return self.type.members[0].type
> @@ -1055,7 +1060,7 @@ 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)
>
>
>  class QAPISchemaCommand(QAPISchemaEntity):
> @@ -1080,7 +1085,7 @@ class QAPISchemaCommand(QAPISchemaEntity):
>              assert isinstance(self.ret_type, QAPISchemaType)
>
>      def visit(self, visitor):
> -        visitor.visit_command(self.name, self.info,
> +        visitor.visit_command(self.name, self._info,
>                                self.arg_type, self.ret_type,
>                                self.gen, self.success_response)
>
> @@ -1099,7 +1104,7 @@ class QAPISchemaEvent(QAPISchemaEntity):
>              assert not self.arg_type.variants   # not implemented
>
>      def visit(self, visitor):
> -        visitor.visit_event(self.name, self.info, self.arg_type)
> +        visitor.visit_event(self.name, self._info, self.arg_type)
>
>
>  class QAPISchema(object):
> diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
> index 649677e..f2cce64 100644
> --- a/tests/qapi-schema/test-qapi.py
> +++ b/tests/qapi-schema/test-qapi.py
> @@ -22,7 +22,7 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
>          if prefix:
>              print '    prefix %s' % prefix
>
> -    def visit_object_type(self, name, info, base, members, variants):
> +    def visit_object_type(self, name, info, base, members, variants, implicit):
>          print 'object %s' % name
>          if base:
>              print '    base %s' % base.name

Three of our visitors implement visit_object_type():

* test-qapi.py doesn't care about implicit (implicitness is obvious
  enough from the name here).

* qapi-types.py and qapi-visit.py ignore implicit object types.  Hmm.

  qapi-introspect.py has a similar need: it wants to ignore *all* types.
  Two ways to ignore entities seem one too many.  Preexisting, but your
  patch makes it stand out a bit more.

  Could we reuse the existing mechanism somehow (and keep method
  visit_object_type() simple)?

  To reuse it without changes, we'd have to make implicit object types a
  separate class, so that QAPISchema.visit()'s isinstance() test can be
  put to work.

  Another option is generalizing QAPISchema's filter.  How?

  A third option is to abandon QAPISchema's filter, and make
  qapi-introspect.py filter in the visitor methods, just like we filter
  implicit objects.

Patch could be split into

A. Encapsulate the "is implicit" predicate in a method, i.e. replace
   not o.info by o.is_implicit().

B. Clean up how we filter out implicit objects.  May better go before A,
   not sure.

C. Rename .info to ._info.  Not sure we even want this part.
Eric Blake Sept. 29, 2015, 3:58 a.m. UTC | #2
On 09/28/2015 06:43 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> A future patch will enable error reporting from the various
>> check() methods.  But to report an error on an implicit type,
>> we'll need to associate a location with the type (the same
>> location as the top-level entity that is causing the creation
>> of the implicit type), and once we do that, keying off of
>> whether foo.info exists is no longer a viable way to determine
>> if foo is an implicit type.
> 
> Ensuring error messages are good even for implicit types could be hard.
> But pretty much anything's better than error messages without location
> information.

Especially since the current implementation crashes hard when trying to
report an error with info=None.

> 
>> Rename the info member to _info (so that sub-classes can still
>> use it, but external code should not), add an is_implicit()
>> method to QAPISchemaObjectType, and adjust the visitor to pass
>> another parameter about whether the type is implicit.
> 
> I have doubts on the rename.

Fair enough; the proposal to separate it into its own patch, so we can
then discard or easily revert it, sounds like the right approach.


>>  class QAPISchemaObjectType(QAPISchemaType):
>> @@ -961,21 +961,25 @@ class QAPISchemaObjectType(QAPISchemaType):
>>              self.variants.check(schema, members, seen)
>>          self.members = members
>>
>> +    def is_implicit(self):
>> +        return self.name[0] == ':'
>> +
> 
> The predicate could be defined on any QAPISchemaType, or even any
> QAPISchemaEntity, but right now we only ever want to test it for
> objects.  Okay.

Yeah, I thought about that.  All builtin types are implicit, all array
types are implicit, no commands or events are implicit, and we didn't
make any different generated output based on whether enums were explicit
or implicit, so that leaves just objects.

>> +++ b/tests/qapi-schema/test-qapi.py
>> @@ -22,7 +22,7 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
>>          if prefix:
>>              print '    prefix %s' % prefix
>>
>> -    def visit_object_type(self, name, info, base, members, variants):
>> +    def visit_object_type(self, name, info, base, members, variants, implicit):
>>          print 'object %s' % name
>>          if base:
>>              print '    base %s' % base.name
> 
> Three of our visitors implement visit_object_type():

Another idea would be change the signature from:

def visit_object_type(self (QAPISchemaVisitor), name (str),
                      info (dict), base (QEMUSchemaObjectType),
                      members (list of QEMUSchemaObjectTypeMember),
                      variants (QAPISchemaObjectTypeVariants),
                      implicit (bool))

to:

def visit_object_type(self, object (QEMUSchemaObjectType))

and let callers dereference object.name, object.info, object.base,
object.members (or object.local_members), object.variants, and
object.is_implicit() as they see fit. (In fact, in one of my later
patches, I already wished I had access to the actual
QEMUSchemaObjectType object rather than its breakdown of parts to begin
with, and ended up doing a schema.lookupType(name) just to get back to
that piece of information).


> 
> * test-qapi.py doesn't care about implicit (implicitness is obvious
>   enough from the name here).
> 
> * qapi-types.py and qapi-visit.py ignore implicit object types.  Hmm.
> 
>   qapi-introspect.py has a similar need: it wants to ignore *all* types.
>   Two ways to ignore entities seem one too many.  Preexisting, but your
>   patch makes it stand out a bit more.
> 
>   Could we reuse the existing mechanism somehow (and keep method
>   visit_object_type() simple)?
> 
>   To reuse it without changes, we'd have to make implicit object types a
>   separate class, so that QAPISchema.visit()'s isinstance() test can be
>   put to work.

Maybe. Would also make implementing is_implicit() easy (which type did I
instantiate) rather than hacky (does name start with ':').

> 
>   Another option is generalizing QAPISchema's filter.  How?
> 
>   A third option is to abandon QAPISchema's filter, and make
>   qapi-introspect.py filter in the visitor methods, just like we filter
>   implicit objects.

I'm still thinking about this one.

> 
> Patch could be split into
> 
> A. Encapsulate the "is implicit" predicate in a method, i.e. replace
>    not o.info by o.is_implicit().
> 
> B. Clean up how we filter out implicit objects.  May better go before A,
>    not sure.
> 
> C. Rename .info to ._info.  Not sure we even want this part.

Yes, I'll go along with a split somewhere along these lines before
reposting this patch for v6, although I'm going to have to sleep on it
before deciding how to clean up the filtering.
Markus Armbruster Sept. 29, 2015, 7:51 a.m. UTC | #3
Eric Blake <eblake@redhat.com> writes:

> On 09/28/2015 06:43 AM, Markus Armbruster wrote:
>> Eric Blake <eblake@redhat.com> writes:
>> 
>>> A future patch will enable error reporting from the various
>>> check() methods.  But to report an error on an implicit type,
>>> we'll need to associate a location with the type (the same
>>> location as the top-level entity that is causing the creation
>>> of the implicit type), and once we do that, keying off of
>>> whether foo.info exists is no longer a viable way to determine
>>> if foo is an implicit type.
>> 
>> Ensuring error messages are good even for implicit types could be hard.
>> But pretty much anything's better than error messages without location
>> information.
>
> Especially since the current implementation crashes hard when trying to
> report an error with info=None.
>
>> 
>>> Rename the info member to _info (so that sub-classes can still
>>> use it, but external code should not), add an is_implicit()
>>> method to QAPISchemaObjectType, and adjust the visitor to pass
>>> another parameter about whether the type is implicit.
>> 
>> I have doubts on the rename.
>
> Fair enough; the proposal to separate it into its own patch, so we can
> then discard or easily revert it, sounds like the right approach.
[...]
> So far, we've used the '_' prefix only for instance variables that are
> clearly internal.  Mostly for stuff flowing from __init__() to check().

I'd rather not rename it at all now.  Stick to our present use of the
'_' prefix.

>>>  class QAPISchemaObjectType(QAPISchemaType):
>>> @@ -961,21 +961,25 @@ class QAPISchemaObjectType(QAPISchemaType):
>>>              self.variants.check(schema, members, seen)
>>>          self.members = members
>>>
>>> +    def is_implicit(self):
>>> +        return self.name[0] == ':'
>>> +
>> 
>> The predicate could be defined on any QAPISchemaType, or even any
>> QAPISchemaEntity, but right now we only ever want to test it for
>> objects.  Okay.
>
> Yeah, I thought about that.  All builtin types are implicit, all array
> types are implicit, no commands or events are implicit, and we didn't
> make any different generated output based on whether enums were explicit
> or implicit, so that leaves just objects.
>
>>> +++ b/tests/qapi-schema/test-qapi.py
>>> @@ -22,7 +22,7 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
>>>          if prefix:
>>>              print '    prefix %s' % prefix
>>>
>>> -    def visit_object_type(self, name, info, base, members, variants):
>>> +    def visit_object_type(self, name, info, base, members, variants, implicit):
>>>          print 'object %s' % name
>>>          if base:
>>>              print '    base %s' % base.name
>> 
>> Three of our visitors implement visit_object_type():
>
> Another idea would be change the signature from:
>
> def visit_object_type(self (QAPISchemaVisitor), name (str),
>                       info (dict), base (QEMUSchemaObjectType),
>                       members (list of QEMUSchemaObjectTypeMember),
>                       variants (QAPISchemaObjectTypeVariants),
>                       implicit (bool))
>
> to:
>
> def visit_object_type(self, object (QEMUSchemaObjectType))
>
> and let callers dereference object.name, object.info, object.base,
> object.members (or object.local_members), object.variants, and
> object.is_implicit() as they see fit. (In fact, in one of my later
> patches, I already wished I had access to the actual
> QEMUSchemaObjectType object rather than its breakdown of parts to begin
> with, and ended up doing a schema.lookupType(name) just to get back to
> that piece of information).

If you apply this idea across the board, all the visit_FOO() take
exactly two parameters, self and a FOO.  Feels a bit like declaring
bankruptcy on the visitor pattern...  You start to wonder why we need a
separate visit_FOO().

Nevertheless, I've felt the temptation myself.

>> * test-qapi.py doesn't care about implicit (implicitness is obvious
>>   enough from the name here).
>> 
>> * qapi-types.py and qapi-visit.py ignore implicit object types.  Hmm.
>> 
>>   qapi-introspect.py has a similar need: it wants to ignore *all* types.
>>   Two ways to ignore entities seem one too many.  Preexisting, but your
>>   patch makes it stand out a bit more.
>> 
>>   Could we reuse the existing mechanism somehow (and keep method
>>   visit_object_type() simple)?
>> 
>>   To reuse it without changes, we'd have to make implicit object types a
>>   separate class, so that QAPISchema.visit()'s isinstance() test can be
>>   put to work.
>
> Maybe. Would also make implementing is_implicit() easy (which type did I
> instantiate) rather than hacky (does name start with ':').

I don't mind the hacky bit, since you encapsulate it.

>>   Another option is generalizing QAPISchema's filter.  How?

We can always add an indirection: instead of parameterizing a fixed
predicate (ignore and isinstance(entity, ignore)) with a type ignore, we
could pass a predicate, i.e. a function mapping entity to bool.

>>   A third option is to abandon QAPISchema's filter, and make
>>   qapi-introspect.py filter in the visitor methods, just like we filter
>>   implicit objects.
>
> I'm still thinking about this one.
>
>> 
>> Patch could be split into
>> 
>> A. Encapsulate the "is implicit" predicate in a method, i.e. replace
>>    not o.info by o.is_implicit().
>> 
>> B. Clean up how we filter out implicit objects.  May better go before A,
>>    not sure.
>> 
>> C. Rename .info to ._info.  Not sure we even want this part.
>
> Yes, I'll go along with a split somewhere along these lines before
> reposting this patch for v6, although I'm going to have to sleep on it
> before deciding how to clean up the filtering.

Sounds good.
diff mbox

Patch

diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index b292682..aa25e03 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -253,8 +253,8 @@  class QAPISchemaGenTypeVisitor(QAPISchemaVisitor):
             self.decl += gen_array(name, element_type)
             self._gen_type_cleanup(name)

-    def visit_object_type(self, name, info, base, members, variants):
-        if info:
+    def visit_object_type(self, name, info, base, members, variants, implicit):
+        if not implicit:
             self._fwdecl += gen_fwd_object_or_array(name)
             if variants:
                 assert not members      # not implemented
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 1f287ba..62a47fa 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -348,8 +348,8 @@  class QAPISchemaGenVisitVisitor(QAPISchemaVisitor):
             self.decl += decl
             self.defn += defn

-    def visit_object_type(self, name, info, base, members, variants):
-        if info:
+    def visit_object_type(self, name, info, base, members, variants, implicit):
+        if not implicit:
             self.decl += gen_visit_decl(name)
             if variants:
                 assert not members      # not implemented
diff --git a/scripts/qapi.py b/scripts/qapi.py
index 7275daa..1dc7641 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -786,7 +786,7 @@  class QAPISchemaEntity(object):
     def __init__(self, name, info):
         assert isinstance(name, str)
         self.name = name
-        self.info = info
+        self._info = info

     def c_name(self):
         return c_name(self.name)
@@ -814,7 +814,7 @@  class QAPISchemaVisitor(object):
     def visit_array_type(self, name, info, element_type):
         pass

-    def visit_object_type(self, name, info, base, members, variants):
+    def visit_object_type(self, name, info, base, members, variants, implicit):
         pass

     def visit_object_type_flat(self, name, info, members, variants):
@@ -877,7 +877,7 @@  class QAPISchemaBuiltinType(QAPISchemaType):
         return self._json_type_name

     def visit(self, visitor):
-        visitor.visit_builtin_type(self.name, self.info, self.json_type())
+        visitor.visit_builtin_type(self.name, self._info, self.json_type())


 class QAPISchemaEnumType(QAPISchemaType):
@@ -903,7 +903,7 @@  class QAPISchemaEnumType(QAPISchemaType):
         return 'string'

     def visit(self, visitor):
-        visitor.visit_enum_type(self.name, self.info,
+        visitor.visit_enum_type(self.name, self._info,
                                 self.values, self.prefix)


@@ -922,7 +922,7 @@  class QAPISchemaArrayType(QAPISchemaType):
         return 'array'

     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)


 class QAPISchemaObjectType(QAPISchemaType):
@@ -961,21 +961,25 @@  class QAPISchemaObjectType(QAPISchemaType):
             self.variants.check(schema, members, seen)
         self.members = members

+    def is_implicit(self):
+        return self.name[0] == ':'
+
     def c_name(self):
-        assert self.info
+        assert not self.is_implicit()
         return QAPISchemaType.c_name(self)

     def c_type(self, is_param=False):
-        assert self.info
+        assert not self.is_implicit()
         return QAPISchemaType.c_type(self)

     def json_type(self):
         return 'object'

     def visit(self, visitor):
-        visitor.visit_object_type(self.name, self.info,
-                                  self.base, self.local_members, self.variants)
-        visitor.visit_object_type_flat(self.name, self.info,
+        visitor.visit_object_type(self.name, self._info,
+                                  self.base, self.local_members, self.variants,
+                                  self.is_implicit())
+        visitor.visit_object_type_flat(self.name, self._info,
                                        self.members, self.variants)


@@ -1034,7 +1038,8 @@  class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
     # This function exists to support ugly simple union special cases
     # TODO get rid of them, and drop the function
     def simple_union_type(self):
-        if isinstance(self.type, QAPISchemaObjectType) and not self.type.info:
+        if isinstance(self.type, QAPISchemaObjectType) and \
+           self.type.is_implicit():
             assert len(self.type.members) == 1
             assert not self.type.variants
             return self.type.members[0].type
@@ -1055,7 +1060,7 @@  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)


 class QAPISchemaCommand(QAPISchemaEntity):
@@ -1080,7 +1085,7 @@  class QAPISchemaCommand(QAPISchemaEntity):
             assert isinstance(self.ret_type, QAPISchemaType)

     def visit(self, visitor):
-        visitor.visit_command(self.name, self.info,
+        visitor.visit_command(self.name, self._info,
                               self.arg_type, self.ret_type,
                               self.gen, self.success_response)

@@ -1099,7 +1104,7 @@  class QAPISchemaEvent(QAPISchemaEntity):
             assert not self.arg_type.variants   # not implemented

     def visit(self, visitor):
-        visitor.visit_event(self.name, self.info, self.arg_type)
+        visitor.visit_event(self.name, self._info, self.arg_type)


 class QAPISchema(object):
diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
index 649677e..f2cce64 100644
--- a/tests/qapi-schema/test-qapi.py
+++ b/tests/qapi-schema/test-qapi.py
@@ -22,7 +22,7 @@  class QAPISchemaTestVisitor(QAPISchemaVisitor):
         if prefix:
             print '    prefix %s' % prefix

-    def visit_object_type(self, name, info, base, members, variants):
+    def visit_object_type(self, name, info, base, members, variants, implicit):
         print 'object %s' % name
         if base:
             print '    base %s' % base.name