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