Documentation Guide
This documentation is written using Sphinx.
Installing Sphinx
You will need the sphinx documentation tool and its numpydoc extension, napoleon extension, and mermaid extension, along with the readthedocs theme.
Assuming you have the pip python package installer, you can easily install the required tools from PyPI:
>>> pip install Sphinx, numpydoc, sphinxcontrib-napoleon, sphinxcontrib-mermaid, sphinx_rtd_theme
Building the docs
Navigate to the documentation
directory (see Directory Layout). If Sphinx is propery installed, you should be able to just run make html
:
>>> make html
[huge printout of compiling status informational messages]
Build finished. The HTML pages are in _build/html
The documentation will be built to HTML and you can view it locally by opening _build/html/index.html
You can also similarly build the documentation to PDF, LaTeX, Windows Help, or several other formats as desired. Use make help
to see the available output formats.
Rebuilding from Scratch
If there are structural changes made to the EXOSIMS directory (i.e., file/folder additions or deletions) you will need to run sphinx-apidoc
. From the documentation
directory run:
>>> sphinx-apidoc -f -o . ../EXOSIMS/
This will rebuild all of the EXOSIMS.*
rst files, after which you can run make html
to rebuild the html documentation. Note that this command also generates a modules.rst
file that we do not use, and which can be safely deleted (but should not be added to the repository).
The prototype input table (EXOSIMS Prototype Inputs) is auto-generated by utility buildargdoc.py
in the documentation
directory. It can be called as:
>>> python buildargdoc.py
This entire rebuild procedure is also packaged into a bash script for POSIX systems (builddocs.sh
) and a batch file for Windows systems ( builddocs.bat
). Both are intended to be run from the documentation
directory.
Docstrings
Valid and well-formatted docstrings are required for all EXOSIMS code. The file docstring_example.py
in the documentation
directory (see Directory Layout) provides some examples of the prefered code documentation style. Here we list the most important requirements:
Classes
All Class docstrings (occurring immediately after the class declaration) must include a description of the purpose of the class followed by a full list of arguments to the class __init__
, in the exact order of method declaration, and then a full list of class attributes, preferably in alphabetical order. Each entry of both lists must include a well-defined type (see Intersphinx) and a detailed description. For arguments, keyword inputs must list their defaults, and optional inputs must be tagged as such in the type definition. An example of the preferred format is:
class ExampleClass():
"""An explanation of what this is for
Args:
input1 (type):
Description of argument input1
input2 (type):
Description of argument input2
input3 (type):
Description of keyword input3. Defaults to defaultval
input4 (type, optional):
Description of optional keyword input4. Defaults to None
Attributes:
attribute1 (type):
Description of attribute 1
attribute2 (type):
Description of attribute 2
"""
def __init__(
self,
input1,
input2,
input3=defaultval
input4=None
):
self.attribute1 = input1 + input2
if input4 is not None:
self.attribute2 = input3
else:
self.attribute2 = None
Note that the __init__
(unlike all other methods) does not require its own docstring.
Methods
Every method (other than a class __init__
) must have a docstring identifying the purpose of the method,
its inputs and outputs. Input arguments should be formatted exactly as in the class example, above. The return is described first
by its type (not a name). The type is followed by a detailed description of the returns. If a method is returning multiple objects, then the return type is a tuple. Method docstring for class methods should not list self
as an argument, and should also note any attributes that are updated by the method. An example of a docstring for a method returning a single value is:
def example_method1(input1):
"""Does some computation on input1
Args:
input1 (type):
Description of input1
Returns:
type:
A description of the return
"""
out = some_operation_on(input1)
return out
For multiple outputs:
def example_method2(input1):
"""Does some computation on input1
Args:
input1 (type):
Description of input1
Returns:
tuple:
output1 (type):
A description of the first output
output2 (type):
A description of the second output
"""
out1 = some_operation_on(input1)
out2 = some_other_operation_on(input2)
return out1, out2
Intersphinx
EXOSIMS docs make use of intersphinx to connect to documentation for both python and other projects (in particular numpy, scipy, and astropy). In order for this to work, it is necessary to make sure that types used in docstrings are well defined. Python native types should be written as:
str
float
int
dict
list
bool
For third party modules, types should be written as full class strings. For example, numpy arrays should be typed as numpy.ndarray
. Adding a leading tilde (~numpy.ndarray
) will suppress all of the leading packages/modules (so in this case, only ndarray
) will be written in the compiled documentation. Try to be as specific as possible about the expected contents of a variable. For example, a list of dictionaries should be typed as list(dict)
and a numpy array of booleans should be typed as ~numpy.ndarray(bool)
. Astropy quantities should be typed as ~astropy.units.Quantity
. An astropy quantity array has type ~astropy.units.Quantity(~numpy.ndarray(float))
. The associated description of the variable should explicitly state the physical unit type unless it is obvious from the description itself (i.e., a mass is expected to have mass units, etc.). For EXOSIMS conventions, see the prototype docstrings.
EXOSIMS modules should be typed by referencing the module-specific page in this documentation. For example, if a method has a TimeKeeping object as an input, the docstring should look like:
def example_method2(TK):
"""Does some computation requiring TimeKeeping
Args:
TK (:ref:`TimeKeeping`):
TimeKeeping object
"""
Similarly, **specs
inputs should reference the Input Specification (by writing :ref:`sec:inputespec`
in the docstring).
Comments
Commenting your code is great! You should do as much of that as possible. However, unless your code and comment fit within 88 characters, your comment should always precede the code. Comments should follow the same line length limits (88 characters) as the code. Comments should use a single # symbol followed by a single space. For example: