Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion docs/integrations/writing-integrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -370,9 +370,27 @@ Each official integration is its own repository and PyPI package, mirroring the
`test_commands.py` (resolution through `FromDI`), and `test_websockets.py`
where the framework has websockets. Aim for the same 100%-coverage gate
`modern-di` holds.
- **Canonical example** (`examples/`). Ship a runnable `examples/app.py` (plus an
empty `examples/__init__.py`) demonstrating the recommended wiring: an
APP-scoped `Settings` plus one work-scoped service that depends on it by type,
resolved into a single handler/task/command via the framework's real idiom.
Use the `typing.Annotated[T, FromDI(...)]` marker form, not a `= FromDI(...)`
default (the default-call form trips ruff `B008`). Name the example's types to
match the integration's `docs/integrations/<framework>.md` snippet; diverge
only where testability requires it (e.g. return a value the test can assert).
A `tests/test_example.py` **smoke test** drives it through the repo's own
in-memory test double (test client / eager mode / in-memory broker — whatever
the existing tests use) and asserts the **real injected output**, never a mock.
The smoke test must cover `examples/app.py` to **100%** under the coverage
gate — do **not** add a coverage `omit`; mark `# pragma: no cover` only on a
genuinely unreachable boot line (`if __name__ == "__main__"` / server-run).
Link it from the README with a `Usage example: [examples/](./examples)` line
directly under `Full guide:`.
- **Mirror `modern-di`'s** `CLAUDE.md`, `Justfile`, and `architecture/` truth
home. Keep resolution sync-only and add no runtime dependency beyond the
framework and `modern-di`.
framework and `modern-di`. `ruff` is unpinned and CI floats it forward, so keep
`CPY001` (no per-file copyright header) in the lint `ignore` and reflow any
pre-existing Markdown-embedded code fences the current `ruff` reformats.
- **Docs.** Add a `docs/integrations/<framework>.md` usage page **in the
`modern-di` repo** and a nav entry for it in `mkdocs.yml` (under the matching
family group: Web / Tasks & events / Bots / RPC / CLI / Testing). Follow the
Expand Down Expand Up @@ -427,6 +445,9 @@ Each official integration is its own repository and PyPI package, mirroring the
- [ ] Tests cover lifespan (incl. restart), resolution through `FromDI`, and
context injection from the connection object; coverage gate green.
- [ ] Usage page + `mkdocs.yml` nav entry added in the `modern-di` repo.
- [ ] `examples/app.py` (+ smoke test asserting real injected output, 100%
coverage, no `omit`) and a README `Usage example: [examples/](./examples)`
line.
- [ ] `CLAUDE.md`, `Justfile`, `architecture/` mirrored;
[planning-convention](https://github.com/lesnik512/planning-convention)
followed.
53 changes: 53 additions & 0 deletions planning/changes/2026-07-25.01-codify-canonical-example.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
---
summary: Codified the canonical-example authoring rule in writing-integrations.md — every integration ships a runnable examples/app.py + a 100%-covering in-memory smoke test (no coverage omit) + a README Usage-example line — after piloting it in faststream and rolling it to the other 9.
---

# Change: Codify the canonical-example authoring rule

**Lane:** lightweight — docs-only, one authoring-doc edit (`writing-integrations.md`)
plus a checklist item; no code or public-API change.

## Goal

Turn the canonical-example pattern — piloted in `modern-di-faststream` and rolled
out to the other 9 integrations — into a documented authoring rule so present and
future integrations conform, instead of a de-facto pattern read off the existing
repos.

## Approach

Add a **Canonical example** bullet to `## Repo scaffolding` in
`docs/integrations/writing-integrations.md` and a matching `## Checklist` item.
The rule captures the shape the pilot locked plus the refinements the 9-repo
rollout surfaced:

- `examples/app.py` (+ empty `examples/__init__.py`): APP-scoped `Settings` + one
work-scoped service depending on it by type, resolved into one
handler/task/command via the framework's real idiom.
- Use the `typing.Annotated[T, FromDI(...)]` marker form, not a `= FromDI(...)`
default (the default-call form trips ruff `B008`; the pilot needed a `noqa`, the
rollout avoided it with `Annotated`).
- Name the example's types to match the integration's docs-page snippet; diverge
only for testability.
- `tests/test_example.py` smoke test via the repo's own in-memory test double,
asserting real injected output (never a mock), covering `examples/app.py` to
**100%** — no coverage `omit`; `# pragma: no cover` only a genuinely unreachable
boot line.
- README `Usage example: [examples/](./examples)` line under `Full guide:`.
- Plus a ruff note: `ruff` is unpinned and CI floats it, so keep `CPY001` ignored
and reflow any pre-existing Markdown code fences the current `ruff` reformats
(every rollout PR hit this).

Not an `architecture/` capability change — this is authoring guidance for the
sibling repos, so it lives only in `writing-integrations.md`.

## Files

- `docs/integrations/writing-integrations.md` — Canonical-example scaffolding
bullet + ruff-float note + checklist item.
- `planning/changes/2026-07-25.01-codify-canonical-example.md` — this file.

## Verification

- [x] `just check-planning` — bundle validates.
- [x] `just lint-ci` — no-autofix lint (ruff 0.16) + planning validation clean.
Loading