Statistics
| Branch: | Tag: | Revision:

root / docs / doxygen / swig_doc.py @ 11d58fe2

History | View | Annotate | Download (8.4 kB)

1
#
2
# Copyright 2010,2011 Free Software Foundation, Inc.
3
# 
4
# This file is part of GNU Radio
5
# 
6
# GNU Radio is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 3, or (at your option)
9
# any later version.
10
# 
11
# GNU Radio is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
# GNU General Public License for more details.
15
# 
16
# You should have received a copy of the GNU General Public License
17
# along with GNU Radio; see the file COPYING.  If not, write to
18
# the Free Software Foundation, Inc., 51 Franklin Street,
19
# Boston, MA 02110-1301, USA.
20
# 
21
"""
22
Creates the swig_doc.i SWIG interface file.
23
Execute using: python swig_doc.py xml_path outputfilename
24
25
The file instructs SWIG to transfer the doxygen comments into the
26
python docstrings.
27
28
"""
29
30
import sys
31
32
try:
33
    from doxyxml import DoxyIndex, DoxyClass, DoxyFriend, DoxyFunction, DoxyFile, base
34
except ImportError:
35
    from gnuradio.doxyxml import DoxyIndex, DoxyClass, DoxyFriend, DoxyFunction, DoxyFile, base
36
37
38
def py_name(name):
39
    bits = name.split('_')
40
    return '_'.join(bits[1:])
41
42
def make_name(name):
43
    bits = name.split('_')
44
    return bits[0] + '_make_' + '_'.join(bits[1:])
45
46
47
class Block(object):
48
    """
49
    Checks if doxyxml produced objects correspond to a gnuradio block.
50
    """
51
52
    @classmethod
53
    def includes(cls, item):
54
        if not isinstance(item, DoxyClass):
55
            return False
56
        # Check for a parsing error.
57
        if item.error():
58
            return False
59
        return item.has_member(make_name(item.name()), DoxyFriend)
60
61
62
def utoascii(text):
63
    """
64
    Convert unicode text into ascii and escape quotes.
65
    """
66
    if text is None:
67
        return ''
68
    out = text.encode('ascii', 'replace')
69
    out = out.replace('"', '\\"')
70
    return out
71
72
73
def combine_descriptions(obj):
74
    """
75
    Combines the brief and detailed descriptions of an object together.
76
    """
77
    description = []
78
    bd = obj.brief_description.strip()
79
    dd = obj.detailed_description.strip()
80
    if bd:
81
        description.append(bd)
82
    if dd:
83
        description.append(dd)
84
    return utoascii('\n\n'.join(description)).strip()
85
    
86
87
entry_templ = '%feature("docstring") {name} "{docstring}"'
88
def make_entry(obj, name=None, templ="{description}", description=None):
89
    """
90
    Create a docstring entry for a swig interface file.
91
    
92
    obj - a doxyxml object from which documentation will be extracted.
93
    name - the name of the C object (defaults to obj.name())
94
    templ - an optional template for the docstring containing only one
95
            variable named 'description'.
96
    description - if this optional variable is set then it's value is
97
            used as the description instead of extracting it from obj.
98
    """
99
    if name is None:
100
        name=obj.name()
101
    if description is None:
102
        description = combine_descriptions(obj)
103
    docstring = templ.format(description=description)
104
    if not docstring:
105
        return ''
106
    return entry_templ.format(
107
        name=name,
108
        docstring=docstring,
109
        )
110
111
112
def make_func_entry(func, name=None, description=None, params=None):
113
    """
114
    Create a function docstring entry for a swig interface file.
115
116
    func - a doxyxml object from which documentation will be extracted.
117
    name - the name of the C object (defaults to func.name())
118
    description - if this optional variable is set then it's value is
119
            used as the description instead of extracting it from func.
120
    params - a parameter list that overrides using func.params.
121
    """
122
    if params is None:
123
        params = func.params
124
    params = [prm.declname for prm in params]
125
    if params:
126
        sig = "Params: (%s)" % ", ".join(params)
127
    else:
128
        sig = "Params: (NONE)"
129
    templ = "{description}\n\n" + sig
130
    return make_entry(func, name=name, templ=utoascii(templ),
131
                      description=description)
132
133
134
def make_class_entry(klass, description=None):
135
    """
136
    Create a class docstring for a swig interface file.
137
    """
138
    output = []
139
    output.append(make_entry(klass, description=description))
140
    for func in klass.in_category(DoxyFunction):
141
        name = klass.name() + '::' + func.name()
142
        output.append(make_func_entry(func, name=name))
143
    return "\n\n".join(output)
144
145
146
def make_block_entry(di, block):
147
    """
148
    Create class and function docstrings of a gnuradio block for a
149
    swig interface file.
150
    """
151
    descriptions = []
152
    # Get the documentation associated with the class.
153
    class_desc = combine_descriptions(block)
154
    if class_desc:
155
        descriptions.append(class_desc)
156
    # Get the documentation associated with the make function
157
    make_func = di.get_member(make_name(block.name()), DoxyFunction)
158
    make_func_desc = combine_descriptions(make_func)
159
    if make_func_desc:
160
        descriptions.append(make_func_desc)
161
    # Get the documentation associated with the file
162
    try:
163
        block_file = di.get_member(block.name() + ".h", DoxyFile)
164
        file_desc = combine_descriptions(block_file)
165
        if file_desc:
166
            descriptions.append(file_desc)
167
    except base.Base.NoSuchMember:
168
        # Don't worry if we can't find a matching file.
169
        pass
170
    # And join them all together to make a super duper description.
171
    super_description = "\n\n".join(descriptions)
172
    # Associate the combined description with the class and
173
    # the make function.
174
    output = []
175
    output.append(make_class_entry(block, description=super_description))
176
    creator = block.get_member(block.name(), DoxyFunction)
177
    output.append(make_func_entry(make_func, description=super_description,
178
                                  params=creator.params))
179
    return "\n\n".join(output)
180
181
182
def make_swig_interface_file(di, swigdocfilename, custom_output=None):
183
    
184
    output = ["""
185
/*
186
 * This file was automatically generated using swig_doc.py.
187
 * 
188
 * Any changes to it will be lost next time it is regenerated.
189
 */
190
"""]
191
192
    if custom_output is not None:
193
        output.append(custom_output)
194
195
    # Create docstrings for the blocks.
196
    blocks = di.in_category(Block)
197
    make_funcs = set([])
198
    for block in blocks:
199
        try:
200
            make_func = di.get_member(make_name(block.name()), DoxyFunction)
201
            make_funcs.add(make_func.name())
202
            output.append(make_block_entry(di, block))
203
        except block.ParsingError:
204
            print('Parsing error for block %s' % block.name())
205
206
    # Create docstrings for functions
207
    # Don't include the make functions since they have already been dealt with.
208
    funcs = [f for f in di.in_category(DoxyFunction) if f.name() not in make_funcs]
209
    for f in funcs:
210
        try:
211
            output.append(make_func_entry(f))
212
        except f.ParsingError:
213
            print('Parsing error for function %s' % f.name())
214
215
    # Create docstrings for classes
216
    block_names = [block.name() for block in blocks]
217
    klasses = [k for k in di.in_category(DoxyClass) if k.name() not in block_names]
218
    for k in klasses:
219
        try:
220
            output.append(make_class_entry(k))
221
        except k.ParsingError:
222
            print('Parsing error for class %s' % k.name())
223
224
    # Docstrings are not created for anything that is not a function or a class.
225
    # If this excludes anything important please add it here.
226
227
    output = "\n\n".join(output)
228
229
    swig_doc = file(swigdocfilename, 'w')
230
    swig_doc.write(output)
231
    swig_doc.close()
232
233
if __name__ == "__main__":
234
    # Parse command line options and set up doxyxml.
235
    err_msg = "Execute using: python swig_doc.py xml_path outputfilename"
236
    if len(sys.argv) != 3:
237
        raise StandardError(err_msg)
238
    xml_path = sys.argv[1]
239
    swigdocfilename = sys.argv[2]
240
    di = DoxyIndex(xml_path)
241
242
    # gnuradio.gr.msq_queue.insert_tail and delete_head create errors unless docstrings are defined!
243
    # This is presumably a bug in SWIG.
244
    #msg_q = di.get_member(u'gr_msg_queue', DoxyClass)
245
    #insert_tail = msg_q.get_member(u'insert_tail', DoxyFunction)
246
    #delete_head = msg_q.get_member(u'delete_head', DoxyFunction)
247
    output = []
248
    #output.append(make_func_entry(insert_tail, name='gr_py_msg_queue__insert_tail'))
249
    #output.append(make_func_entry(delete_head, name='gr_py_msg_queue__delete_head'))
250
    custom_output = "\n\n".join(output)
251
252
    # Generate the docstrings interface file.
253
    make_swig_interface_file(di, swigdocfilename, custom_output=custom_output)