The package requires Python 3.9 or newer. The pyrealm package can be installed
from PyPi but to develop the package you will need
to clone this repository and then install
Poetry for dependency management and to
set up the development environment.
From within the cloned repository, first install the dependencies defined in pyproject.toml:
poetry installThen you can run the tests to check that the installation is working correctly:
poetry run pytestThe package is designed to provide a set of tools to be used within a Python script or
notebook. The package documentation provides some minimal examples of using the various
modules within pyrealm - see the README for links to these
examples - but typically a user will load input data, pass it into imported pyrealm
classes and functions and then use the outputs in further functions.
Some of the package modules - such as the pmodel and splash modules - require input
data in arrays, typically with spatial and temporal dimensions loaded from NetCDF files
or similar formats. Other modules - such as the tmodel - work on a site by site basis
and require the configuration of site-specific details, such as the definition of plant
functional types and plant community structures.
The pyrealm repository includes both the pyrealm package and the
pyrealm_build_data package. The pyrealm_build_data package contains datasets that
are used in the pyrealm build process.
- Example datasets that are used in the package documentation, such as simple spatial datasets for showing the use of the P Model.
- "Golden" datasets including a set of input data and output predictions from
previous implementations of
pyrealmfunctionality. These are used to benchmarkpyrealmfunctionality within the testing framework (see below).
Note that pyrealm_build_data is a source distribution only (sdist) component of
pyrealm, so is not included in binary distributions (wheel), such as those available
from PyPi.
The datasets in pyrealm_build_data are stored via Git LFS.
After cloning the repository, only text pointers to these large files are downloaded.
In order to actually fetch the data into the local working tree,
you can run the command git lfs pull.
The package uses poetry to manage dependencies, generate virtual environments for code
development and then for package building and publication. You will need to install
poetry>=1.2.0 to develop the code.
The codebase is version-controlled using git with a repository at:
https://github.com/ImperialCollegeLondon/pyrealm
The package uses the git flow model for development and release:
- All code development should happen specific
feature/feature_namebranches. - Pull requests (PR) should be made to merge feature branches into the
developbranch. - Candidate release versions should be made on specific
release/x.y.zbranches and these are then committed to themainbranch only after final checking. - The
mainbranch should only ever contain commits representing new release versions - do not work on themainbranch.
Both the develop and main branches are protected on GitHub to avoid accidents!
The module codes uses docstrings written in the Google
style.
Unlike the main documentation pages, the docstrings in code are written using
reStructuredText because the autodoc functions in sphinx currently rely on rst
inputs. This allows the function documentation to be stored alongside the code and
included simply into the documentation.
The pyrealm package is documented using sphinx, with source material in the
docs/source directory.
The documentation in source uses Myst
Markdown rather than the standard
sphinx reStructuredText (.rst) format. This is a literate programming format that
can be used as a Jupyter notebook. The markdown format is more git friendly than the
standard Jupyter .ipynb format, which uses a JSON format and is updated to contain
non-essential code execution information.
When the package documentation is built in sphinx the myst_nb extension is used
automatically to execute the code in MyST markdown documents and render the resulting
code and outputs. The documentation can therefore contain dynamically generated examples
of running code and output plots to demonstrate the use and behaviour of the package.
This build process also ensures that code in documentation is kept up to date with the
package. The syntax for including code blocks in documents is shown below - this example
includes a tag that causes the code to be concealed in the built documentation, but with
a toggle button to allow it to be expanded.
```{code-cell} python
:tags: [hide-input]
# This is just an example code cell to demonstrate how code is included in
# the pyrealm documentation.
```
The sphinx configuration includes the sphinx.ext.mathjax extension to support
mathematical notation. This has been configured to also load the mhchem extension,
supporting the rendering of chemical notation.
The project uses continuous integration (CI) via GitHub Actions to maintain code quality
and check confirm that the package and website are building correctly. The
pyrealm_ci.yaml currently defines three CI jobs:
- code quality assurance (
qa) - code testing (
test) - documentation building (
docs_build)
The status of code checking for pushed commits can be seen at:
https://github.com/ImperialCollegeLondon/pyrealm/actions
Although GitHub Actions automates these steps for any pushes, pull requests and releases on the repository, you should also perform the same steps locally before submitting code to ensure that your code passes testing.
The project uses pre-commit to enforce code quality. The configuration file
.pre-commit-config.yaml shows the details of the tool
chain, but isort, black, flake8 and markdownlint are used to maintain code
quality.
You will need to install pre-commit to develop
package code. Once you have installed pre-commit you then need to install the
configuration for the repository:
pre-commit installAfter this step, all commits within the repository must then pass the suite of quality assurance checks. Note that the repository also uses a separate GitHub action to update the code quality assurance toolchain on a weekly basis, so you will periodically see updates to the checking.
The pyrealm package principally uses pytest to provide benchmark tests, unit tests
and integration testing. In addition, doctest is used to maintain examples of code
usage in the package docstrings and ensure that the documented return values are
correct.
The test directory contains modules providing test suites for each of the different
package modules. This includes:
- benchmark testing the output of
pyrealmcode against previously existing implementations of some functionality, such as therpmodelandSPLASHpackages. - unit testing of individual functions and methodsm and,
- integration testing of using combinations of modules.
These are the main tests that the package is behaving as expected and producing stable outputs and can be run from repository using:
poetry run pytestPytest-profiling is a plugin to pytest
that enables profiling of tests. It can be used to generate a call graph and to
determine the number of hits and total time spent in each function or method.
Graphviz is a package that uses dot command to
facilitate the rendering of the call graph and a manual install of graphviz is required
depending on the os. Dedicated profiling tests have been created for PyRealm.
Please see the relevant testing directory.
poetry run pytest --profileto generate a report. You can run
poetry run pytest --profile-svgto generate a call graph. The graph is saved at prof/combined.svg.
To enable profiling of a test function or class, decorate it with @pytest.mark.profiling.
Using the plugin pytest-coverage you can generate coverage reports. You can run
poetry run pytest --cov=<test_path>to perform coverage analysis. The report is stored with the name index.html.
It can be used to determine if your contribution is adequately tested.
The package docstrings contain doctest examples of code use. These examples are
intended to provide simple examples of method or function use and generate an output and
the doctest process is used to validate that the provided values are correct.
These tests are automatically run when pytest is run, but individual module files can
be checked using, for example:
poetry run python -m doctest pyrealm/pmodel/pmodel.pyNormally, doctest is used to test a return value but can also be used to check that an
error or warning is raised. See the example for pyrealm.utilities.convert_rh_to_vpd
which redirects the stderr to stdout to allow for the warning text to be included in the
doctest.
sphinx is used to build an HTML version of the package documentation provided in
docs/source and to build the API documentation provided in the code docstrings. The
sphinx building process requires some extra packages, but these are included in the
docs group in pyproject.toml and should be installed.
In order to build the package documentation, Jupyter needs to be able to associate the
documentation files with the Python environment managed by poetry. This is done by
installing the poetry environment as a new Jupyter kernel with a fixed name. This
allows all build systems to run notebooks using the correct build environment:
# Set ipython kernel
poetry run python -m ipykernel install --user --name=pyrealm_python3In order to build the package documentation, the following command can be used:
# Build docs using sphinx
cd docs
poetry run sphinx-build -W --keep-going source buildThe documentation for the package is hosted at:
https://pyrealm.readthedocs.io/en/develop/pmodel.html
This has been configured to automatically build commits to the main branch, which
should generate version specific sets of documentation.
The documentation uses the sphinxcontrib-bibtex package to support citations. This
uses Latex like citation keys in the documentation to insert references and build a
bibliography. The sphinx configuration in docs/source/conf.py provides a custom
Author/Year citation style. The reference library in source/refs.bib needs to be kept
up to date with the literature for the project.
Releasing a new version of the package follows the flow below:
- Create a
releasebranch fromdevelopcontaining the new release code. - Check that this branch builds correctly, that the documentation builds correctly and
that the package publishes to the
test-pypirepository. - When all is well, create pull requests on GitHub to merge the
releasebranch into bothdevelopandmain, along with a version tag for the release. - Once you have updated your local repository, then the tag can be used to build and publish the final version to the main PyPi site.
It is easier if git is configured to push new tags along with commits. This
essentially just means that new releases can be sent with a single commit. This only
needs to be set once.
set git config --global push.followTags trueUsing git flow commands as an example to create a new release:
git flow release start new_releaseObviously, use something specific, not new_release! Ideally, you would do a dry run of
the next step and use the version - but it should be fairly obvious what this will be!
The poetry version command can then be used to bump the version number. Note that the
command needs a 'bump rule', which sets which part of the semantic version number to
increment (major, minor or patch). For example:
poetry version patchThis updates pyproject.toml. At present, the package is set up so that you also have
to update the version number in pyrealm/version.py to match manually.
With those changes committed, publish the release branch:
git commit -m "Version bump" pyrealm/version.py
git flow release publish new_releaseThe GitHub Actions will then ensure that the code passes quality assurance and then runs the test suites on a range of Python versions and operating systems.
The sdist and wheel builds for the package can then be built locally using poetry
poetry buildThe first time this is run, poetry needs to be configured to add the Test PyPi
repository and an API token from that site. Note that accounts are not shared between
the Test and main PyPi sites: the API token for test-pypi is different from
pypi and you have to log in to each system separately and generate a token on each.
poetry config repositories.test-pypi https://test.pypi.org/legacy/
poetry config pypi-token.test-pypi <your-token>The built packages can then be published to test-pypi using:
poetry publish -r test-pypiLog in to:
which is the admin site controlling the build process. From the Versions tab, activate
the release/new_release branch and wait for it to build. Check the Builds tab to see
that it has built successfully and maybe check updates! If it has built succesfully, do
check pages to make sure that page code has executed successfully, and then go back to
the Versions tab and deactivate and hide the branch.
If all is well, then two PRs need to be made on GitHub:
- The
releasebranch intomain, to bring all commits since the last release and any fixes on release intomain. - The
releasebranch intodevelop, to bring anyreleasefixes back intodevelop.
Once both of those have been merged, the feature branch can be deleted.
Once the origin repository is merged, then use git pull to bring develop and
main up to date on a local repo. Then, create a tag using the release version.
git checkout main
git tag <version>
git push --tagsThe final commit on main is now tagged with the release version. You can add tags on
the GitHub website, but only by using the GitHub release system and we are using PyPi to
distribute package releases.
Before publishing a package to the main PyPi site for the first time, you need to set an API token for PyPi.
poetry config pypi-token.pypi <your-token>And now you can build the packages from main and publish.
poetry build
poetry publish