doxy2swig.py

来自「一个人工神经网络的程序。 文档等说明参见http://aureservoir.」· Python 代码 · 共 429 行

PY
429
字号
#!/usr/bin/env python"""Doxygen XML to SWIG docstring converter.Usage:  doxy2swig.py [options] input.xml output.iConverts Doxygen generated XML files into a file containing docstringsthat can be used by SWIG-1.3.x.  Note that you need to get SWIGversion > 1.3.23 or use Robin Dunn's docstring patch to be able to usethe resulting output.input.xml is your doxygen generated XML file and output.i is where theoutput will be written (the file will be clobbered)."""######################################################################## This code is implemented using Mark Pilgrim's code as a guideline:#   http://www.faqs.org/docs/diveintopython/kgp_divein.html## Author: Prabhu Ramachandran# License: BSD style## Thanks:#   Johan Hake:  the include_function_definition feature#   Bill Spotz:  bug reports and testing.#######################################################################from xml.dom import minidomimport reimport textwrapimport sysimport typesimport os.pathimport optparsedef my_open_read(source):    if hasattr(source, "read"):        return source    else:        return open(source)def my_open_write(dest):    if hasattr(dest, "write"):        return dest    else:        return open(dest, 'w')class Doxy2SWIG:        """Converts Doxygen generated XML files into a file containing    docstrings that can be used by SWIG-1.3.x that have support for    feature("docstring").  Once the data is parsed it is stored in    self.pieces.    """            def __init__(self, src, include_function_definition=True):        """Initialize the instance given a source object.  `src` can        be a file or filename.  If you do not want to include function        definitions from doxygen then set        `include_function_definition` to `False`.  This is handy since        this allows you to use the swig generated function definition        using %feature("autodoc", [0,1]).        """        f = my_open_read(src)        self.my_dir = os.path.dirname(f.name)        self.xmldoc = minidom.parse(f).documentElement        f.close()        self.pieces = []        self.pieces.append('\n// File: %s\n'%\                           os.path.basename(f.name))        self.space_re = re.compile(r'\s+')        self.lead_spc = re.compile(r'^(%feature\S+\s+\S+\s*?)"\s+(\S)')        self.multi = 0        self.ignores = ['inheritancegraph', 'param', 'listofallmembers',                        'innerclass', 'name', 'declname', 'incdepgraph',                        'invincdepgraph', 'programlisting', 'type',                        'references', 'referencedby', 'location',                        'collaborationgraph', 'reimplements',                        'reimplementedby', 'derivedcompoundref',                        'basecompoundref']        #self.generics = []        self.include_function_definition = include_function_definition        if not include_function_definition:            self.ignores.append('argsstring')                        def generate(self):        """Parses the file set in the initialization.  The resulting        data is stored in `self.pieces`.        """        self.parse(self.xmldoc)        def parse(self, node):        """Parse a given node.  This function in turn calls the        `parse_<nodeType>` functions which handle the respective        nodes.        """        pm = getattr(self, "parse_%s"%node.__class__.__name__)        pm(node)    def parse_Document(self, node):        self.parse(node.documentElement)    def parse_Text(self, node):        txt = node.data        txt = txt.replace('\\', r'\\\\')        txt = txt.replace('"', r'\"')        # ignore pure whitespace        m = self.space_re.match(txt)        if m and len(m.group()) == len(txt):            pass        else:            self.add_text(textwrap.fill(txt, break_long_words=False))    def parse_Element(self, node):        """Parse an `ELEMENT_NODE`.  This calls specific        `do_<tagName>` handers for different elements.  If no handler        is available the `generic_parse` method is called.  All        tagNames specified in `self.ignores` are simply ignored.                """        name = node.tagName        ignores = self.ignores        if name in ignores:            return        attr = "do_%s" % name        if hasattr(self, attr):            handlerMethod = getattr(self, attr)            handlerMethod(node)        else:            self.generic_parse(node)            #if name not in self.generics: self.generics.append(name)    def parse_Comment(self, node):        """Parse a `COMMENT_NODE`.  This does nothing for now."""        return    def add_text(self, value):        """Adds text corresponding to `value` into `self.pieces`."""        if type(value) in (types.ListType, types.TupleType):            self.pieces.extend(value)        else:            self.pieces.append(value)    def get_specific_nodes(self, node, names):        """Given a node and a sequence of strings in `names`, return a        dictionary containing the names as keys and child        `ELEMENT_NODEs`, that have a `tagName` equal to the name.        """        nodes = [(x.tagName, x) for x in node.childNodes \                 if x.nodeType == x.ELEMENT_NODE and \                 x.tagName in names]        return dict(nodes)    def generic_parse(self, node, pad=0):        """A Generic parser for arbitrary tags in a node.        Parameters:         - node:  A node in the DOM.         - pad: `int` (default: 0)           If 0 the node data is not padded with newlines.  If 1 it           appends a newline after parsing the childNodes.  If 2 it           pads before and after the nodes are processed.  Defaults to           0.        """        npiece = 0        if pad:            npiece = len(self.pieces)            if pad == 2:                self.add_text('\n')                        for n in node.childNodes:            self.parse(n)        if pad:            if len(self.pieces) > npiece:                self.add_text('\n')    def space_parse(self, node):        self.add_text(' ')        self.generic_parse(node)    do_ref = space_parse    do_emphasis = space_parse    do_bold = space_parse    do_computeroutput = space_parse    do_formula = space_parse    def do_compoundname(self, node):        self.add_text('\n\n')        data = node.firstChild.data        self.add_text('%%feature("docstring") %s "\n'%data)    def do_compounddef(self, node):        kind = node.attributes['kind'].value        if kind in ('class', 'struct'):            prot = node.attributes['prot'].value            if prot <> 'public':                return            names = ('compoundname', 'briefdescription',                     'detaileddescription', 'includes')            first = self.get_specific_nodes(node, names)            for n in names:                if first.has_key(n):                    self.parse(first[n])            self.add_text(['";','\n'])            for n in node.childNodes:                if n not in first.values():                    self.parse(n)        elif kind in ('file', 'namespace'):            nodes = node.getElementsByTagName('sectiondef')            for n in nodes:                self.parse(n)    def do_includes(self, node):        self.add_text('C++ includes: ')        self.generic_parse(node, pad=1)    def do_parameterlist(self, node):        self.add_text(['\n', '\n', 'Parameters:', '\n'])        self.generic_parse(node, pad=1)    def do_para(self, node):        self.add_text('\n')        self.generic_parse(node, pad=1)    def do_parametername(self, node):        self.add_text('\n')        self.add_text("%s: "%node.firstChild.data)    def do_parameterdefinition(self, node):        self.generic_parse(node, pad=1)    def do_detaileddescription(self, node):        self.generic_parse(node, pad=1)    def do_briefdescription(self, node):        self.generic_parse(node, pad=1)    def do_memberdef(self, node):        prot = node.attributes['prot'].value        id = node.attributes['id'].value        kind = node.attributes['kind'].value        tmp = node.parentNode.parentNode.parentNode        compdef = tmp.getElementsByTagName('compounddef')[0]        cdef_kind = compdef.attributes['kind'].value                if prot == 'public':            first = self.get_specific_nodes(node, ('definition', 'name'))            name = first['name'].firstChild.data            if name[:8] == 'operator': # Don't handle operators yet.                return            if not first.has_key('definition') or \                   kind in ['variable', 'typedef']:                return            if self.include_function_definition:                defn = first['definition'].firstChild.data            else:                defn = ""            self.add_text('\n')            self.add_text('%feature("docstring") ')                        anc = node.parentNode.parentNode            if cdef_kind in ('file', 'namespace'):                ns_node = anc.getElementsByTagName('innernamespace')                if not ns_node and cdef_kind == 'namespace':                    ns_node = anc.getElementsByTagName('compoundname')                if ns_node:                    ns = ns_node[0].firstChild.data                    self.add_text(' %s::%s "\n%s'%(ns, name, defn))                else:                    self.add_text(' %s "\n%s'%(name, defn))            elif cdef_kind in ('class', 'struct'):                # Get the full function name.                anc_node = anc.getElementsByTagName('compoundname')                cname = anc_node[0].firstChild.data                self.add_text(' %s::%s "\n%s'%(cname, name, defn))            for n in node.childNodes:                if n not in first.values():                    self.parse(n)            self.add_text(['";', '\n'])            def do_definition(self, node):        data = node.firstChild.data        self.add_text('%s "\n%s'%(data, data))    def do_sectiondef(self, node):        kind = node.attributes['kind'].value        if kind in ('public-func', 'func', 'user-defined', ''):            self.generic_parse(node)    def do_header(self, node):        """For a user defined section def a header field is present        which should not be printed as such, so we comment it in the        output."""        data = node.firstChild.data        self.add_text('\n/*\n %s \n*/\n'%data)        # If our immediate sibling is a 'description' node then we        # should comment that out also and remove it from the parent        # node's children.        parent = node.parentNode        idx = parent.childNodes.index(node)        if len(parent.childNodes) >= idx + 2:            nd = parent.childNodes[idx+2]            if nd.nodeName == 'description':                nd = parent.removeChild(nd)                self.add_text('\n/*')                self.generic_parse(nd)                self.add_text('\n*/\n')    def do_simplesect(self, node):        kind = node.attributes['kind'].value        if kind in ('date', 'rcs', 'version'):            pass        elif kind == 'warning':            self.add_text(['\n', 'WARNING: '])            self.generic_parse(node)        elif kind == 'see':            self.add_text('\n')            self.add_text('See: ')            self.generic_parse(node)        else:            self.generic_parse(node)    def do_argsstring(self, node):        self.generic_parse(node, pad=1)    def do_member(self, node):        kind = node.attributes['kind'].value        refid = node.attributes['refid'].value        if kind == 'function' and refid[:9] == 'namespace':            self.generic_parse(node)    def do_doxygenindex(self, node):        self.multi = 1        comps = node.getElementsByTagName('compound')        for c in comps:            refid = c.attributes['refid'].value            fname = refid + '.xml'            if not os.path.exists(fname):                fname = os.path.join(self.my_dir,  fname)            print "parsing file: %s"%fname            p = Doxy2SWIG(fname)            p.generate()            self.pieces.extend(self.clean_pieces(p.pieces))    def write(self, fname):        o = my_open_write(fname)        if self.multi:            o.write("".join(self.pieces))        else:            o.write("".join(self.clean_pieces(self.pieces)))        o.close()    def clean_pieces(self, pieces):        """Cleans the list of strings given as `pieces`.  It replaces        multiple newlines by a maximum of 2 and returns a new list.        It also wraps the paragraphs nicely.                """        ret = []        count = 0        for i in pieces:            if i == '\n':                count = count + 1            else:                if i == '";':                    if count:                        ret.append('\n')                elif count > 2:                    ret.append('\n\n')                elif count:                    ret.append('\n'*count)                count = 0                ret.append(i)        _data = "".join(ret)        ret = []        for i in _data.split('\n\n'):            if i == 'Parameters:':                ret.extend(['Parameters:\n-----------', '\n\n'])            elif i.find('// File:') > -1: # leave comments alone.                ret.extend([i, '\n'])            else:                _tmp = textwrap.fill(i.strip(), break_long_words=False)                _tmp = self.lead_spc.sub(r'\1"\2', _tmp)                ret.extend([_tmp, '\n\n'])        return retdef convert(input, output, include_function_definition=True):    p = Doxy2SWIG(input, include_function_definition)    p.generate()    p.write(output)def main():    usage = __doc__    parser = optparse.OptionParser(usage)    parser.add_option("-n", '--no-function-definition',                      action='store_true',                      default=False,                      dest='func_def',                      help='do not include doxygen function definitions')        options, args = parser.parse_args()    if len(args) != 2:        parser.error("error: no input and output specified")    convert(args[0], args[1], not options.func_def)    if __name__ == '__main__':    main()

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?