# Python Development Rules
These rules describe how you must develop any Python projects.
Use the `uv` package & project manager to operate all projects.
If `uv` is not available in your environment, you can install it with `pipx` if available:
```bash
pipx install uv
```
Or with curl, as `uv` can later update itself:
```
curl -LsSf https://astral.sh/uv/install.sh | sh
```
Once in a dedicated directory, the project can be set up with:
```bash
uv self update # ensure latest uv
uv init --bare # create pyproject.toml file
uv venv # create .venv/ setup
git init -b main
```
The `uv init` step might optionally take `--app`, `--lib`, or `--package` as argument instead of `--bare` if you know already that it will be more appropriate:
- `--app`: web servers, command-line interfaces (with a main.py file at the root)
- `--lib`: functions and objects for other projects to consume (no entry point)
- `--package`: Python package development with a build system
## Assumptions
- Source code is in `./$CODE/` (e.g., `src/`, `mypack/`, etc.)
- Tests are in `./tests/`
- All tools must be run via `uv run` (do not invoke tools directly).
- All code is **strictly type-checked**, no excuses.
- **Never** add import statements anywhere else than the header of a module.
## Required tools
- black (code formatting)
- ruff (lint, including bandit-like security audit)
- ty (type safety)
- pytest (test runner)
- pip-audit (dependencies audit)
Install tool-chain into the project environment with `uv`:
```bash
uv add --dev black ruff ty pytest pip-audit
````
### Format the code
Black will automatically fix any code formatting issues.
```bash
uv run black $CODE tests
```
### Lint and auto-fix where possible
Use an extensive ruleset for security, bugs, and other poor coding habits.
Enforce PEP8 naming with N, too.
`black` handles line lengths, so that can be ignored.
```bash
uv run ruff check $CODE tests --select E,F,W,S,B,C4,I,N --ignore E501 --fix
```
If you get tired of this long command line, these rules can be configured in the `pyproject.toml` file:
```toml
[tool.ruff]
select = [
"E", # pycodestyle errors (default)
"W", # pycodestyle warnings (default)
"F", # pyflakes
"B", # flake8-bugbear
"S", # flake8-bandit
"C4", # flake8-comprehensions
"I", # isort
"N", # pep8-naming
]
ignore = [
"E501", # line too long, handled by black
]
```
### Write type-safe code
```bash
uv run ty $CODE
```
### Do test-driven development
Write failing tests before implementation. Follow [@testing](testing.md) guidelines. And run the tests with `pytest`:
```bash
uv run pytest tests
```
### Audit dependencies
Only needed when you add a new package of once in a while, to ensure no dependencies have known vulnerabilities.
```bash
uv run pip-audit
```
## Code standards
- Keep functions small, single-purpose, and easy to test.
- Ensure all (function) interfaces and non-private variables are type-checked.
- Never make an exception like "if TYPE_CHECKED: ...", assume all code is type-checked.
- Prefer expressive code over comments.
- Only comment when the logic is genuinely hard to understand.
- Never add imports anywhere except at the head of the module.
## Definition of Done
After making a test green and the refactoring is done, ensure all tools pass or fix any issues.
Note that `ruff` has to be configured in the `pyproject.toml` file or you need to use the extended arguments to select rulesets shown above in the linting section.
```bash
uv run black $CODE tests
uv run ty $CODE
uv run ruff check $CODE tests --fix
uv run pytest tests
uv run pip-audit
```