Style: PEP 8, black, ruff (formatting and linting)

Consistent code style is not a matter of taste — it is about reducing the cost of reading. In ZEUS the backend (FastAPI + SQLAlchemy + ARQ) is written by several people, so we enforce style automatically rather than in PR comments. Two tools handle 95% of the work: black (formatting) and ruff (linting, and largely an isort replacement too).

PEP 8 in a nutshell

PEP 8 is Python's official style guide. The things we care about most:

  • 4 spaces for indentation, never tabs.
  • snake_case for functions and variables, PascalCase for classes, UPPER_CASE for constants.
  • Line length: at ProfessNet 88 characters (the black default), not 79.
  • Imports: stdlib → third-party → local, separated by a blank line.

black — the end of formatting debates

black is "uncompromising": you don't configure the style, you accept it.

# bad — manual, inconsistent formatting
d = {'host':probe.host,'forest':probe.forest ,  'ts': now()}

# good — after `black .`
d = {"host": probe.host, "forest": probe.forest, "ts": now()}

ruff — the fast linter

ruff (written in Rust) combines flake8, isort, pyupgrade and dozens of other rules. It catches dead imports, unused variables and bad patterns.

# ruff: F401 'os' imported but unused  → remove the import
import os
from app.probes import run_inventory


async def collect(forest: str) -> list[dict]:
    return await run_inventory(forest)

The ProfessNet standard — pyproject.toml

We keep the configuration in a single pyproject.toml in the service directory:

[tool.black]
line-length = 88
target-version = ["py312"]

[tool.ruff]
line-length = 88
target-version = "py312"

[tool.ruff.lint]
select = ["E", "F", "I", "UP", "B"]  # style, errors, imports, upgrade, bugbear
ignore = ["E501"]                     # line length is handled by black

ProfessNet standard: select = ["E", "F", "I", "UP", "B"] is our minimum. I replaces isort — we don't install isort separately.

Running it

ruff check --fix .   # fix whatever can be fixed automatically
black .              # format
ruff check .         # check whether anything needs manual fixing

We run the same commands in pre-commit and in CI. A PR that fails ruff check or changes anything after black --check does not get into main.

Tip: in VS Code set black as the formatter and enable "format on save", plus the ruff extension — then you won't see a red CI locally.


The rule is simple: we don't debate formatting, because a machine does it for us. We move the energy from code review onto logic, security and naming — which we'll cover in the next lessons.