""" 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 from gnuradio.utils.doxyxml import DoxyIndex, DoxyClass, DoxyFriend, DoxyFunction 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 dd = obj.detailed_description if bd: description.append(bd) if dd: description.append(dd) return utoascii('\n\n'.join(description)) 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): """ Create a class docstring for a swig interface file. """ output = [] output.append(make_entry(klass)) 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(block): """ Create class and function docstrings of a gnuradio block for a swig interface file. """ output = [] # Get creator function make_func = di.get_member(make_name(block.name())) output.append(make_class_entry(block)) creator = block.get_member(block.name()) make_func_desc = "Creates a {name} block.\n\n{block_desc}" description = make_func_desc.format( block_desc = combine_descriptions(block), name = py_name(block.name()), ) output.append(make_func_entry(make_func, description=description, params=creator.params)) return "\n\n".join(output) # 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] swig_doc = file(swigdocfilename, 'w') di = DoxyIndex(xml_path) output = [] # 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') insert_tail = msg_q.get_member(u'insert_tail') delete_head = msg_q.get_member(u'delete_head') 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')) # 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())) make_funcs.add(make_func.name()) output.append(make_block_entry(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.write(output) swig_doc.close()