What to contribute?
===================
The package contains multiple forms of files:
- Source code
- Documentation
- Notebooks
Coding conventions
------------------
This package follows the
`PEP8 `__ recommendations
strictly. For example, we fix the line length of each code line to
**79**.
We are gradually adopting type hints (see section on `type
hinting <#type-hinting>`__).
We also document the code as we write it in doc-string (see section on
`code documentation <#code-documentation>`__).
We also use a list of linters to automatically check we respect those
conventions (see section on `linters <#linters>`__).
Type hinting
~~~~~~~~~~~~
We are gradually adopting `type
hinting `__ to add some
semblance of a static typed package. **We strongly encourage
contributors to use type hints in newly added code.**
It enables to add information about variables, parameters and function
return types directly inline.
This enables at least to document what those types are supposed to be,
as this type hinting has no influence at runtime.
Furthermore, some utilities such as
`mypy `__ can perform static
verification of your code by parsing and checking the coherence of these
type hints.
.. note::
* the type hints are used to tag the functions to verify using mypy,
so you can use type hinting on parts of your code you want to check more
thoroughly
* these type hints are parsed by sphinx to complete the autogenerated
API documentation
Linters
~~~~~~~
We use the following list of tools to automatically check that
best coding practices are followed:
- :ref:`flake8 `: check
`PEP8 `__ rules compliance
(install with ``pip install flake8``)
- :ref:`isort `: check and place the imports order (install with
``pip install isort``)
- :ref:`black `: code formatter (install with
``pip install black``)
A note on floating point precision
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The policy of this package concerning floating point accuracy is the
following:
- Numeric computations in the source code return raw results (no rounding except if it is part of an algorithm)
- When checking floats equalities/inequalities, the function ``math.isclose`` from module ``math`` should be used
- with its default parameters if possible
- otherwise the function documentation should mention those parameters
- parameters can alternatively be passed by the function so user has control
- Numeric results in unit tests should be checked digits precision (using for example
``unittest.TestCase.assertAlmostEqual`` function)
- with its default precision if possible (7 digits)
- otherwise the tested precision should be mentionned in the source code documentation
Backward compatibility
~~~~~~~~~~~~~~~~~~~~~~
As a general rule, you should always make non-breaking changes to a code base.
In the case of this package, we are only considering the python scripts (their main scope)
as the code we want stable.
The internal API, classes, functions and methods behind our scripts are not *yet* stable.
Therefore, we **must never** push breaking changes between major releases.
Furthermore, the case must be real solid to push breaking changes even on a major release.
If such breaking changes are identified in advance, they must be delayed and concentrated in the next major release.
Code documentation
------------------
For maintainability, it is important that **the documentation of the
code is being built at the same time the code is developed**. There are
different documentation conventions, we will describe the one we chose.
This choice of conventions is conditioned by the documentation tool we
recommend: `sphinx `__. This
software is able to generate a complete documentation containing
manually written .rst files alongside an autogenerated API obtained by
parsing source files doc-strings.
Doc strings
~~~~~~~~~~~
All python functions and classes should be documented inline, using
doc-string in
`reST `__
formatting compliant with sphinx. Modules and subpackages can also be
documented by placing a doc-string before the first import. The aim of
subpackage / module doc-string is to express its intent, and also add
any relevant information that cannot be contained in the functions and
classes (for example: scripts should be described in those doc-strings).
This doc-strings should at the minimum contain the description of the
class constructor / function parameters, return values and types, and
intent. It can also be completed with various other information at the
developers' discretion (for example: mathematic formulas, todos, example
code, etc).
When referencing other classes, types and functions from a doc-string,
you should as much as possible use a reST reference to the actual one.
The aim being to facilitate the documentation readability above all
else.
There are at least 4 different ways to use reST references inside the
documentation (in either doc-string or reST files):
- create link to another document (will be converted to a link to
another web page): :literal:`:doc:`relative/path/to/document\``
(note: no file extension!)
- create link to another section (from any document):
:literal:`:ref:`relative/path/to/document:Section name\`` (note
``Section name`` is the actual section name as written)
- create arbitrary link to a section or figure: create a **global**
reference just before referenced section/figure definition
``.. _my_ref:`` then it can de referenced using
:literal:`:ref:`my_ref\``
- create an external link: URL are automatically converted into links,
otherwise one can be created using
:literal:`:ref:`link_name \``
You should also remember to cite scientific references on which you base
your contributions (see next section for more details).
References
~~~~~~~~~~
As our package is meant primarily for research purposes, it is important
that we include in the documentation bibliography references so users
can clearly see what the code is based on.
We have decided to use the ``doc/refs.bib`` BibTex file
to centralize all references across the whole package. This way, all
references are unique, and any contributor should check if a reference
is not already present before adding it.
Then, we recommend the references being cited in the documentation of
the functions or modules using the extension ``sphinxcontrib.bibtex``
format:
::
:footcite:p:`REF_NAME`
(for a reference named ``REF_NAME``)
Then, all references on a single page can be displayed as a foot note
using:
::
.. footbibliography::
Type hinting in doc
~~~~~~~~~~~~~~~~~~~
The type hints can be parsed by sphinx to complete the autogenerated API
documentation. They are used to complete the type information of
parameters and return types of functions.
Comments
~~~~~~~~
Comments should be used to inform about the implementation details such
as scope intent, explanation of a line of code, etc.
They can be placed on their own line when describing the intent of the
following code lines, or at the end of a code line to explain this
particular line.
Example
~~~~~~~
You can see below an example of a code properly documented:
.. code:: python
# src/my_array.py
"""This module is used to perform array computations.
Usage
=====
Sum two arrays using this module as a script:
.. code:: bash
python my_array.py ARRAY1 ARRAY2 OUTPUT
ARRAY1 and ARRAY2 are two csv files containing arrays.
OUTPUT is the output csv file.
"""
import argparse
import numpy as np
class MyArray:
"""This class provides a wrapper for :class:`numpy.array`.
:param array: array to wrap
"""
def __init__(self, array: np.array):
self.array = array
@classmethod
def load_csv(cls, filename: str) -> 'MyArray':
"""Load array from csv file.
:param filename: csv file
"""
return MyArray(np.loadtxt(filename, delimiter=","))
def save_csv(self, filename: str):
"""Save array in csv file
:param filename: csv file
"""
np.savetxt(filename, self.array, fmt="%g", delimiter=",")
def sum_arrays(array1: MyArray, array2: MyArray) -> MyArray:
"""Returns the sum of two arrays.
:param array1:
:param array2:
"""
return MyArray(array1.array + array2.array)
if __name__ == "__main__":
# Configuration of parameters
parser = argparse.ArgumentParser(
description="Sum two csv arrays"
)
parser.add_argument("array1", help="first array in csv file")
parser.add_argument("array2", help="second array in csv file")
parser.add_argument("output", help="output file")
# Parse arguments
args = parser.parse_args()
# Load input files
array1 = MyArray.load_csv(args.array1)
array2 = MyArray.load_csv(args.array2)
# Sum inputs and save output
res = sum_arrays(array1, array2)
res.save_csv(args.output)
Tests
-----
In this package, no systematic tests are implemented thus far.
Notebooks
---------
We added `jupyter notebooks `__ that can be run as
examples in ``notebooks/`` folder.
Then to be able to run the notebooks, go to the ``notebooks/`` directory
and run the following:
.. code:: bash
jupyter notebook
This will open a web-page in your browser, listing all the provided
notebooks of this package.
For a better grasp at the real user experience when installing the
package, and running the examples notebook, **we strongly recommend setting up another**
:ref:`virtual environment `
**for the** ``notebooks/`` **subdirectory.**
In this new :ref:`virtual environment `, you will do the
following (from ``notebooks/``):
- install this package in editable mode: ``pip install -e ..``
- install jupyter package: ``pip install jupyter``