introspectionvalapygobjectgobjectvapi

What is the relationship between Vala VAPI and GObject Introspection?


First, some context: I'm a Python developer who has written a medium-sized application using PyGObject, taking advantage of GObject Introspection to access things like GSettings, etc. Some of my Python objects actually subclass GObject.GObject, so I'm using GObject quite extensively.

Recently, a certain library has come to my attention that wraps a C library in GObject (gexiv2, used by Shotwell/Vala), however it doesn't currently support introspection. I'm interested in adding introspection support to gexiv2 so that I can access it from Python, but I don't even know where to begin on this topic.

When I research introspection and VAPI, I see lots of documentation referencing the fact that the VAPI can be automatically generated from the introspection annotations... but what about a project that already has a VAPI, but no introspection? Is it possible to automatically generate the introspection annotations given the VAPI?

Thanks.


Solution

  • Well, after getting sick of the tediousness of hand-copying VAPI definitions into introspection annotations, I wrote this (crude) script to do it for me:

    #!/bin/env python
    
    import sys
    
    from collections import defaultdict
    
    ANNOTATION = """/**
     * %s:
    %s *
     * Returns:%s
     */
    """
    
    PARAMETER = """ * @%s:%s
    """
    
    methods = defaultdict(set)
    
    attrs = defaultdict(dict)
    
    with open(sys.argv[1]) as vapi:
        for line in vapi:
            tokens = line.split()
            try:
                names = tuple(tokens[0].split('.'))
            except IndexError:
                continue
    
            attrs[names] = {}
            for attribute in tokens[1:]:
                key, val = attribute.split('=')
                if val == '"1"': val = True
                if val == '"0"': val = False
                attrs[names][key] = val
    
            methods[names[0]]
            if len(names) > 1:
                methods[names[0]].add(names[-1])
    
    for method in methods:
        params = ''
        for param in methods[method]:
            param_attributes = ''
            param_attrs = attrs[(method, param)]
            if param_attrs.get('hidden'):
                param_attributes += ' (skip)'
            if param_attrs.get('is_out'):
                param_attributes += ' (out)'
            if param_attrs.get('transfer_ownership'):
                param_attributes += ' (transfer full)'
            elif 'transfer_ownership' in param_attrs:
                param_attributes += ' (transfer none)'
            if param_attrs.get('array_null_terminated'):
                param_attributes += ' (array zero-terminated=1)'
            if param_attrs.get('array_length_pos'):
                param_attributes += ' (array length=FIXME)'
            if param_attributes:
                param_attributes += ':'
            params += PARAMETER % (param, param_attributes)
    
        attributes = ''
        method_attrs = attrs[(method,)]
        if method_attrs.get('transfer_ownership'):
            attributes += ' (transfer full)'
        elif 'transfer_ownership' in method_attrs:
            attributes += ' (transfer none)'
        if method_attrs.get('nullable'):
            attributes += ' (allow-none)'
        if method_attrs.get('array_null_terminated'):
            attributes += ' (array zero-terminated=1)'
        if attributes:
            attributes += ':'
    
        print ANNOTATION % (method, params, attributes)
    

    This obviously has some disadvantages: It doesn't insert the annotations into the code, it simply prints them, so you have to do quite a bit of copy & pasting to get everything into the right place. It also doesn't handle arrays very well, but it at least lets you know when there's an array you need to fix manually. All in all, it was significantly less work to run this script and then massage the results than it was to parse by hand. I'm posting it here in the hopes that it gets picked up by google and somebody else may benefit one day (although I dearly hope that all GObject-based projects from here on out simply start with annotations and then use vapigen).