If you'd like to contribute to Chi (thanks!), please have a look at the guidelines below.
If you're already familiar with our workflow, maybe have a quick look at the pre-commit checks directly below.
Before you commit any code, please perform the following checks:
- No style issues:
$ flake8
- All tests pass:
$ python run-tests.py --unit
- The documentation builds:
$ python run-tests.py --doctest
We use GIT and GitHub to coordinate our work. When making any kind of update, we try to follow the procedure below.
- Create an issue where new proposals can be discusssed before any coding is done.
- Create a branch of this repo (ideally on your own fork), where all changes will be made
- Download the source code onto your local system, by cloning the repository (or your fork of the repository).
- Install Chi with the developer options.
- Test if your installation worked, using the test script:
$ python run-tests.py --unit
.
You now have everything you need to start making changes!
- Chi is developed in Python, and makes heavy use of NumPy (see also NumPy for MatLab users and Python for R users).
- Make sure to follow our coding style guidelines.
- Commit your changes to your branch with useful, descriptive commit messages: Remember these are publically visible and should still make sense a few months ahead in time. While developing, you can keep using the github issue you're working on as a place for discussion. Refer to your commits when discussing specific lines of code.
- If you want to add a dependency on another library, or re-use code you found somewhere else, have a look at these guidelines.
- Test your code!
- Chi has online documentation at http://chi.readthedocs.io/. To make sure any new methods or classes you added show up there, please read the documentation section.
- When you feel your code is finished, or at least warrants serious discussion, run the pre-commit checks and then create a pull request (PR) on Chi's GitHub page.
- Once a PR has been created, it will be reviewed by any member of the community. Changes might be suggested which you can make by simply adding new commits to the branch. When everything's finished, someone with the right GitHub permissions will merge your changes into Chi's main repository.
To install Chi with all developer options, use:
$ git clone [email protected]:DavAug/chi.git
$ cd chi
$ pip install -e .[docs]
This will
- Install all the dependencies for Chi, including the ones for documentation (docs).
- Tell Python to use your local Chi files when you use
import chi
anywhere on your system.
At this point you will already have to have Sundials installed, as explained in the non-dev install instructions.
You may also want to create a virtual environment first, using virtualenv or conda.
Chi follows the PEP8 recommendations for coding style. These are very common guidelines, and community tools have been developed to check how well projects implement them.
We use flake8 to check our PEP8 adherence. To try this on your system, navigate to the Chi directory in a console and type
$ flake8
When you commit your changes they will be checked against flake8 automatically (see infrastructure).
Naming is hard. In general, we aim for descriptive class, method, and argument names. Avoid abbreviations when possible without making names overly long, so mean
is better than mu
, but a class name like AdaptiveMCMC
is fine.
Class names are CamelCase, and start with an upper case letter, for example SuperDuperMCMC
. Method and variable names are lower case, and use underscores for word separation, for example x
or iteration_count
.
To be consistent with the work so far, all Chi material in the repository (code, comments, docstrings, documentation, etc.) should be written in UK english, the only exception being when quoting other sources, e.g. titles of scientific articles in references.
While it's a bad idea for developers to "reinvent the wheel", it's important for users to get a reasonably sized download and an easy install. In addition, external libraries can sometimes cease to be supported, and when they contain bugs it might take a while before fixes become available as automatic downloads to Chi users. For these reasons, all dependencies in Chi should be thought about carefully, and discussed on GitHub.
Direct inclusion of code from other packages is possible, as long as their license permits it and is compatible with ours, but again should be considered carefully and discussed in the group. Snippets from blogs and stackoverflow can often be included without attribution, but if they solve a particularly nasty problem (or are very hard to read) it's often a good idea to attribute (and document) them, by making a comment with a link in the source code.
All code requires testing. We use the unittest package for our tests. (These tests typically just check that the code runs without error, and so, are more debugging than testing in a strict sense. Nevertheless, they are very useful to have!)
To run quick tests, use
$ python run-tests.py --unit
Every new feature should have its own test. To create ones, have a look at the test
directory and see if there's a test for a similar method. Copy-pasting this is a good way to start.
Next, add some simple (and speedy!) tests of your main features. If these run without exceptions that's a good start! Next, check the output of your methods using any of these assert methods.
Guidelines for writing unit tests:
- Unit tests should test a very small block of code (e.g. a single method) 1b. When writing tests, start from the simplest case, and then work up
- Unit tests test the public API (i.e. they never access private methods or variables). They test if the code does what it promises to do, not how it does it. For example, after running
my_object.set_x(4)
, you might check ifmy_object.x()
returns 4, but you never check ifmy_object._x == 4
: how the object stores its data is its own business. - There are hundreds of unit tests, and good developers run all of them several times a day. Therefore, unit tests should be fast.
- If you're testing something stochastic, seed the number generator as part of the test, i.e. with
np.random.seed(1)
.
Chi is documented in several ways.
First and foremost, every method and every class should have a docstring that describes in plain terms what it does, and what the expected input and output is.
These docstrings can be fairly simple, but can also make use of reStructuredText, a markup language designed specifically for writing technical documentation. For example, you can link to other classes and methods by writing :class:`chi.MechanisticModel`
and :meth:`simulate()`
.
In addition, we write a (very) small bit of documentation in separate reStructuredText files in the docs
directory. Most of what these files do is simply import docstrings from the source code. But they also do things like add tables and indexes. If you've added a new class to a module, search the docs
directory for that module's .rst
file and add your class (in alphabetical order) to its index. If you've added a whole new module, copy-paste another module's file and add a link to your new file in the appropriate index.rst
file.
Using Sphinx the documentation in docs
can be converted to HTML, PDF, and other formats. In particular, we use it to generate the documentation on http://pints.readthedocs.io/
-
Each docstring should start with a single sentence explaining what it does.
-
If desired, this can be followed by a blank line and one or several paragraphs providing a more detailed explanation. These paragraphs can include code snippets or use LaTeX expressions for mathematics (see below).
-
If the class is a subclass of some other Chi type, it may be good to mention this here. For example:
Extends :class:`MechanisticModel`.
-
Simple arguments can be described textually. For example, a docstring could be a single line "Sets the width parameter to
w
.". For complicated functions or methods it may be good to include a parameters section:Parameters ---------- x : int A variable `x` that should be an integer y A variable without a type hint
This syntax can also be used for constructor arguments. Note that default values for any arguments are already displayed automatically in the function/method/constructor signature.
-
Simple return types can be described textually, but complicated return types (which are not encouraged) can use the syntax:
Returns ------- samples A list of samples. likelihoods A list of their corresponding log-likelihoods
-
References to literature are highly encouraged, and go near the bottom of the docstring:
Adaptive covariance MCMC based on Haario et al. [1]_, [2]_. (rest of the docstring goes here) References ---------- .. [1] Johnstone, Chang, Bardenet, de Boer, Gavaghan, Pathmanathan, Clayton, Mirams (2015) "Uncertainty and variability in models of the cardiac action potential: Can we build trustworthy models?". Journal of Molecular and Cellular Cardiology. https://10.1016/j.yjmcc.2015.11.018 .. [2] Haario, Saksman, Tamminen (2001) "An adaptive Metropolis algorithm". Bernoulli. https://doi.org/10.2307/3318737
There is no standard format (e.g. APA style), but authors, titles, years, and journals are recommended, as well as a link based on a DOI.
-
Longer code snippets can go at the very end of a docstring
Examples -------- :: errors = [ pints.MeanSquaredError(problem1), pints.MeanSquaredError(problem2), ] # Equally weighted e1 = pints.SumOfErrors(errors) # Differrent weights: weights = [ 1.0, 2.6, ] e2 = pints.SumOfErrors(errors, weights)
When referencing a variable in a docstring, please use the syntax ``x``
.
Longer code snippets can be started using this form:
"""
An example follows here::
print('Hello world')
"""
LaTeX expressions can be embedded in docstrings by using the syntax ```:math:`expression```` for inline mathematics, or a longer form for multi-line strings:
r"""
Defines a :math:`\gamma` (log) prior with given shape parameter ``a``
and rate parameter ``b``, with pdf
.. math::
f(x|a,b)=\frac{b^a x^{a-1} e^{-bx}}{\mathrm{\Gamma}(a)}
"""
Note that when using maths, it is best to define the docstring in a raw string, i.e. by writing r""" your stuff here """
. This will allow you to write e.g. 1 + \tau
instead of 1 + \\tau
and will stop flake8 from complaining about invalid escape sequences.
To test and debug the documentation, it's best to build it locally. To do this, make sure you have the relevant dependencies installed (see installation), navigate to your Chi directory in a console, and then type:
cd docs
make clean
make html
Next, open a browser, and navigate to your local PINTS directory (by typing the path, or part of the path into your location bar). Then have a look at <your chi path>/docs/build/html/index.html
.
Occasionally, we'll make a new release for Chi, and update the version on PyPI (which is where pip
will download it from, for non-dev users).
To do this:
- Decide a new release is necessary, discuss it in the group.
- Make sure the version number has changed since the last release.
- Use the GitHub releases page to create a new release. Each release will create a tag in the git repository, which should have the format
v1.2.3
.- The first number is for big events, the second for regular releases (e.g. new features), the final for bugfixes and smaller improvements. This is subjective.
- Beyond that, there is no significance to these numbers (e.g. it doesn't matter if they're odd or even,
v0.9.9
is followed byv0.9.10
).
- Check what has changed since the last release, and write some release notes to summarise what's new.
- Creating the new release in github will automatically update PyPI, so do this with care.
- Keep in mind that PyPI version numbers are eternal: You cannot modify a release, only create a new one with a new version number.
- Once the new release is done, create a PR to update the version number (final digit) to indicate that the code in the repo is no longer the version on PIP.
Installation of Chi and dependencies is handled via setuptools
Configuration files:
setup.py
MANIFEST.in
The MANIFEST.in
file is used to list non-Python files to be included in the installation.
It's always worth using an up-to-date version of pip. On older systems especially, having an up-to-date pip will prevent all kinds of version incompatibility issues:
$ pip install --upgrade pip
All committed code is tested using GitHub Actions, tests are published on https://github.com/pints-team/pints/actions.
Configuration files:
.github/workflows/*.yml
Unit tests and flake8 testing is done for every commit. A nightly cronjob also tests the notebooks.
Code coverage (how much of our code is actually seen by the (linux) unit tests) is tested using Codecov, a report is visible on https://codecov.io/gh/DavAug/chi.
It is possible to measure code coverage locally using coverage.py. To run a particular test file (below test_mechanistic_models.py
) and record coverage, run:
coverage run -m unittest test_mechanistic_models.py
To see the coverage for a particular file (below _mechanistic_models.py
) triggered by this test, run:
coverage report -m _mechanistic_models.py
Configuration files:
.coveragerc
Documentation is built using https://readthedocs.org/ and published on http://pints.readthedocs.io/.
Configuration files:
.readthedocs.yaml
.requirements-docs.txt
Style checking is performed using flake8.
Configuration files:
.flake8
GitHub does some magic with particular filesnames. In particular:
- The first page people see when they go to our GitHub page displays the contents of README.md, which is written in the Markdown format. Some guidelines can be found here.
- The license for using Chi is stored in LICENSE.md, and automatically linked to by GitHub.
- This file, CONTRIBUTING.md is recognised as the contribution guidelines and a link is automatically displayed when new issues or pull requests are created.
We want to thank the PINTS developer team whose CONTRIBUTING.md
helped us as guidance for this document.