Contributing to the Documentation

Here we will describe the procedure through which to contribute to these Finesse documentation pages and guidelines for conventions and styles to use. The documentation is built with Sphinx and uses reStructured Text (reST) for formatting and markdown support.

Structure of the Project

The directory structure of the project is shown in the figure below. Each directory within finesse/docs/source/ contains the documentation files which these pages are built from. The sub-directories of the documentation each contain an index.rst file which links the relevant pages to that section of the documentation.

../_images/dir_structure.png

In order to expand a section of the documentation with another page, you must link your new reST file in the index.rst file of that section. Below is an example showing the index file for the An Introduction to Finesse section of the documentation:

.. _intro:

An Introduction to Finesse
==========================

This section contains information regarding the key concepts of Finesse and how to use them to
produce accurate frequency-domain models of any interferometer configuration you are
considering.

.. toctree::
    :maxdepth: 1

    installation
    key_concepts
    simple_example
    changes

The starting line (.. _intro:) defines a cross-reference label for the file so that other reST files within the documentation can link to the introduction index page using :ref:`<intro>`. Within the snippet above a toctree is defined which links the listed reST files to the index page for the introduction. As described earlier, if you wanted to link a new reST file for a different topic within the introduction then you would simply add the name of this file to the toctree directive list.

Comments and Todo items

The .. todo:: directive will add a highlighted todo item in the manual, these should be used only as placeholders for larger sections, such as .. todo:: This section is missing for a missing section. For internal todo items such as ‘fix this later’ a simple comment can be used, for example: .. Todo: fix this later.

Writing Sphinx Compatible Docstrings

To produce well formatted API documentation for Finesse, all module, class and function docstrings will be written in the numpydoc style (follow the link for the style guide). If you installed Python through Anaconda then you should already have the numpydoc extension installed, otherwise run pip install numpydoc to retrieve it.

Note

In contrast to PEP-257, numpydoc asks for class __init__ methods to be documented in the class docstring, not under the __init__ method itself.

Note

There is currently no directly supported method for documenting properties in numpydoc, instead you should use the Sphinx formatting syntax in the docstrings for a class property. This involves using docstrings only for the “getter” and passing the tags :getter: and :setter: to document both. An example is shown below for the tuning (\(\phi\)) property of the class Surface:

@property
def phi(self):
    """The microscopic tuning of the surface.

    :getter: Returns the surface tuning.
    :setter: Sets the surface tuning.
    """
    return self._phi

@phi.setter
def phi(self, value):
    self._phi = value

The above docstrings result in the Surface.phi documentation page.

Linking to internal and external module objects

Sphinx enables you to cross-reference modules, classes, functions and attributes both internal to the project and external to it. To cross-reference an internal object (or attribute, method etc.) simply use the following directives:

  • :mod:`.internal_module_name` to create a link to an internal module,

  • :class:`.internal_class_name` to create a link to an internal class,

  • :func:`.internal_function_name` to create a link to an internal function or :meth:`.internal_class_name.internal_function_name` to create a link to an internal class method,

  • :attr:`.internal_class_name.internal_attribute_name` to create a link to an internal class attribute.

An example of this is shown below for the Model.path() method:

def path(self, from_node, to_node):
    """Retrieves an ordered list of the path trace betwwen the specified nodes, including any
    spaces.

    The list is formatted as `[(node, to_comp)]` where `to_comp` can be any sub-class instance
    of :class:`.Connector` (including :class:`.Space`) and `node` is an input to `to_comp`.

    Parameters
    ----------
    from_node : :class:`.Node`
        Node to trace from.

    to_node : :class:`.Node`
        Node to trace to.

    Returns
    -------
    out : list
        A list of the nodes and components (including spaces) between `from_node`
        and `to_node` in order.

    Raises
    ------
    e : :class:`.NodeException`
        If either of `from_node`, `to_node` are not contained within the model.
    """
    from .components import Nothing

    nc_between = self.path(from_node, to_node)
    nodes_comps_spaces = []

    trace_end = Nothing('end')
    for node, comp in nc_between:
        if node.is_input:
            nodes_comps_spaces.append((node, comp))
        else:
            space = node.space
            if space is None:
                nodes_comps_spaces.append((node, trace_end))
            else:
                nodes_comps_spaces.append((node, space))
    return nodes_comps_spaces

Linking to objects in external modules is just as simple but requires a change to the conf.py file in the finesse/docs/source directory if you want to have links to a module not already defined in the intersphinx_mapping variable in this file. For details on how to specify new targets in this variable see the intersphinx documentation.

Once the necessary target is included in intersphinx_mapping, then you can create links to external modules’ documentation as before using the directive syntax shown in the bulleted list above. An example follows for the Model.network attribute which creates a link to the networkx.DiGraph class:

@property
def network(self):
    """The directed graph object containing the optical
    configuration as a :class:`networkx.DiGraph` instance.

    The `network` stores :class:`.Node` instances as nodes and
    :class:`.Space` instances as edges, where the former has
    access to its associated component via :attr:`.Node.component`.

    :getter: Returns the directed graph object containing the configuration
             (read-only).
    """
    return self.__network

Documenting cython extensions

See Documenting Cython extensions.

Showing output from inline scripts

The jupyter-sphinx extension is available which provides the ability to execute code embedded in the documentation in a Jupyter kernel, then show the code (with syntax highlighting) alongside the outputs of that code in the documentation. It also supports plot outputs (and others, such as LaTeX and JavaScript widgets).

Code is executed in the environment used to build the documentation, so the finesse package is available to import and run.

Simple code blocks can be executed with .. jupyter-execute:: directives. More advanced usage is possible, such as running scripts within different kernels (using the :id: directive option), showing line numbers, importing and showing the contents of scripts on the file system, etc. See the jupyter-sphinx documentation for more information.

Building the Documentation

The documentation can be built using the Makefile provided within the docs directory. Run make with no arguments to print a list of available output formats. The most common output format is HTML, which can be produced with the command make html. Note that in order to compile the documentation certain extra dependencies are required. These are automatically installed when the project is installed in dev mode, e.g. using pip install -e .[dev].