From a20c127a9469aeac9ec60bf432c4128c49349f34 Mon Sep 17 00:00:00 2001 From: Ben Reynwar Date: Wed, 3 Nov 2010 20:53:07 -0700 Subject: Generation of python docstrings from doxygen xml. swig_doc.i is a swig interface file containing all the docstrings. It is generated using swig_doc.py and included by gnuradio.i. Minor changes to the swig make files have been made so that the generation of swig_doc.i occurs. --- gnuradio-core/src/lib/swig/swig_doc.py | 195 +++++++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 gnuradio-core/src/lib/swig/swig_doc.py (limited to 'gnuradio-core/src/lib/swig/swig_doc.py') diff --git a/gnuradio-core/src/lib/swig/swig_doc.py b/gnuradio-core/src/lib/swig/swig_doc.py new file mode 100644 index 000000000..52989e1bd --- /dev/null +++ b/gnuradio-core/src/lib/swig/swig_doc.py @@ -0,0 +1,195 @@ +""" +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() -- cgit