openflexure-microscope-server/apidocs/dev/python.md

3.7 KiB

Python Server Development

Overview

Python code should be:

  • Broken up into small, clear, testable functions.
  • Documented with docstrings (For conventions see below).
  • Type hinted
  • Covered by unit tests (using pytest)

Python environment, build, and dependencies

As of v3 we specify dependencies in pyproject.toml. These are not currently frozen due to difficulties matching versions on different platforms. On Raspberry Pi, it's best to specify --only-binary=:all: when installing packages, to ensure libraries like numpy and scipy are not compiled from source (which takes many hours, and/or fails). Pinning dependencies with requirements.txt and/or requirements.in may happen in the future.

Python: Formatting, linting, and tests

Ruff is used both for linting and formatting the codebase. Our custom linter rules are in our pyproject.toml.

All of the commands below assume that you are running in the OFM virtual environment, i.e. you have run ofm activate on an OpenFlexure SD card, or source .venv/bin/activate on Linux, or .venv/Scripts/activateon Windows Powershell, or .venv\Scripts\activate.bat on Windows CMD.

Before committing you should lint and auto-format your code:

  • To lint, run ruff check and manually fix any flagged issues
  • To auto-format the Python code run ruff format
  • To check for spelling errors codespell .

Before pushing please auto-format your code and also run the quality checks (linting, static analysis, and unit tests)

  • All commands above
  • pytest
  • mypy src

We use several code analysis and formatting libraries in this project. Our CI will check each of these automatically, so ensuring they pass locally will save you time. Currently ruff is used for linting/formatting and pytest for unit tests.

To check your code before each commit we recommend putting the following script in your .git/hooks directory with the filename pre-commit.

#!/bin/bash

set -e

function angrymessage()
{
    RED='\033[1;31m'
    NC='\033[0m' # No Color
    printf "\n${RED}Not committing as ruff or spellcheck failed${NC}\n\n"
}

trap angrymessage ERR

ruff format --check
ruff check
codespell .

Docstrings conventions.

We write docstrings in reStructuredText, and process them with pydoctor.

For function arguments, return values, and exceptions we use the following style recognised by reStructuredText:

def a_function(arg1: str, arg2: int) -> tuple[float, int]:
    """A one sentence summary, in imperative mood, ending with punctuation.

    More detail after a blank line. This could be a whole paragraph.

    When referring to ``code`` in reStructuredText used double backticks

    :param arg1: This is the first argument, we can say everything we need to about it.
        If the text wraps onto a new line, this line is indented.
    :param arg2: This is the second argument.

    :returns: Say what it returns without anything within the colons. If returning
        multiple values, a bulleted list is a good idea - just make sure:

        * It is indented to 1 level in
        * There is a blank line before the bullets
        * The bullet points are ``*`` not ``-``
    """

+The linter rules we use for docstrings are defined by pydocstyle. We do not use rules D203, D213, D400 for reasons explained in our pyproject.toml file.

The documentation generated by pydoctor from docstrings can be viewed in any merge request (if the CI pipeline has passed) using the "View App" button.