Contributing

If you have a minor issue with the software, it is likely easiest to open an issue for the maintainers to address. For larger changes, it may help to discuss them first either by opening an issue, or in the discussion section.

For development you will need:

  • Python

  • A C++ compiler with support for C++20 (recent GCC, Clang, or MSVC)

  • tox

  • Git and a GitHub account (if you want to submit your changes to the project)

Once you have these installed you have the tools you need to make changes to the code or documentation, test your changes, and submit them through a pull request.

To build wheels or sdists to distribute to your users (either original or modified), consider using build:

$ python -m build

which will produce a source distribution and installable wheel in the dist/ directory.

Note

If you submit changes to this project, they will be distributed under the same 3-clause BSD license as the rest of the project. By submitting changes you accept this licensing, and indicate that you have the ability to license your changes in this way including authorization from your employer, if necessary.

Workflow

General steps:

  1. Fork and clone the repository

  2. Create a new local branch

  3. Make your changes

  4. Run the tests

  5. Push your changes to your fork

  6. Submit a pull request

Checkout locally

For development you will need a local clone of the repository. If you only want to build the software for your own use you can simply clone our repository directly. For more information, see GitHub’s instructions for cloning a repository.

If you want to submit your changes, you will need to make a pull request for which you will need a fork. For information on forking repositories and making pull requests see GitHub’s introduction to making contributions.

Make your changes

Once you have a local copy of our repository you can begin to edit the source code and documentation. If you are making code changes try to do as much as possible in Python since this simplifies development, testing, and maintenance. Only use C++ in cases where using Python would have a large performance impact, or would require duplicating functionality in native code.

See the subsections below for some things to keep in mind as you are editing.

Python

We manage Python formatting and style with a few linting tools. All of these are run with the tests using tox (see information on running tests below).

As you modify the code, add any new public functions to __all__ in their modules and make sure that all public functions have complete docstrings. Private functions should have names starting with an underscore and should not be listed in __all__.

For each new function, consider its call signature. Functions of a single parameter, or for which parameters do not have descriptive names, should use positional-only parameters. Some function arguments—particularly boolean flags—should be keyword-only. See information on parameters from the Python documentation for more information on parameter types.

If you add any new imports make sure not to add any import cycles.

Don’t use Python assert statements except in tests.

C++

Use only features from C++20. The standard library can be used, but only parts which do not require special C++ runtime support. In particular this means no functionality which throws exceptions or requires cleanup after exceptions (only trivially-destructible types). Avoid compiler-specific extensions, but if you use them provide fallback versions with the preprocessor.

This produces code which can use templates, but otherwise handles errors and memory allocation C-style. Make sure to handle error flows and to clean up your memory and decrement Python refcounts where necessary.

Any errors at runtime should be reported as Python exceptions from the interface code in adrt_cdefs_py.cpp. Our convention is that functions in the adrt::_py namespace set Python exceptions on error. Other functions in the adrt namespace do not and will need extra handling. No Python APIs are used outside of adrt_cdefs_py.cpp. Only use features from the Limited API for the oldest actively-supported version of Python.

Put non-template and non-inline functions in cpp files, likely adrt_cdefs_common.cpp.

Include assertions with assert for conditions which are required for correctness (not error handling). In particular, assertions on function arguments to check preconditions and document requirements. Use static_assert to check type-level requirements.

Documentation

All public functions must have complete docstrings. For functions which are implemented in C++ we add docstrings with the Python wrappers in _wrappers.py. Docstrings are written in NumPy format. Make sure that any new functions appear on the correct API reference page.

The documentation is generated using Sphinx. All documentation source code is in the docs/ directory and HTML pages can be generated using tox:

$ tox run -e docs

After which the main page will be at docs/_build/html/index.html. If you have made documentation changes, look over the generated pages to check formatting.

Tests

Every public function should have tests. We use pytest for testing from Python. Each function being tested has a separate test file in the tests/ directory.

As much as possible, test not only expected use, but also error cases (invalid arguments, array dtypes or shapes, etc.).

Functions in C++ that are not exposed to Python, like many of the functions in adrt_cdefs_common.hpp can be tested using Catch2. Each function being tested has its own test file under tests/cpp/.

Commit Messages

Include a short summary of the changes on the first line. For changes that are more involved, include further details in a new paragraph after a blank line. In particular, discuss why the change was made.

Make sure your Git client is configured with your name and email before committing.

Run tests

Once you have finished making your changes, you should run tests locally and ensure that they pass before making a pull request.

We use tox to run our tests and linters. Tox will install required dependencies then run the tests. First, install tox, then run:

$ tox

This will also run our linters and report any Python style or formatting issues.

Native Tests

You do not need to run C++ tests unless you have made changes to them or have edited the functions they cover (in particular those in adrt_cdefs_common.hpp). In most cases you can disregard this section.

To run the tests, you will need a copy of Catch2 version 3. We have a Python script tools/download_catch2.py to retrieve one (requires httpx[http2]):

$ python tools/download_catch2.py tests/cpp/catch2/

Then on a Linux system, run:

$ g++ -std=c++20 -g -Wall -Wextra -Wpedantic $(find src/adrt tests/cpp/ -name '*.cpp' -not -name 'adrt_cdefs_py.cpp') -I src/adrt/ -o tests/cpp/test_all
$ tests/cpp/test_all

A similar compilation process should work on other systems. Generally, you need to compile all *.cpp files except adrt_cdefs_py.cpp and add src/adrt/ to the include search path.

Submit a pull request

Once your changes are ready to submit, push your working branch to your repository fork. Then create a pull request for the branch with your edits.

Automated tests will run on your pull request (including some that are not run locally). Check the compilation logs for the automated runs and fix any compiler warnings or test failures.

Thank you for your contribution!