diff options
Diffstat (limited to 'gr-howto-write-a-block/docs/doxygen/swig_doc.py')
-rw-r--r-- | gr-howto-write-a-block/docs/doxygen/swig_doc.py | 253 |
1 files changed, 253 insertions, 0 deletions
diff --git a/gr-howto-write-a-block/docs/doxygen/swig_doc.py b/gr-howto-write-a-block/docs/doxygen/swig_doc.py new file mode 100644 index 000000000..5034099e3 --- /dev/null +++ b/gr-howto-write-a-block/docs/doxygen/swig_doc.py @@ -0,0 +1,253 @@ +# +# Copyright 2010,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# +""" +Creates the swig_doc.i SWIG interface file. +Execute using: python swig_doc.py xml_path outputfilename + +The file instructs SWIG to transfer the doxygen comments into the +python docstrings. + +""" + +import sys + +try: + from doxyxml import DoxyIndex, DoxyClass, DoxyFriend, DoxyFunction, DoxyFile, base +except ImportError: + from gnuradio.doxyxml import DoxyIndex, DoxyClass, DoxyFriend, DoxyFunction, DoxyFile, base + + +def py_name(name): + bits = name.split('_') + return '_'.join(bits[1:]) + +def make_name(name): + bits = name.split('_') + return bits[0] + '_make_' + '_'.join(bits[1:]) + + +class Block(object): + """ + Checks if doxyxml produced objects correspond to a gnuradio block. + """ + + @classmethod + def includes(cls, item): + if not isinstance(item, DoxyClass): + return False + # Check for a parsing error. + if item.error(): + return False + return item.has_member(make_name(item.name()), DoxyFriend) + + +def utoascii(text): + """ + Convert unicode text into ascii and escape quotes. + """ + if text is None: + return '' + out = text.encode('ascii', 'replace') + out = out.replace('"', '\\"') + return out + + +def combine_descriptions(obj): + """ + Combines the brief and detailed descriptions of an object together. + """ + description = [] + bd = obj.brief_description.strip() + dd = obj.detailed_description.strip() + if bd: + description.append(bd) + if dd: + description.append(dd) + return utoascii('\n\n'.join(description)).strip() + + +entry_templ = '%feature("docstring") {name} "{docstring}"' +def make_entry(obj, name=None, templ="{description}", description=None): + """ + Create a docstring entry for a swig interface file. + + obj - a doxyxml object from which documentation will be extracted. + name - the name of the C object (defaults to obj.name()) + templ - an optional template for the docstring containing only one + variable named 'description'. + description - if this optional variable is set then it's value is + used as the description instead of extracting it from obj. + """ + if name is None: + name=obj.name() + if description is None: + description = combine_descriptions(obj) + docstring = templ.format(description=description) + if not docstring: + return '' + return entry_templ.format( + name=name, + docstring=docstring, + ) + + +def make_func_entry(func, name=None, description=None, params=None): + """ + Create a function docstring entry for a swig interface file. + + func - a doxyxml object from which documentation will be extracted. + name - the name of the C object (defaults to func.name()) + description - if this optional variable is set then it's value is + used as the description instead of extracting it from func. + params - a parameter list that overrides using func.params. + """ + if params is None: + params = func.params + params = [prm.declname for prm in params] + if params: + sig = "Params: (%s)" % ", ".join(params) + else: + sig = "Params: (NONE)" + templ = "{description}\n\n" + sig + return make_entry(func, name=name, templ=utoascii(templ), + description=description) + + +def make_class_entry(klass, description=None): + """ + Create a class docstring for a swig interface file. + """ + output = [] + output.append(make_entry(klass, description=description)) + for func in klass.in_category(DoxyFunction): + name = klass.name() + '::' + func.name() + output.append(make_func_entry(func, name=name)) + return "\n\n".join(output) + + +def make_block_entry(di, block): + """ + Create class and function docstrings of a gnuradio block for a + swig interface file. + """ + descriptions = [] + # Get the documentation associated with the class. + class_desc = combine_descriptions(block) + if class_desc: + descriptions.append(class_desc) + # Get the documentation associated with the make function + make_func = di.get_member(make_name(block.name()), DoxyFunction) + make_func_desc = combine_descriptions(make_func) + if make_func_desc: + descriptions.append(make_func_desc) + # Get the documentation associated with the file + try: + block_file = di.get_member(block.name() + ".h", DoxyFile) + file_desc = combine_descriptions(block_file) + if file_desc: + descriptions.append(file_desc) + except base.Base.NoSuchMember: + # Don't worry if we can't find a matching file. + pass + # And join them all together to make a super duper description. + super_description = "\n\n".join(descriptions) + # Associate the combined description with the class and + # the make function. + output = [] + output.append(make_class_entry(block, description=super_description)) + creator = block.get_member(block.name(), DoxyFunction) + output.append(make_func_entry(make_func, description=super_description, + params=creator.params)) + return "\n\n".join(output) + + +def make_swig_interface_file(di, swigdocfilename, custom_output=None): + + output = [""" +/* + * This file was automatically generated using swig_doc.py. + * + * Any changes to it will be lost next time it is regenerated. + */ +"""] + + if custom_output is not None: + output.append(custom_output) + + # Create docstrings for the blocks. + blocks = di.in_category(Block) + make_funcs = set([]) + for block in blocks: + try: + make_func = di.get_member(make_name(block.name()), DoxyFunction) + make_funcs.add(make_func.name()) + output.append(make_block_entry(di, block)) + except block.ParsingError: + print('Parsing error for block %s' % block.name()) + + # Create docstrings for functions + # Don't include the make functions since they have already been dealt with. + funcs = [f for f in di.in_category(DoxyFunction) if f.name() not in make_funcs] + for f in funcs: + try: + output.append(make_func_entry(f)) + except f.ParsingError: + print('Parsing error for function %s' % f.name()) + + # Create docstrings for classes + block_names = [block.name() for block in blocks] + klasses = [k for k in di.in_category(DoxyClass) if k.name() not in block_names] + for k in klasses: + try: + output.append(make_class_entry(k)) + except k.ParsingError: + print('Parsing error for class %s' % k.name()) + + # Docstrings are not created for anything that is not a function or a class. + # If this excludes anything important please add it here. + + output = "\n\n".join(output) + + swig_doc = file(swigdocfilename, 'w') + swig_doc.write(output) + swig_doc.close() + +if __name__ == "__main__": + # Parse command line options and set up doxyxml. + err_msg = "Execute using: python swig_doc.py xml_path outputfilename" + if len(sys.argv) != 3: + raise StandardError(err_msg) + xml_path = sys.argv[1] + swigdocfilename = sys.argv[2] + di = DoxyIndex(xml_path) + + # gnuradio.gr.msq_queue.insert_tail and delete_head create errors unless docstrings are defined! + # This is presumably a bug in SWIG. + #msg_q = di.get_member(u'gr_msg_queue', DoxyClass) + #insert_tail = msg_q.get_member(u'insert_tail', DoxyFunction) + #delete_head = msg_q.get_member(u'delete_head', DoxyFunction) + output = [] + #output.append(make_func_entry(insert_tail, name='gr_py_msg_queue__insert_tail')) + #output.append(make_func_entry(delete_head, name='gr_py_msg_queue__delete_head')) + custom_output = "\n\n".join(output) + + # Generate the docstrings interface file. + make_swig_interface_file(di, swigdocfilename, custom_output=custom_output) |