Blame common/scangobj-merge.py

Packit f546b1
#!/usr/bin/python
Packit f546b1
# -*- Mode: Python -*-
Packit f546b1
# vi:si:et:sw=4:sts=4:ts=4
Packit f546b1
Packit f546b1
"""
Packit f546b1
parse, merge and write gstdoc-scanobj files
Packit f546b1
"""
Packit f546b1
Packit f546b1
from __future__ import print_function, unicode_literals
Packit f546b1
Packit f546b1
import codecs
Packit f546b1
import os
Packit f546b1
import sys
Packit f546b1
Packit f546b1
def debug(*args):
Packit f546b1
    pass
Packit f546b1
Packit f546b1
# OrderedDict class based on
Packit f546b1
# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/107747
Packit f546b1
# Licensed under the Python License
Packit f546b1
class OrderedDict(dict):
Packit f546b1
    def __init__(self):
Packit f546b1
        self._keys = []
Packit f546b1
        dict.__init__(self)
Packit f546b1
Packit f546b1
    def __delitem__(self, key):
Packit f546b1
        dict.__delitem__(self, key)
Packit f546b1
        self._keys.remove(key)
Packit f546b1
Packit f546b1
    def __setitem__(self, key, item):
Packit f546b1
        dict.__setitem__(self, key, item)
Packit f546b1
        if key not in self._keys: self._keys.append(key)
Packit f546b1
Packit f546b1
    def clear(self):
Packit f546b1
        dict.clear(self)
Packit f546b1
        self._keys = []
Packit f546b1
Packit f546b1
    def copy(self):
Packit f546b1
        dict = dict.copy(self)
Packit f546b1
        dict._keys = self._keys[:]
Packit f546b1
        return dict
Packit f546b1
Packit f546b1
    def items(self):
Packit f546b1
        return zip(self._keys, self.values())
Packit f546b1
Packit f546b1
    def keys(self):
Packit f546b1
        return self._keys
Packit f546b1
Packit f546b1
    def popitem(self):
Packit f546b1
        try:
Packit f546b1
            key = self._keys[-1]
Packit f546b1
        except IndexError:
Packit f546b1
            raise KeyError('dictionary is empty')
Packit f546b1
Packit f546b1
        val = self[key]
Packit f546b1
        del self[key]
Packit f546b1
Packit f546b1
        return (key, val)
Packit f546b1
Packit f546b1
    def setdefault(self, key, failobj = None):
Packit f546b1
        dict.setdefault(self, key, failobj)
Packit f546b1
        if key not in self._keys: self._keys.append(key)
Packit f546b1
Packit f546b1
    def update(self, dict):
Packit f546b1
        dict.update(self, dict)
Packit f546b1
        for key in dict.keys():
Packit f546b1
            if key not in self._keys: self._keys.append(key)
Packit f546b1
Packit f546b1
    def values(self):
Packit f546b1
        return map(self.get, self._keys)
Packit f546b1
Packit f546b1
class Object:
Packit f546b1
    def __init__(self, name):
Packit f546b1
        self._signals = OrderedDict()
Packit f546b1
        self._args = OrderedDict()
Packit f546b1
        self.name = name
Packit f546b1
Packit f546b1
    def __repr__(self):
Packit f546b1
        return "<Object %s>" % self.name
Packit f546b1
Packit f546b1
    def add_signal(self, signal, overwrite=True):
Packit f546b1
        if not overwrite and signal.name in self._signals:
Packit f546b1
            raise IndexError("signal %s already in %r" % (signal.name, self))
Packit f546b1
        self._signals[signal.name] = signal
Packit f546b1
Packit f546b1
    def add_arg(self, arg, overwrite=True):
Packit f546b1
        if not overwrite and arg.name in self._args:
Packit f546b1
            raise IndexError("arg %s already in %r" % (arg.name, self))
Packit f546b1
        self._args[arg.name] = arg
Packit f546b1
Packit f546b1
class Docable:
Packit f546b1
    def __init__(self, **kwargs):
Packit f546b1
        for key in self.attrs:
Packit f546b1
            setattr(self, key, kwargs[key])
Packit f546b1
        self.dict = kwargs
Packit f546b1
Packit f546b1
    def __repr__(self):
Packit f546b1
        return "<%r %s>" % (str(self.__class__), self.name)
Packit f546b1
Packit f546b1
class Signal(Docable):
Packit f546b1
    attrs = ['name', 'returns', 'args']
Packit f546b1
Packit f546b1
class Arg(Docable):
Packit f546b1
    attrs = ['name', 'type', 'range', 'flags', 'nick', 'blurb', 'default']
Packit f546b1
Packit f546b1
class GDoc:
Packit f546b1
    def load_file(self, filename):
Packit f546b1
        try:
Packit f546b1
            lines = codecs.open(filename, encoding='utf-8').readlines()
Packit f546b1
            self.load_data("".join(lines))
Packit f546b1
        except IOError:
Packit f546b1
            print ("WARNING - could not read from %s" % filename)
Packit f546b1
        except UnicodeDecodeError as e:
Packit f546b1
            print ("WARNING - could not parse %s: %s" % (filename, e))
Packit f546b1
Packit f546b1
    def save_file(self, filename, backup=False):
Packit f546b1
        """
Packit f546b1
        Save the information to the given file if the file content changed.
Packit f546b1
        """
Packit f546b1
        olddata = None
Packit f546b1
        try:
Packit f546b1
            lines = codecs.open(filename, encoding='utf-8').readlines()
Packit f546b1
            olddata = "".join(lines)
Packit f546b1
        except IOError:
Packit f546b1
            print ("WARNING - could not read from %s" % filename)
Packit f546b1
        newdata = self.get_data()
Packit f546b1
        if olddata and olddata == newdata:
Packit f546b1
            return
Packit f546b1
Packit f546b1
        if olddata:
Packit f546b1
            if backup:
Packit f546b1
                os.rename(filename, filename + '.bak')
Packit f546b1
Packit f546b1
        handle = codecs.open(filename, "w", encoding='utf-8')
Packit f546b1
        handle.write(newdata)
Packit f546b1
        handle.close()
Packit f546b1
Packit f546b1
class Signals(GDoc):
Packit f546b1
    def __init__(self):
Packit f546b1
        self._objects = OrderedDict()
Packit f546b1
Packit f546b1
    def load_data(self, data):
Packit f546b1
        """
Packit f546b1
        Load the .signals lines, creating our list of objects and signals.
Packit f546b1
        """
Packit f546b1
        import re
Packit f546b1
        smatcher = re.compile(
Packit f546b1
            '(?s)'                                      # make . match \n
Packit f546b1
            '<SIGNAL>\n(.*?)</SIGNAL>\n'
Packit f546b1
            )
Packit f546b1
        nmatcher = re.compile(
Packit f546b1
            '<NAME>'
Packit f546b1
            '(?P<object>\S*)'                           # store object
Packit f546b1
            '::'
Packit f546b1
            '(?P<signal>\S*)'                           # store signal
Packit f546b1
            '</NAME>'
Packit f546b1
        )
Packit f546b1
        rmatcher = re.compile(
Packit f546b1
            '(?s)'                                      # make . match \n
Packit f546b1
            '<RETURNS>(?P<returns>\S*)</RETURNS>\n'     # store returns
Packit f546b1
            '(?P<args>.*)'                              # store args
Packit f546b1
        )
Packit f546b1
        for block in smatcher.findall(data):
Packit f546b1
            nmatch = nmatcher.search(block)
Packit f546b1
            if nmatch:
Packit f546b1
                o = nmatch.group('object')
Packit f546b1
                debug("Found object", o)
Packit f546b1
                debug("Found signal", nmatch.group('signal'))
Packit f546b1
                if o not in self._objects:
Packit f546b1
                    object = Object(o)
Packit f546b1
                    self._objects[o] = object
Packit f546b1
Packit f546b1
                rmatch = rmatcher.search(block)
Packit f546b1
                if rmatch:
Packit f546b1
                    dict = rmatch.groupdict().copy()
Packit f546b1
                    dict['name'] = nmatch.group('signal')
Packit f546b1
                    signal = Signal(**dict)
Packit f546b1
                    self._objects[o].add_signal(signal)
Packit f546b1
Packit f546b1
    def get_data(self):
Packit f546b1
        lines = []
Packit f546b1
        for o in self._objects.values():
Packit f546b1
            for s in o._signals.values():
Packit f546b1
                block = """<SIGNAL>
Packit f546b1
<NAME>%(object)s::%(name)s</NAME>
Packit f546b1
<RETURNS>%(returns)s</RETURNS>
Packit f546b1
%(args)s</SIGNAL>
Packit f546b1
"""
Packit f546b1
                d = s.dict.copy()
Packit f546b1
                d['object'] = o.name
Packit f546b1
                lines.append(block % d)
Packit f546b1
Packit f546b1
        return "\n".join(lines) + '\n'
Packit f546b1
Packit f546b1
class Args(GDoc):
Packit f546b1
    def __init__(self):
Packit f546b1
        self._objects = OrderedDict()
Packit f546b1
Packit f546b1
    def load_data(self, data):
Packit f546b1
        """
Packit f546b1
        Load the .args lines, creating our list of objects and args.
Packit f546b1
        """
Packit f546b1
        import re
Packit f546b1
        amatcher = re.compile(
Packit f546b1
            '(?s)'                                      # make . match \n
Packit f546b1
            '<ARG>\n(.*?)</ARG>\n'
Packit f546b1
            )
Packit f546b1
        nmatcher = re.compile(
Packit f546b1
            '<NAME>'
Packit f546b1
            '(?P<object>\S*)'                           # store object
Packit f546b1
            '::'
Packit f546b1
            '(?P<arg>\S*)'                              # store arg
Packit f546b1
            '</NAME>'
Packit f546b1
        )
Packit f546b1
        rmatcher = re.compile(
Packit f546b1
            '(?s)'                                      # make . match \n
Packit f546b1
            '<TYPE>(?P<type>\S*)</TYPE>\n'              # store type
Packit f546b1
            '<RANGE>(?P<range>.*?)</RANGE>\n'           # store range
Packit f546b1
            '<FLAGS>(?P<flags>\S*)</FLAGS>\n'           # store flags
Packit f546b1
            '<NICK>(?P<nick>.*?)</NICK>\n'              # store nick
Packit f546b1
            '<BLURB>(?P<blurb>.*?)</BLURB>\n'           # store blurb
Packit f546b1
            '<DEFAULT>(?P<default>.*?)</DEFAULT>\n'     # store default
Packit f546b1
        )
Packit f546b1
        for block in amatcher.findall(data):
Packit f546b1
            nmatch = nmatcher.search(block)
Packit f546b1
            if nmatch:
Packit f546b1
                o = nmatch.group('object')
Packit f546b1
                debug("Found object", o)
Packit f546b1
                debug("Found arg", nmatch.group('arg'))
Packit f546b1
                if o not in self._objects:
Packit f546b1
                    object = Object(o)
Packit f546b1
                    self._objects[o] = object
Packit f546b1
Packit f546b1
                rmatch = rmatcher.search(block)
Packit f546b1
                if rmatch:
Packit f546b1
                    dict = rmatch.groupdict().copy()
Packit f546b1
                    dict['name'] = nmatch.group('arg')
Packit f546b1
                    arg = Arg(**dict)
Packit f546b1
                    self._objects[o].add_arg(arg)
Packit f546b1
                else:
Packit f546b1
                    print ("ERROR: could not match arg from block %s" % block)
Packit f546b1
Packit f546b1
    def get_data(self):
Packit f546b1
        lines = []
Packit f546b1
        for o in self._objects.values():
Packit f546b1
            for a in o._args.values():
Packit f546b1
                block = """<ARG>
Packit f546b1
<NAME>%(object)s::%(name)s</NAME>
Packit f546b1
<TYPE>%(type)s</TYPE>
Packit f546b1
<RANGE>%(range)s</RANGE>
Packit f546b1
<FLAGS>%(flags)s</FLAGS>
Packit f546b1
<NICK>%(nick)s</NICK>
Packit f546b1
<BLURB>%(blurb)s</BLURB>
Packit f546b1
<DEFAULT>%(default)s</DEFAULT>
Packit f546b1
</ARG>
Packit f546b1
"""
Packit f546b1
                d = a.dict.copy()
Packit f546b1
                d['object'] = o.name
Packit f546b1
                lines.append(block % d)
Packit f546b1
Packit f546b1
        return "\n".join(lines) + '\n'
Packit f546b1
Packit f546b1
class SingleLine(GDoc):
Packit f546b1
    def __init__(self):
Packit f546b1
        self._objects = []
Packit f546b1
Packit f546b1
    def load_data(self, data):
Packit f546b1
        """
Packit f546b1
        Load the .interfaces/.prerequisites lines, merge duplicates
Packit f546b1
        """
Packit f546b1
        # split data on '\n'
Packit f546b1
        lines = data.splitlines();
Packit f546b1
        # merge them into self._objects
Packit f546b1
        for line in lines:
Packit f546b1
            if line not in self._objects:
Packit f546b1
                self._objects.append(line)
Packit f546b1
Packit f546b1
    def get_data(self):
Packit f546b1
        lines = sorted(self._objects)
Packit f546b1
        return "\n".join(lines) + '\n'
Packit f546b1
Packit f546b1
def main(argv):
Packit f546b1
    modulename = None
Packit f546b1
    try:
Packit f546b1
        modulename = argv[1]
Packit f546b1
    except IndexError:
Packit f546b1
        sys.stderr.write('Please provide a documentation module name\n')
Packit f546b1
        sys.exit(1)
Packit f546b1
Packit f546b1
    signals = Signals()
Packit f546b1
    signals.load_file(modulename + '.signals')
Packit f546b1
    signals.load_file(modulename + '.signals.new')
Packit f546b1
    signals.save_file(modulename + '.signals', backup=True)
Packit f546b1
    os.unlink(modulename + '.signals.new')
Packit f546b1
Packit f546b1
    args = Args()
Packit f546b1
    args.load_file(modulename + '.args')
Packit f546b1
    args.load_file(modulename + '.args.new')
Packit f546b1
    args.save_file(modulename + '.args', backup=True)
Packit f546b1
    os.unlink(modulename + '.args.new')
Packit f546b1
Packit f546b1
    ifaces = SingleLine()
Packit f546b1
    ifaces.load_file(modulename + '.interfaces')
Packit f546b1
    ifaces.load_file(modulename + '.interfaces.new')
Packit f546b1
    ifaces.save_file(modulename + '.interfaces', backup=True)
Packit f546b1
    os.unlink(modulename + '.interfaces.new')
Packit f546b1
Packit f546b1
    prereq = SingleLine()
Packit f546b1
    prereq.load_file(modulename + '.prerequisites')
Packit f546b1
    prereq.load_file(modulename + '.prerequisites.new')
Packit f546b1
    prereq.save_file(modulename + '.prerequisites', backup=True)
Packit f546b1
    os.unlink(modulename + '.prerequisites.new')
Packit f546b1
Packit f546b1
main(sys.argv)