Files
gnwmanager/.github/contributing.md
2023-08-19 15:52:11 -07:00

44 lines
2.8 KiB
Markdown

## Environment Setup
1. We use [Poetry](https://python-poetry.org/docs/#installation) for managing virtual environments and dependencies.
Once Poetry is installed, run `poetry install` in this repo to get started.
2. For managing linters, static-analysis, and other tools, we use [pre-commit](https://pre-commit.com/#installation).
Once Pre-commit is installed, run `pre-commit install` in this repo to install the hooks.
Using pre-commit ensures PRs match the linting requirements of the codebase.
## Documentation
Whenever possible, please add docstrings to your code!
We use [numpy-style napoleon docstrings](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/#google-vs-numpy).
To confirm docstrings are valid, build the docs by running `poetry run make html` in the `docs/` folder.
I typically write dosctrings first, it will act as a guide to limit scope and encourage unit-testable code.
Good docstrings include information like:
1. If not immediately obvious, what is the intended use-case? When should this function be used?
2. What happens during errors/edge-cases.
3. When dealing with physical values, include units.
## Unit Tests
We use the [pytest](https://docs.pytest.org/) framework for unit testing. Ideally, all new code is partners with
new unit tests to exercise that code. If fixing a bug, consider writing the test first to confirm the existence of the
bug, and to confirm that the new code fixes it.
Unit tests should only test a single concise body of code. If this is hard to do, there are two solutions that can help:
1. Restructure the code. Keep inputs/outputs to be simple variables. Avoid complicated interactions with state.
2. Use [pytest-mock](https://pytest-mock.readthedocs.io/en/latest/) to mock out external interactions.
## Coding Style
In an attempt to keep consistency and maintainability in the code-base, here are some high-level guidelines for code that might not be enforced by linters.
* Use f-strings.
* Keep/cast path variables as `pathlib.Path` objects.
Do not use `os.path`.
For public-facing functions, cast path arguments immediately to `Path`.
* Use magic-methods when appropriate. It might be better to implement ``MyClass.__call__()`` instead of ``MyClass.run()``.
* Do not return sentinel values for error-states like `-1` or `None`. Instead, raise an exception.
* Avoid deeply nested code. Techniques like returning early and breaking up a complicated function into multiple functions results in easier to read and test code.
* Consider if you are double-name-spacing and how modules are meant to be imported.
E.g. it might be better to name a function `read` instead of `image_read` in the module `my_package/image.py`.
Consider the module name-space and whether or not it's flattened in `__init__.py`.
* Only use multiple-inheritance if using a mixin. Mixin classes should end in `"Mixin"`.