Contributing to the documentation

Source code: docs

Here we 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.

See also

The authoritative reStructuredText User Documentation.

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` (see note below). 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.

Titles of sections should not use any styling such as the |Finesse| or backtick for code blocks “``”, because such styling does not always carry over correctly or as intended in cross-references.

Note

References to labels can be written in the form :ref:`intro` only if the intro label is defined directly before a section or figure directive. If a label is defined somewhere else then references can only be made to this label with an explicit title, e.g. :ref:`Some label <mylabel>`.

A common pitfall is to define a label before an .. include: directive at the top of a .rst file. In this case Sphinx cannot work out which label to use for the reference and so emits a warning during the build. The fix is to either move the label to be before a section title or add an explicit title to its references.

See the Sphinx referencing documentation for more information.

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).

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 reST 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 nr property of the class BeamParam:

@property
def nr(self):
    """The refractive index associated with the `BeamParam`.

    :getter: Returns the index of refraction.
    :setter: Sets the index of refraction.
    """
    return self.__nr

@nr.setter
def nr(self, value):
    self.__nr = value

The above docstrings result in the finesse.gaussian.BeamParam.nr documentation page.

Linking to internal and external 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.

The Finesse documentation tooling provides additional cross-reference support for various situations:

  • :kat:command:`command_name` to create a link to a kat-script command,

  • :kat:element:`element_name` to create a link to a kat-script element,

  • :kat:analysis:`analysis_name` to create a link to a kat-script analysis,

  • :issue:`123` to create a link to an issue on the tracker,

  • :source:`path/to/script.py` to create a link to a source code file relative to the Finesse package root (i.e. /src/finesse/), or :source:/path/to/script.py`` to create a link to a source code file relative to the project root.

An example of cross-referencing 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 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.

Style guide

Finesse is developed by many people and maintaining consistency across the documentation is essential. The following subsections describe some style aspects to be conformed to when writing documentation.

Where style guidance is undefined, conform first to the rules enforced by doc8 then the Python documentation style guide.

Line length

Try to stick to a maximum of 88 characters. This is consistent with the rules for Python code, set by Black. This limit isn’t just for aesthetics in your IDE: code blocks in documentation pages with longer lines will overflow the visible page and create irritating horizontal scroll bars.

You may wish to create a ruler in your IDE to give a visual cue for this limit.

Hint

In some editors you can highlight a block of text and press Alt + Q to automatically wrap the text at the configured line length. In Visual Studio Code you might wish to install the Rewrap extension to enable this shortcut, and set a ruler in .vscode/settings.json using e.g. "editor.rulers": [88],.

Sections

There are particular characters assigned to heading levels in reST; the structure is determined from the succession of headings as defined in each .rst file. Finesse documentation should nevertheless ideally follow this convention:

  • = with overline, for page titles

  • =, for sections

  • -, for subsections

  • ^, for subsubsections

  • ", for paragraphs

Style

Try not to write documentation with many nested subsections; documentation should be mostly prose. Only the first four section levels are displayed in the sidebar of the HTML documentation, and each level beyond the top level is hidden until its parent is navigated to. If a section makes sense to appear in the sidebar then it should be within the top four levels; use common sense.

Titles

Section titles should use sentence case, e.g. Showing output from inline scripts, not Showing Output from Inline Scripts.

Do not use backtick-style (``thing``) or replacement (e.g. |Finesse|) formatting in titles as these do not render nicely in the sidebar. Instead you could consider using single quotes, e.g. “Logging via the Python ‘logging’ module”.

Code examples

Code examples should follow the same style <code_style> as Finesse itself.

Grammar

Tone

Use an affirmative, succinct tone. Concise is better than verbose. Users typically don’t read whole pages of documentation but rather arrive at particular sections via links or search results.

English

Use of US English is preferred, for consistency with the Python documentation; however, refactoring existing text just for the sake of changing English forms is discouraged - focus your efforts on something more fruitful. As with the Python documentation, consistency within a page or section is more important than global consistency.

References to Finesse

Finesse 3 should be referred to in the documentation simply as Finesse. Explicit inclusion of version numbers should only be made when referring to or comparing differences in behaviour.

Core documentation should focus on explaining how Finesse 3 works, not how it’s different to other tools or previous versions. Discussions of differences between Finesse 2 and 3 should be kept in relevant sections. It’s fine however to include brief “see also” directives in Finesse 3 documentation pointing to such discussions.

Capitalisation

Various Finesse-related terms have particular capitalisation conventions. Some are also defined as macros which format the text appropriately:

  • |Finesse|, not e.g. FINESSE

  • Pykat, not e.g. PyKat

  • KatScript, not e.g. kat script, katscript, or kat-script

Code examples

Write code examples using the same formatting rules as Finesse source code.

Prefer the use of .. code-block:: (provided by Sphinx) rather than .. code:: (provided by docutils).

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 developer mode. If you wish to install just the dependencies required for building the documentation, you can use e.g. pip install -e .[docs].

In order to build the PDF docs (using make latexpdf), you must ensure that a comprehensive LaTeX package (such as texlive) and a program that provides the rsvg-convert tool are available on the system path.

Note

There are some alternatives to rsvg-convert if this tool is not available, through they involve making some manual edits to the documentation’s settings in conf.py - see the documentation for sphinxcontrib-svg2pdfconverter for more information.

Linting tools

The doc8 package provides a command line tool to check documentation. Running doc8 from the project root directory will have it automatically pick up the line length setting defined in setup.cfg. For instance, you can lint the whole documentation source with:

$ cd /path/to/finesse/git/project/  # The directory that contains setup.cfg.
$ doc8 docs/source

This will print a list of files and their corresponding issues, if any.