diff options
Diffstat (limited to 'docs/sphinx/gnuradio_sphinx.py')
-rw-r--r-- | docs/sphinx/gnuradio_sphinx.py | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/docs/sphinx/gnuradio_sphinx.py b/docs/sphinx/gnuradio_sphinx.py new file mode 100644 index 000000000..6f35a6fce --- /dev/null +++ b/docs/sphinx/gnuradio_sphinx.py @@ -0,0 +1,151 @@ +""" +Customizations of sphinx for gnuradio use. +""" + +from sphinx.ext.autodoc import py_ext_sig_re +from sphinx.ext.autodoc import ClassDocumenter, FunctionDocumenter, members_option +from sphinx.ext.autodoc import bool_option, members_set_option, identity +from sphinx.ext.autodoc import ALL + +# A dictionary of the number of lines to delete from the beginning of docstrings +lines_to_delete = {} + +def setup(sp): + # Fix line-breaks in signature. + sp.connect('autodoc-process-signature', fix_signature) + sp.connect('autodoc-process-docstring', remove_lines) + # Add node to autodocument signal-processing blocks. + sp.add_autodocumenter(BlockDocumenter) + sp.add_autodocumenter(PyBlockDocumenter) + +def remove_lines(app, what, name, obj, options, lines): + del_lines = lines_to_delete.get(name, 0) + # Don't delete any lines if this is called again. + lines_to_delete[name] = 0 + lines[:] = lines[del_lines:] + +def fix_signature(app, what, name, obj, options, signature, return_annotation): + """ + SWIG produces signature at the top of docstrings of the form + 'blah(int arg1, float arg2) -> return_type' + and if the string is long it breaks it over multiple lines. + + Sphinx gets confused if it is broken over multiple lines. + fix_signature and remove_lines get around this problem. + """ + if return_annotation is not None: + return + + if hasattr(obj, '__doc__'): + docs = obj.__doc__ + else: + docs = None + if not docs: + return None + doclines = docs.split('\n') + del_lines = remove_linebreaks_in_signature(doclines) + # match first line of docstring against signature RE + match = py_ext_sig_re.match(doclines[0]) + if not match: + return None + exmod, path, base, args, retann = match.groups() + # ok, now jump over remaining empty lines and set the remaining + # lines as the new doclines + i = 1 + while i < len(doclines) and not doclines[i].strip(): + i += 1 + lines_to_delete[name] = i - 1 + del_lines + # format args + signature = "({0})".format(args) + return signature, retann + +def remove_linebreaks_in_signature(lines): + alllines = '\n'.join(lines) + alllines = alllines.lstrip() + bits = alllines.split('->') + if len(bits) == 1: + return 0 + after = '->'.join(bits[1:]) + after_lines = after.split('\n') + ending = None + remainder = [] + for line in after_lines: + if line and ending is None: + ending = line + elif ending is not None: + remainder.append(line) + first_line = ' '.join([a.strip() for a in bits[0].split('\n') if a.strip()]) + ' -> ' + ending.strip() + match = py_ext_sig_re.match(first_line) + # If it is a signature, make the change to lines. + if match: + new_lines = [first_line] + remainder + lines[:] = new_lines + return len(bits[0].split('\n')) + else: + return 0 + +# These methods are not displayed in the documentation of blocks to +# avoid redundancy. +common_block_members =[ + 'check_topology', + 'detail', + 'history', + 'input_signature', + 'name', + 'nitems_read', + 'nitems_written', + 'nthreads', + 'output_multiple', + 'output_signature', + 'relative_rate', + 'set_detail', + 'set_nthreads', + 'start', + 'stop', + 'thisown', + 'to_basic_block', + 'unique_id', + ] + +class BlockDocumenter(FunctionDocumenter): + """ + Specialized Documenter subclass for gnuradio blocks. + + It merges together the documentation for the generator function (e.g. gr.head) + with the wrapped sptr (e.g. gr.gr_head_sptr) to keep the documentation + tidier. + """ + objtype = 'block' + directivetype = 'function' + # Don't want to use this for generic functions for give low priority. + priority = -10 + + def __init__(self, *args, **kwargs): + super(BlockDocumenter, self).__init__(*args, **kwargs) + # Get class name + bits = self.name.split('.') + if len(bits) != 3 or bits[0] != 'gnuradio': + raise ValueError("expected name to be of form gnuradio.x.y but it is {0}".format(self.name)) + sptr_name = 'gnuradio.{0}.{0}_{1}_sptr'.format(bits[1], bits[2]) + # Create a Class Documenter to create documentation for the classes members. + self.classdoccer = ClassDocumenter(self.directive, sptr_name, indent=self.content_indent) + self.classdoccer.real_modname = self.classdoccer.get_real_modname() + self.classdoccer.options.members = ALL + self.classdoccer.options.exclude_members = common_block_members + self.classdoccer.parse_name() + self.classdoccer.import_object() + + def document_members(self, *args, **kwargs): + return self.classdoccer.document_members(*args, **kwargs) + +class PyBlockDocumenter(ClassDocumenter): + """ + Specialized Documenter subclass for hierarchical python gnuradio blocks. + """ + objtype = 'pyblock' + directivetype = 'class' + + def __init__(self, *args, **kwargs): + super(PyBlockDocumenter, self).__init__(*args, **kwargs) + self.options.members = ALL + self.options.exclude_members = common_block_members |