# 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](https://docs.pytest.org)) ## 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](https://docs.astral.sh/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/activate`on 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`. ```python #!/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](https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html), and process them with [pydoctor](https://pydoctor.readthedocs.io/). For function arguments, return values, and exceptions we use the following style recognised by reStructuredText: ```python 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](https://docs.astral.sh/ruff/rules/#pydocstyle-d). 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.