- Pull the rethink lambda logo (assets/img/logo.svg) into docs/assets and wire it as theme.logo + favicon — matches the Gitea brand mark. - standards.md: fold six thin sections into three fuller ones (Files and style / Documentation / Quality and error handling), each illustrated with python snippets, a flake8 output block, Do/Don't tabbed examples, a traceback, and a log line. Admonitions for the run-it-locally tip and the lib-logging note. - libraries.md: add a collapsible 'using a library' example (pyproject pin + import/usage python snippet). Verified in-browser: logo renders in the header, snippets/tabs/traceback/ log blocks render against the dark theme, libraries example expands. mkdocs build --strict clean. Signed-off-by: disqualifier <dev@disqualifier.me>
3.9 KiB
Coding Standards
The house standards every Rethink Studios project follows. They keep our code consistent, readable, and predictable across the whole suite.
Files and style
- Files end with a single trailing newline (LF / Unix line endings), and carry no trailing whitespace on any line.
- 4 spaces for indentation by default. Respect language norms — TS/JS use 2 spaces, Go uses tabs — and when editing an existing file, follow that file's existing indentation.
- flake8 clean, max line length 120. Type hints on public functions.
A well-formed module — module docstring, type hints, lowercase-start docstring:
"""async key/value store backed by a single json file"""
from pathlib import Path
def load_state(path: Path) -> dict[str, str]:
"""read the json state file, returning an empty dict if it's missing"""
if not path.exists():
return {}
return _read_json(path)
flake8 tells you the moment you drift — keep the tree clean:
$ flake8
./aiokv/store.py:14:80: E501 line too long (96 > 79 characters)
./aiokv/store.py:22:1: F401 'json' imported but unused
./aiokv/store.py:31:5: E303 too many blank lines (3)
!!! tip "Run it locally"
Wire the shared config into an alias so every project lints the same way:
alias flake8='flake8 --config ~/.config/flake8' (max line 120). See the
Workflow page.
Documentation
- Public functions get a docstring — lowercase-start, no trailing period.
- Keep inline comments minimal; prefer docstrings. Use an inline comment only where the code is genuinely complex.
- Each module/file states its scope and purpose via a module docstring (header string) — not a license or copyright header.
- Every library and project has a README with the install line, what it does, and a usage example.
=== "Do"
```python
def mask_secret(value: str, keep: int = 4) -> str:
"""mask all but the last ``keep`` characters of a secret"""
if len(value) <= keep:
return "*" * len(value)
return "*" * (len(value) - keep) + value[-keep:]
```
=== "Don't"
```python
# Masks a secret. <- license-header-style noise, capitalized, trailing period
def mask_secret(value, keep=4): # no type hints
# loop over the chars and hide them
return "*" * (len(value) - keep) + value[-keep:] # crashes if short
```
Quality and error handling
Fail loud — never swallow exceptions. Catch the specific exception and
log it; don't paper over failures with a bare except.
=== "Do"
```python
import logging
log = logging.getLogger(__name__)
def fetch(url: str) -> bytes:
"""fetch ``url``, logging and re-raising on failure"""
try:
return _client.get(url).content
except TimeoutError:
log.warning("fetch timed out: %s", url)
raise
```
=== "Don't"
```python
def fetch(url):
try:
return _client.get(url).content
except Exception:
pass # swallowed — the caller has no idea anything broke
```
When something does break, a loud failure gives you a real traceback to act on:
Traceback (most recent call last):
File "run.py", line 42, in <module>
data = fetch("https://example.test/feed")
File "aioweb/session.py", line 88, in fetch
return self._client.get(url).content
TimeoutError: request timed out after 30s
…and the log line that precedes it tells you where to look:
2026-06-29 14:03:11,204 WARNING aioweb.session fetch timed out: https://example.test/feed
!!! note "Logging belongs to the app, not the library"
Libraries emit only — log = logging.getLogger(__name__) and nothing
else. Handlers, levels, and formatting are configured once at the
application entry point, so a lib never dictates how its host logs.