diff --git a/brand/build/projects.py b/brand/build/projects.py index f01310c..ccce029 100644 --- a/brand/build/projects.py +++ b/brand/build/projects.py @@ -20,6 +20,7 @@ "that-depends": lambda: sym.graph(_CX, _CY, R, dashed=False), "modern-di-fastapi": lambda: sym.bolt_disc(_CX, _CY, R), "modern-di-litestar": lambda: sym.star_disc(_CX, _CY, R), + "modern-di-starlette": lambda: sym.sparkle_cluster(_CX, _CY, R), "modern-di-faststream": lambda: sym.faststream(_CX, _CY, R), "modern-di-typer": lambda: sym.terminal(_CX, _CY, R), "modern-di-pytest": lambda: sym.bars(_CX, _CY, R), diff --git a/brand/build/symbols.py b/brand/build/symbols.py index c5c2aaf..e538d8e 100644 --- a/brand/build/symbols.py +++ b/brand/build/symbols.py @@ -54,6 +54,36 @@ def _star5(cx: float, cy: float, radius: float, color: str, inner: float = 0.42) return f'' +def _sparkle4(cx: float, cy: float, radius: float, color: str, inner: float = 0.34) -> str: + """Four-point sparkle (concave star) centred on (cx,cy).""" + pts: list[tuple[float, float]] = [] + for i in range(4): + ao = -90 + i * 90 + pts.append( + ( + cx + radius * math.cos(math.radians(ao)), + cy + radius * math.sin(math.radians(ao)), + ) + ) + ai = ao + 45 + pts.append( + ( + cx + radius * inner * math.cos(math.radians(ai)), + cy + radius * inner * math.sin(math.radians(ai)), + ) + ) + body = " ".join(f"{x:.1f},{y:.1f}" for x, y in pts) + return f'' + + +def sparkle_cluster(cx: float, cy: float, r: float) -> str: + """Starlette cue: a large four-point sparkle with a small companion + (a "little star" — starlette).""" + big = _sparkle4(cx - 0.174 * r, cy + 0.130 * r, r * 0.82, GOLD) + small = _sparkle4(cx + 0.565 * r, cy - 0.522 * r, r * 0.36, GOLD) + return big + small + + def _circ_arc(cx: float, cy: float, rad: float, a0: float, a1: float, w: float) -> str: """Clockwise arc a0->a1 (deg, increasing) with a leading arrowhead at a1.""" a1s = a1 - 7 # stop the stroke short so the head caps it cleanly diff --git a/brand/projects/modern-di-starlette/lockup-dark.svg b/brand/projects/modern-di-starlette/lockup-dark.svg new file mode 100644 index 0000000..a9f8867 --- /dev/null +++ b/brand/projects/modern-di-starlette/lockup-dark.svg @@ -0,0 +1 @@ + diff --git a/brand/projects/modern-di-starlette/lockup-light.svg b/brand/projects/modern-di-starlette/lockup-light.svg new file mode 100644 index 0000000..e45b907 --- /dev/null +++ b/brand/projects/modern-di-starlette/lockup-light.svg @@ -0,0 +1 @@ + diff --git a/brand/projects/modern-di-starlette/lockup.png b/brand/projects/modern-di-starlette/lockup.png new file mode 100644 index 0000000..1b92deb Binary files /dev/null and b/brand/projects/modern-di-starlette/lockup.png differ diff --git a/brand/projects/modern-di-starlette/mark-1024.png b/brand/projects/modern-di-starlette/mark-1024.png new file mode 100644 index 0000000..25040de Binary files /dev/null and b/brand/projects/modern-di-starlette/mark-1024.png differ diff --git a/brand/projects/modern-di-starlette/mark-512.png b/brand/projects/modern-di-starlette/mark-512.png new file mode 100644 index 0000000..eafd980 Binary files /dev/null and b/brand/projects/modern-di-starlette/mark-512.png differ diff --git a/brand/projects/modern-di-starlette/mark.svg b/brand/projects/modern-di-starlette/mark.svg new file mode 100644 index 0000000..19d4914 --- /dev/null +++ b/brand/projects/modern-di-starlette/mark.svg @@ -0,0 +1 @@ + diff --git a/docs/index.md b/docs/index.md index 1c5d487..3b572c4 100644 --- a/docs/index.md +++ b/docs/index.md @@ -67,7 +67,7 @@ production Python services. Use one piece or all of them — each is independent - **Wire your dependencies** with [`modern-di`](https://github.com/modern-python/modern-di) — typed, scoped dependency injection with one wiring shared across FastAPI, Litestar, - FastStream, and Typer. + Starlette, FastStream, and Typer. ([`that-depends`](https://github.com/modern-python/that-depends), its production-proven predecessor, is still maintained.) - **Call other services reliably** with @@ -98,6 +98,7 @@ catalog below. - [`modern-di`](https://github.com/modern-python/modern-di) — powerful DI framework with scopes. - [`modern-di-fastapi`](https://github.com/modern-python/modern-di-fastapi) — `modern-di` integration for FastAPI. - [`modern-di-litestar`](https://github.com/modern-python/modern-di-litestar) — `modern-di` integration for Litestar. +- [`modern-di-starlette`](https://github.com/modern-python/modern-di-starlette) — `modern-di` integration for Starlette. - [`modern-di-faststream`](https://github.com/modern-python/modern-di-faststream) — `modern-di` integration for FastStream. - [`modern-di-typer`](https://github.com/modern-python/modern-di-typer) — `modern-di` integration for Typer. - [`modern-di-pytest`](https://github.com/modern-python/modern-di-pytest) — `modern-di` integration for pytest. diff --git a/planning/changes/2026-07-02.01-starlette-brand/design.md b/planning/changes/2026-07-02.01-starlette-brand/design.md new file mode 100644 index 0000000..444c3a7 --- /dev/null +++ b/planning/changes/2026-07-02.01-starlette-brand/design.md @@ -0,0 +1,127 @@ +--- +summary: Add modern-di-starlette to the org brand kit (sparkle-cluster mark) and list it on the profile README, docs site, and its GitHub repo settings. +--- + +# Design: Onboard modern-di-starlette into the org brand and surfaces + +## Summary + +`modern-di-starlette` is a new DI integration. Its repo already ships a README +that references central brand assets under +`brand/projects/modern-di-starlette/` — assets that do not exist yet, so those +image links are currently broken. This change mints the project's mark (a +four-point **sparkle cluster**), regenerates its brand assets, adds it to the +`profile/README.md` DI table and the `docs/index.md` DI list, and brings its +GitHub repo metadata (description, topics, website) in line with org +conventions. All code changes live in this repo (`.github`); the integration +repo itself is already complete. + +## Motivation + +The `modern-di-starlette` README (in its own repo) points at +`https://raw.githubusercontent.com/modern-python/.github/main/brand/projects/modern-di-starlette/lockup-{light,dark}.svg` +and `lockup.png`. None of those files exist, so the README renders with a broken +image. Every sibling integration (`modern-di-fastapi`, `modern-di-litestar`, +`modern-di-faststream`, `modern-di-typer`, `modern-di-pytest`) has a mark, a +profile-README row, and a docs-site entry; Starlette must match to keep the +three metadata surfaces (GitHub description, pyproject `description`, profile +blurb) consistent. + +## Non-goals + +- No changes to the `modern-di-starlette` repo contents (README, CI, planning, + architecture already present and correct). +- No social card: integrations are not in `DOCS_REPOS`, matching the other + `modern-di-*` integrations, which have no per-project docs site. +- No new symbol vocabulary beyond the one Starlette mark. + +## Design + +### 1. Sparkle-cluster symbol — `brand/build/symbols.py` + +Add a `sparkle_cluster(cx, cy, r)` function: a large four-point sparkle with a +small secondary sparkle at upper-right, both filled in `GOLD` (no disc). The +four-point sparkle is a concave 8-vertex polygon (outer points at N/E/S/W, +inner points at the 45-degree diagonals). Uses only `GOLD`, so it satisfies the +`test_only_allowed_colours` palette check. It follows the same contract as the +existing `star_disc` / `bolt_disc` (takes `cx, cy, r`, returns bare SVG markup). + +The exact geometry is the approved candidate B, verified in the visual +companion: large sparkle centred slightly down-left, `r*0.82`, `inner=0.34`; +small sparkle up-right, `r*0.36`. The `project_lockup` dark-mode path already +swaps `GOLD_LIGHT` -> `GOLD_DARK` globally, so the dark lockup needs no special +handling. + +### 2. Manifest entry — `brand/build/projects.py` + +Add `"modern-di-starlette": lambda: sym.sparkle_cluster(_CX, _CY, R)` to +`MANIFEST`, in the dependency-injection block, immediately after +`modern-di-litestar` (keeps the web-framework integrations fastapi -> litestar -> +starlette adjacent). + +### 3. Test — `tests/test_projects.py` + +Add `"modern-di-starlette"` to `EXPECTED_REPOS`. That set is asserted equal to +`MANIFEST`, and the valid-SVG / allowed-colours / render tests are parametrized +off it, so no other test edits are required. `DOCS_EXPECTED` is unchanged +(Starlette gets no social card). + +### 4. Regenerate assets — `just sync-assets` + +Runs `python -m brand.build.render`, producing +`brand/projects/modern-di-starlette/{mark.svg, mark-512.png, mark-1024.png, +lockup-light.svg, lockup-dark.svg, lockup.png}`. Commit the generated files. +This resolves the integration repo's broken README image links. + +### 5. Profile README — `profile/README.md` + +Add one row to the **Dependency injection** table, immediately after the +`modern-di-litestar` row, blurb `modern-di integration for Starlette`, carrying +the same badge set as its siblings (Stars, PyPI, Downloads, Context7). + +### 6. Docs site — `docs/index.md` + +Add one bullet to the `Dependency injection { #di }` list after the Litestar +line: `` `modern-di-starlette` — `modern-di` integration for Starlette.`` Also +extend the "The stack" sentence to read "...shared across FastAPI, Litestar, +Starlette, FastStream, and Typer" so the narrative names the new integration +(matching the table/manifest order fastapi -> litestar -> starlette). + +## Operations + +GitHub repo settings for `modern-python/modern-di-starlette`, set via `gh` to +match org conventions: + +- **Description:** `modern-di integration for Starlette` (the canonical + one-liner, matching pyproject and the profile blurb). +- **Website:** `https://modern-di.modern-python.org` (integrations have no own + docs site; they point at the modern-di docs). +- **Topics:** the `modern-di-*` integration base set plus the framework — + `python, dependency-injection, di, ioc-container, modern-di, starlette`. + +These are verified against the live repo before/after: read current values, +compare to the intended set, apply only the diff. + +## Testing + +- `just test` green, in particular `tests/test_projects.py`: + `test_manifest_covers_every_repo` (MANIFEST == EXPECTED_REPOS), + `test_only_allowed_colours`, `test_project_mark_is_valid_svg`, and the + render tests. +- Visually inspect the regenerated `mark.svg` and `lockup-light/dark.svg` / + `lockup.png` for the Starlette entry. +- Confirm the integration repo's README image links now resolve to real files + at the referenced paths. +- `just check-planning` passes before pushing. +- `gh repo view modern-python/modern-di-starlette` reflects the intended + description, homepage, and topics. + +## Risk + +- **Low: mark reads too close to Litestar.** Mitigated — the sparkle cluster is + four-point and disc-less, visibly distinct from Litestar's 5-point star-in- + disc; confirmed against the sibling marks in the visual companion. +- **Low: stray colour fails the palette test.** Mitigated — the symbol uses only + `GOLD`; `test_only_allowed_colours` is the guardrail. +- **Low: GitHub settings drift from pyproject/profile.** Mitigated — all three + use the single canonical one-liner `modern-di integration for Starlette`. diff --git a/planning/changes/2026-07-02.01-starlette-brand/plan.md b/planning/changes/2026-07-02.01-starlette-brand/plan.md new file mode 100644 index 0000000..0a3bbe2 --- /dev/null +++ b/planning/changes/2026-07-02.01-starlette-brand/plan.md @@ -0,0 +1,272 @@ +# starlette-brand — implementation plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use +> superpowers:subagent-driven-development (recommended) or +> superpowers:executing-plans to implement this plan task-by-task. Steps +> use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Onboard `modern-di-starlette` into the org brand kit and every surface +its siblings appear on (mark, profile README, docs site, GitHub repo settings). + +**Spec:** [`design.md`](./design.md) + +**Branch:** `starlette-brand` (already created) + +**Commit strategy:** Per-task commits. + +## Global Constraints + +- Canonical one-liner, used verbatim on every surface: `modern-di integration for Starlette`. +- Brand marks may use only the allowed palette; the Starlette mark uses only `GOLD` (`GOLD_LIGHT = #c98a00`). `test_only_allowed_colours` is the guardrail. +- Ordering everywhere: web-framework integrations run fastapi -> litestar -> starlette (starlette inserted immediately after litestar) in the manifest, the profile table, the docs list, and the stack sentence. +- All Python edits: imports at module level, annotate every argument (repo house style + ruff `ALL`). +- Run from repo root `/Users/kevinsmith/src/pypi/modern-python`. Commands use `uv run` / `just` as the repo does. + +--- + +### Task 1: Mint the Starlette mark (symbol + manifest + test) + +**Files:** +- Modify: `tests/test_projects.py` (add repo to `EXPECTED_REPOS`) +- Modify: `brand/build/symbols.py` (add `_sparkle4` + `sparkle_cluster`) +- Modify: `brand/build/projects.py` (add `MANIFEST` entry) + +**Interfaces:** +- Produces: `sparkle_cluster(cx: float, cy: float, r: float) -> str` in `brand.build.symbols`, returning bare SVG markup (no `` wrapper), consumed by `MANIFEST["modern-di-starlette"]` in `brand.build.projects`. + +- [ ] **Step 1: Write the failing test** + + In `tests/test_projects.py`, add `"modern-di-starlette"` to the `EXPECTED_REPOS` set, immediately after the `"modern-di-litestar"` line: + + ```python + "modern-di-litestar", + "modern-di-starlette", + "modern-di-faststream", + ``` + +- [ ] **Step 2: Run the test to verify it fails** + + Run: `uv run pytest tests/test_projects.py -q` + Expected: FAIL — `test_manifest_covers_every_repo` asserts `set(p.MANIFEST) == EXPECTED_REPOS` and the manifest is missing `modern-di-starlette`; the `project_mark`/`only_allowed_colours` params for that repo also error with `KeyError: 'modern-di-starlette'`. + +- [ ] **Step 3: Add the sparkle symbols** + + In `brand/build/symbols.py`, add these two functions next to `_star5` (which ends just before `_circ_arc`). `_sparkle4` is a private helper; `sparkle_cluster` is the public mark. Colours: `GOLD` only. + + ```python + def _sparkle4(cx: float, cy: float, radius: float, color: str, inner: float = 0.34) -> str: + """Four-point sparkle (concave star) centred on (cx,cy).""" + pts: list[tuple[float, float]] = [] + for i in range(4): + ao = -90 + i * 90 + pts.append( + ( + cx + radius * math.cos(math.radians(ao)), + cy + radius * math.sin(math.radians(ao)), + ) + ) + ai = ao + 45 + pts.append( + ( + cx + radius * inner * math.cos(math.radians(ai)), + cy + radius * inner * math.sin(math.radians(ai)), + ) + ) + body = " ".join(f"{x:.1f},{y:.1f}" for x, y in pts) + return f'' + + + def sparkle_cluster(cx: float, cy: float, r: float) -> str: + """Starlette cue: a large four-point sparkle with a small companion + (a "little star" — starlette).""" + big = _sparkle4(cx - 0.174 * r, cy + 0.130 * r, r * 0.82, GOLD) + small = _sparkle4(cx + 0.565 * r, cy - 0.522 * r, r * 0.36, GOLD) + return big + small + ``` + +- [ ] **Step 4: Add the manifest entry** + + In `brand/build/projects.py`, inside `MANIFEST`, add the Starlette line immediately after `modern-di-litestar`: + + ```python + "modern-di-litestar": lambda: sym.star_disc(_CX, _CY, R), + "modern-di-starlette": lambda: sym.sparkle_cluster(_CX, _CY, R), + "modern-di-faststream": lambda: sym.faststream(_CX, _CY, R), + ``` + +- [ ] **Step 5: Run the tests to verify they pass** + + Run: `uv run pytest tests/test_projects.py -q` + Expected: PASS — manifest now equals `EXPECTED_REPOS`; the new repo's mark parses as valid SVG and uses only allowed colours. + +- [ ] **Step 6: Commit** + + ```bash + git add tests/test_projects.py brand/build/symbols.py brand/build/projects.py + git commit -m "feat(brand): add sparkle-cluster mark for modern-di-starlette" + ``` + +--- + +### Task 2: Regenerate and commit the brand assets + +**Files:** +- Create: `brand/projects/modern-di-starlette/{mark.svg,mark-512.png,mark-1024.png,lockup-light.svg,lockup-dark.svg,lockup.png}` + +Regenerate the kit so the integration repo's README image links resolve. + +- [ ] **Step 1: Regenerate** + + Run: `just sync-assets` + (Runs `uv run python -m brand.build.render`, then copies the org subset into `docs/assets/`.) + +- [ ] **Step 2: Verify only the new files changed** + + Run: `git status --short` + Expected: six new untracked files under `brand/projects/modern-di-starlette/` and nothing else. The render is deterministic, so existing project/org/docs assets must show no diff. If any other file changed, STOP and investigate (likely a tool/font version drift) before committing — do not blanket-commit unexpected diffs. + +- [ ] **Step 3: Visually inspect the mark and lockups** + + Confirm the four-point sparkle cluster renders inside the green/gold frame, and that light/dark lockups carry the `modern-di-starlette` name: + + ```bash + ls -1 brand/projects/modern-di-starlette/ + open brand/projects/modern-di-starlette/mark.svg brand/projects/modern-di-starlette/lockup-light.svg brand/projects/modern-di-starlette/lockup-dark.svg + ``` + Expected: `lockup-dark.svg lockup-light.svg lockup.png mark-1024.png mark-512.png mark.svg`; the dark lockup uses the darker gold (`#f0b528`). + +- [ ] **Step 4: Commit** + + ```bash + git add brand/projects/modern-di-starlette + git commit -m "chore(brand): generate modern-di-starlette assets" + ``` + +--- + +### Task 3: List Starlette on the profile README and docs site + +**Files:** +- Modify: `profile/README.md` (Dependency injection table) +- Modify: `docs/index.md` (DI list + stack sentence) + +Add the integration to both public catalogs, using the canonical one-liner. + +- [ ] **Step 1: Add the profile README table row** + + In `profile/README.md`, in the `### Dependency injection` table, insert this row immediately after the `modern-di-litestar` row (mirrors the sibling row exactly, name swapped): + + ```markdown + | [`modern-di-starlette`](https://github.com/modern-python/modern-di-starlette) | modern-di integration for Starlette | [![Stars](https://img.shields.io/github/stars/modern-python/modern-di-starlette)](https://github.com/modern-python/modern-di-starlette/stargazers) [![PyPI](https://img.shields.io/pypi/v/modern-di-starlette)](https://pypi.org/project/modern-di-starlette/) [![Downloads](https://static.pepy.tech/badge/modern-di-starlette/month)](https://pepy.tech/projects/modern-di-starlette) [![Context7](https://img.shields.io/badge/Context7-docs-blue)](https://context7.com/modern-python/modern-di-starlette) | + ``` + +- [ ] **Step 2: Add the docs DI list bullet** + + In `docs/index.md`, in the `## Dependency injection { #di }` list, insert this bullet immediately after the `modern-di-litestar` line: + + ```markdown + - [`modern-di-starlette`](https://github.com/modern-python/modern-di-starlette) — `modern-di` integration for Starlette. + ``` + +- [ ] **Step 3: Extend the stack sentence** + + In `docs/index.md`, in the `## The stack` section, update the `modern-di` bullet. Replace: + + ``` + dependency injection with one wiring shared across FastAPI, Litestar, + FastStream, and Typer. + ``` + + with: + + ``` + dependency injection with one wiring shared across FastAPI, Litestar, + Starlette, FastStream, and Typer. + ``` + +- [ ] **Step 4: Verify surfaces and run the full suite** + + ```bash + grep -n "modern-di-starlette" profile/README.md docs/index.md + uv run just check-planning + uv run just test + ``` + Expected: the slug `modern-di-starlette` appears once in `profile/README.md` and once in `docs/index.md` (the DI bullet). The stack sentence names "Starlette" (not the slug), so it does not add a grep hit. `check-planning` prints `planning: OK`; the pytest suite is green. + +- [ ] **Step 5: Commit** + + ```bash + git add profile/README.md docs/index.md + git commit -m "docs: list modern-di-starlette on profile README and docs site" + ``` + +--- + +### Task 4: Align the GitHub repo settings + +**Files:** none (out-of-repo; GitHub settings via `gh`). No commit. + +Bring `modern-python/modern-di-starlette` description, website, and topics in line with org conventions. Apply only the diff from current state. + +- [ ] **Step 1: Read current settings** + + ```bash + gh repo view modern-python/modern-di-starlette --json description,homepageUrl,repositoryTopics + ``` + Note which of the three fields already match the intended values below; skip any that already match. + +- [ ] **Step 2: Set description and website** + + ```bash + gh repo edit modern-python/modern-di-starlette \ + --description "modern-di integration for Starlette" \ + --homepage "https://modern-di.modern-python.org" + ``` + +- [ ] **Step 3: Set topics** + + Intended set (integration base set + framework): + `python, dependency-injection, di, ioc-container, modern-di, starlette`. + + ```bash + gh repo edit modern-python/modern-di-starlette \ + --add-topic python \ + --add-topic dependency-injection \ + --add-topic di \ + --add-topic ioc-container \ + --add-topic modern-di \ + --add-topic starlette + ``` + If Step 1 showed stray topics not in the intended set, remove each with `--remove-topic `. + +- [ ] **Step 4: Verify** + + ```bash + gh repo view modern-python/modern-di-starlette --json description,homepageUrl,repositoryTopics + ``` + Expected: description `modern-di integration for Starlette`, homepage `https://modern-di.modern-python.org`, topics exactly the six above. + +--- + +### Task 5: Open the PR + +**Files:** none. + +- [ ] **Step 1: Push the branch** + + ```bash + git push -u origin starlette-brand + ``` + +- [ ] **Step 2: Open the PR** + + ```bash + gh pr create --fill --title "Onboard modern-di-starlette: brand mark + surfaces" + ``` + +- [ ] **Step 3: Watch CI** + + ```bash + gh pr checks --watch + ``` + Expected: all checks green. (If a stale `refs/pull//merge` lint failure appears that doesn't reproduce locally at branch HEAD, push a fresh commit to force GitHub to recompute the merge ref — see CLAUDE.md "CI gotcha".) diff --git a/profile/README.md b/profile/README.md index d09f0f5..ff9d5e4 100644 --- a/profile/README.md +++ b/profile/README.md @@ -26,6 +26,7 @@ Open-source templates and libraries for building production-ready Python applica | [`modern-di`](https://github.com/modern-python/modern-di) | Powerful DI framework with scopes | [![Stars](https://img.shields.io/github/stars/modern-python/modern-di)](https://github.com/modern-python/modern-di/stargazers) [![PyPI](https://img.shields.io/pypi/v/modern-di)](https://pypi.org/project/modern-di/) [![Downloads](https://static.pepy.tech/badge/modern-di/month)](https://pepy.tech/projects/modern-di) [![Context7](https://img.shields.io/badge/Context7-docs-blue)](https://context7.com/modern-python/modern-di) | | [`modern-di-fastapi`](https://github.com/modern-python/modern-di-fastapi) | modern-di integration for FastAPI | [![Stars](https://img.shields.io/github/stars/modern-python/modern-di-fastapi)](https://github.com/modern-python/modern-di-fastapi/stargazers) [![PyPI](https://img.shields.io/pypi/v/modern-di-fastapi)](https://pypi.org/project/modern-di-fastapi/) [![Downloads](https://static.pepy.tech/badge/modern-di-fastapi/month)](https://pepy.tech/projects/modern-di-fastapi) [![Context7](https://img.shields.io/badge/Context7-docs-blue)](https://context7.com/modern-python/modern-di-fastapi) | | [`modern-di-litestar`](https://github.com/modern-python/modern-di-litestar) | modern-di integration for Litestar | [![Stars](https://img.shields.io/github/stars/modern-python/modern-di-litestar)](https://github.com/modern-python/modern-di-litestar/stargazers) [![PyPI](https://img.shields.io/pypi/v/modern-di-litestar)](https://pypi.org/project/modern-di-litestar/) [![Downloads](https://static.pepy.tech/badge/modern-di-litestar/month)](https://pepy.tech/projects/modern-di-litestar) [![Context7](https://img.shields.io/badge/Context7-docs-blue)](https://context7.com/modern-python/modern-di-litestar) | +| [`modern-di-starlette`](https://github.com/modern-python/modern-di-starlette) | modern-di integration for Starlette | [![Stars](https://img.shields.io/github/stars/modern-python/modern-di-starlette)](https://github.com/modern-python/modern-di-starlette/stargazers) [![PyPI](https://img.shields.io/pypi/v/modern-di-starlette)](https://pypi.org/project/modern-di-starlette/) [![Downloads](https://static.pepy.tech/badge/modern-di-starlette/month)](https://pepy.tech/projects/modern-di-starlette) [![Context7](https://img.shields.io/badge/Context7-docs-blue)](https://context7.com/modern-python/modern-di-starlette) | | [`modern-di-faststream`](https://github.com/modern-python/modern-di-faststream) | modern-di integration for FastStream | [![Stars](https://img.shields.io/github/stars/modern-python/modern-di-faststream)](https://github.com/modern-python/modern-di-faststream/stargazers) [![PyPI](https://img.shields.io/pypi/v/modern-di-faststream)](https://pypi.org/project/modern-di-faststream/) [![Downloads](https://static.pepy.tech/badge/modern-di-faststream/month)](https://pepy.tech/projects/modern-di-faststream) [![Context7](https://img.shields.io/badge/Context7-docs-blue)](https://context7.com/modern-python/modern-di-faststream) | | [`modern-di-typer`](https://github.com/modern-python/modern-di-typer) | modern-di integration for Typer | [![Stars](https://img.shields.io/github/stars/modern-python/modern-di-typer)](https://github.com/modern-python/modern-di-typer/stargazers) [![PyPI](https://img.shields.io/pypi/v/modern-di-typer)](https://pypi.org/project/modern-di-typer/) [![Downloads](https://static.pepy.tech/badge/modern-di-typer/month)](https://pepy.tech/projects/modern-di-typer) [![Context7](https://img.shields.io/badge/Context7-docs-blue)](https://context7.com/modern-python/modern-di-typer) | | [`modern-di-pytest`](https://github.com/modern-python/modern-di-pytest) | modern-di integration for pytest | [![Stars](https://img.shields.io/github/stars/modern-python/modern-di-pytest)](https://github.com/modern-python/modern-di-pytest/stargazers) [![PyPI](https://img.shields.io/pypi/v/modern-di-pytest)](https://pypi.org/project/modern-di-pytest/) [![Downloads](https://static.pepy.tech/badge/modern-di-pytest/month)](https://pepy.tech/projects/modern-di-pytest) [![Context7](https://img.shields.io/badge/Context7-docs-blue)](https://context7.com/modern-python/modern-di-pytest) | diff --git a/tests/test_projects.py b/tests/test_projects.py index 5651627..3a9f0d2 100644 --- a/tests/test_projects.py +++ b/tests/test_projects.py @@ -20,6 +20,7 @@ def test_project_frame_parses_and_uses_tokens() -> None: "that-depends", "modern-di-fastapi", "modern-di-litestar", + "modern-di-starlette", "modern-di-faststream", "modern-di-typer", "modern-di-pytest",