diff --git a/brand/build/projects.py b/brand/build/projects.py index ccce029..3a47edb 100644 --- a/brand/build/projects.py +++ b/brand/build/projects.py @@ -21,6 +21,7 @@ "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-aiohttp": lambda: sym.async_loop(_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 e538d8e..b48db14 100644 --- a/brand/build/symbols.py +++ b/brand/build/symbols.py @@ -84,7 +84,9 @@ def sparkle_cluster(cx: float, cy: float, r: float) -> str: return big + small -def _circ_arc(cx: float, cy: float, rad: float, a0: float, a1: float, w: float) -> str: +def _circ_arc( + cx: float, cy: float, rad: float, a0: float, a1: float, w: float, color: str = GOLD +) -> 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 x0 = cx + rad * math.cos(math.radians(a0)) @@ -94,7 +96,7 @@ def _circ_arc(cx: float, cy: float, rad: float, a0: float, a1: float, w: float) large = 1 if (a1s - a0) % 360 > 180 else 0 d = ( f'' + f'fill="none" stroke="{color}" stroke-width="{w}" stroke-linecap="butt"/>' ) ex = cx + rad * math.cos(math.radians(a1)) ey = cy + rad * math.sin(math.radians(a1)) @@ -108,11 +110,22 @@ def _circ_arc(cx: float, cy: float, rad: float, a0: float, a1: float, w: float) d += ( f'' + f'{base[0] - width * px:.1f},{base[1] - width * py:.1f}" fill="{color}"/>' ) return d +def async_loop(cx: float, cy: float, r: float) -> str: + """aiohttp cue: an async event-loop cycle (two chasing arrows) knocked out + of a gold disc.""" + rad = r * 0.52 + w = 3.4 + loop = _circ_arc(cx, cy, rad, 25, 165, w, color=CREAM) + _circ_arc( + cx, cy, rad, 205, 345, w, color=CREAM + ) + return f'' + loop + + FASTSTREAM_PATH = ( "m499.61,356.87l-92.61-160.41-36.48-63.19-10.46,251.02c.07,2.86-.78,6.05-2.51,8.6" "-2.98,4.41-7.42,5.31-9.92,2.02l.02-.03-68.85-90.48-107.13,38.09v.04c-3.89,1.38-7.11" diff --git a/brand/projects/modern-di-aiohttp/lockup-dark.svg b/brand/projects/modern-di-aiohttp/lockup-dark.svg new file mode 100644 index 0000000..f3e7c88 --- /dev/null +++ b/brand/projects/modern-di-aiohttp/lockup-dark.svg @@ -0,0 +1 @@ + diff --git a/brand/projects/modern-di-aiohttp/lockup-light.svg b/brand/projects/modern-di-aiohttp/lockup-light.svg new file mode 100644 index 0000000..d7b06ba --- /dev/null +++ b/brand/projects/modern-di-aiohttp/lockup-light.svg @@ -0,0 +1 @@ + diff --git a/brand/projects/modern-di-aiohttp/lockup.png b/brand/projects/modern-di-aiohttp/lockup.png new file mode 100644 index 0000000..495d324 Binary files /dev/null and b/brand/projects/modern-di-aiohttp/lockup.png differ diff --git a/brand/projects/modern-di-aiohttp/mark-1024.png b/brand/projects/modern-di-aiohttp/mark-1024.png new file mode 100644 index 0000000..553d645 Binary files /dev/null and b/brand/projects/modern-di-aiohttp/mark-1024.png differ diff --git a/brand/projects/modern-di-aiohttp/mark-512.png b/brand/projects/modern-di-aiohttp/mark-512.png new file mode 100644 index 0000000..860ed4b Binary files /dev/null and b/brand/projects/modern-di-aiohttp/mark-512.png differ diff --git a/brand/projects/modern-di-aiohttp/mark.svg b/brand/projects/modern-di-aiohttp/mark.svg new file mode 100644 index 0000000..7dcfdbd --- /dev/null +++ b/brand/projects/modern-di-aiohttp/mark.svg @@ -0,0 +1 @@ + diff --git a/planning/changes/2026-07-02.02-aiohttp-brand/design.md b/planning/changes/2026-07-02.02-aiohttp-brand/design.md new file mode 100644 index 0000000..2d1f937 --- /dev/null +++ b/planning/changes/2026-07-02.02-aiohttp-brand/design.md @@ -0,0 +1,125 @@ +--- +summary: Mint the modern-di-aiohttp brand mark (async event-loop in a gold disc) and generate its assets; public surfaces deferred until the repo ships. +--- + +# Design: modern-di-aiohttp brand mark + +## Summary + +`modern-di-aiohttp` is a new DI integration, still in development — its GitHub +repo is not public and the package is not on PyPI yet. This change mints its +brand mark (an **async event-loop cycle knocked out of a gold disc**) and +generates the six per-project asset files in this repo (`.github`), so they are +ready when the repo goes public. The public-facing surfaces (profile README row, +docs-site bullet, GitHub repo settings) are deliberately **deferred** — they +show PyPI/Downloads badges and link to a public repo that does not exist yet. + +## Motivation + +Every mainstream web-framework integration in the org has a brand mark +(`modern-di-fastapi` bolt-in-disc, `modern-di-litestar` star-in-disc, +`modern-di-starlette` sparkle). Preparing aiohttp's mark now means the asset +URLs resolve the moment its repo publishes its README, with no scramble. + +We reviewed aiohttp's real logo (a node/network graph inside a ring). We +deliberately did **not** adapt it: the node-graph motif is already how +`modern-di` and `that-depends` read in this same lineup, so a faithful copy +would collide. The chosen mark keeps the async meaning and joins the +disc-family (FastAPI/Litestar), which is visually distinct from the DI +graph marks. + +## Non-goals + +- No changes to the (not-yet-public) `modern-di-aiohttp` repo. +- **No** profile README row, docs-site bullet, or GitHub repo settings — these + wait until the repo is public (tracked under "Deferred follow-ups" below). +- No social card: integrations are not in `DOCS_REPOS`. + +## Design + +### 1. Async-loop symbol — `brand/build/symbols.py` + +Add `async_loop(cx, cy, r)`: two cream arrows chasing each other in a circular +loop (the universal "async / event-loop" cycle), knocked out of a gold disc. +It reuses the existing `_circ_arc` arc-with-arrowhead helper. + +`_circ_arc` currently hardcodes `GOLD` for both its stroke and its arrowhead +fill. Extend its signature with a trailing optional `color: str = GOLD` and +substitute `{color}` for the two `{GOLD}` occurrences. This is backward +compatible — the only existing caller (`db_retry`) omits the argument and keeps +`GOLD`. + +```python +def _circ_arc(cx: float, cy: float, rad: float, a0: float, a1: float, w: float, + color: str = GOLD) -> str: + ... # stroke and arrowhead now use {color} instead of {GOLD} + + +def async_loop(cx: float, cy: float, r: float) -> str: + """aiohttp cue: an async event-loop cycle (two chasing arrows) knocked out + of a gold disc.""" + rad = r * 0.52 + w = 3.4 + loop = ( + _circ_arc(cx, cy, rad, 25, 165, w, color=CREAM) + + _circ_arc(cx, cy, rad, 205, 345, w, color=CREAM) + ) + return f'' + loop +``` + +`CREAM` is already imported in `symbols.py`. The mark uses only `GOLD` and +`CREAM`, both in the allowed palette, so `test_only_allowed_colours` passes. + +### 2. Manifest entry — `brand/build/projects.py` + +Add `"modern-di-aiohttp": lambda: sym.async_loop(_CX, _CY, R)` to `MANIFEST`, +in the dependency-injection block, immediately after `modern-di-starlette` +(keeps the web-framework integrations fastapi -> litestar -> starlette -> +aiohttp adjacent). + +### 3. Test — `tests/test_projects.py` + +Add `"modern-di-aiohttp"` 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. + +### 4. Regenerate assets — `just sync-assets` + +Produces `brand/projects/modern-di-aiohttp/{mark.svg, mark-512.png, +mark-1024.png, lockup-light.svg, lockup-dark.svg, lockup.png}`. Commit the +generated files. + +## Deferred follow-ups (when the repo is public) + +Ship these in a later change once `github.com/modern-python/modern-di-aiohttp` +exists and the package is on PyPI: + +- Profile README row in the Dependency injection table. +- Docs-site bullet in the DI list (+ mention in the stack sentence). +- GitHub repo settings: description `modern-di integration for aiohttp`, + homepage `https://modern-di.modern-python.org`, topics + `python, dependency-injection, di, ioc-container, modern-di, aiohttp`. + +The canonical one-liner for all of the above will be +`modern-di integration for aiohttp`. + +## Testing + +- `just test` green, in particular `tests/test_projects.py`: + `test_manifest_covers_every_repo`, `test_only_allowed_colours`, + `test_project_mark_is_valid_svg`, and the render tests. +- Visually inspect the regenerated `mark.svg` and light/dark lockups for the + aiohttp entry. +- `just sync-assets` re-run is a no-op diff apart from the six new files + (deterministic render). +- `just check-planning` passes before pushing. + +## Risk + +- **Low: async_loop reads too close to db-retry's retry arcs** (both are + arced arrows). Mitigated — db-retry is a single retry arc around a database + cylinder; async_loop is two symmetric arrows forming a closed cycle on a + plain disc. Different enough; confirmed against the rendered marks. +- **Low: extending `_circ_arc` breaks db-retry.** Mitigated — the new param is + optional and defaults to `GOLD`; the existing call is unchanged and the + palette test covers db-retry. diff --git a/tests/test_projects.py b/tests/test_projects.py index 3a9f0d2..c93ee6c 100644 --- a/tests/test_projects.py +++ b/tests/test_projects.py @@ -21,6 +21,7 @@ def test_project_frame_parses_and_uses_tokens() -> None: "modern-di-fastapi", "modern-di-litestar", "modern-di-starlette", + "modern-di-aiohttp", "modern-di-faststream", "modern-di-typer", "modern-di-pytest",