Initial extracted documentation set
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
# Source Notes
|
||||
|
||||
This directory stores the reusable evidence behind the convention docs.
|
||||
|
||||
## What belongs here
|
||||
|
||||
One note per upstream repo, with:
|
||||
- why the repo was chosen
|
||||
- repeated conventions, not just isolated examples
|
||||
- caveats and counterexamples
|
||||
- exact `file:line` anchors
|
||||
- pattern candidates supported by the evidence
|
||||
|
||||
## Current notes
|
||||
|
||||
- `fastapi.md`
|
||||
- `starlette.md`
|
||||
- `pydantic.md`
|
||||
- `httpx.md`
|
||||
- `fastapi-users.md`
|
||||
- `full-stack-fastapi-template.md`
|
||||
|
||||
## Quality bar
|
||||
|
||||
A source note is good when it makes later synthesis cheap:
|
||||
- repeated patterns are clearly separated from one-off examples
|
||||
- framework-owned caveats are preserved
|
||||
- citations are fast to verify
|
||||
- vague claims are trimmed away
|
||||
|
||||
Read `../PROCESS.md` for the full repeatable workflow.
|
||||
@@ -0,0 +1,63 @@
|
||||
# FastAPI Users source notes
|
||||
|
||||
Repo: `fastapi-users/fastapi-users`
|
||||
Local checkout: `/home/ubuntu/repos/rodin-sources/fastapi-users`
|
||||
|
||||
## Why this repo was chosen
|
||||
- This repo shows how a substantial FastAPI ecosystem package composes auth/user flows as routers plus dependency factories, rather than bespoke checks inside each endpoint.
|
||||
- It is especially useful for identifying patterns around router composition, auth dependencies, and the behavior differences between strict and optional auth gates.
|
||||
|
||||
## Repeated patterns
|
||||
|
||||
### 1) Feature slices are delivered as routers and assembled centrally
|
||||
- `examples/beanie-oauth/app/app.py:28-57` builds one `FastAPI(lifespan=...)` app and includes router factories for JWT auth, registration, password reset, verification, users, and OAuth.
|
||||
- `tests/test_fastapi_users.py:24-42` repeats the same assembly pattern in tests, including multiple auth-related routers plus a users router.
|
||||
|
||||
Why chosen:
|
||||
- The example app and the test app use the same composition model, which makes this a repeated package convention rather than demo-only structure.
|
||||
|
||||
Implication for synthesis:
|
||||
- Strong evidence for composing auth/account features as routers, not implementing login/register/verify flows endpoint by endpoint.
|
||||
|
||||
### 2) Auth is expressed as dependency factories with explicit policy flags
|
||||
- `examples/beanie-oauth/app/app.py:60-62` protects a route with `Depends(current_active_user)`.
|
||||
- `tests/test_fastapi_users.py:44-114` shows the broader pattern: `fastapi_users.current_user(...)` generates dependencies for current, active, verified, superuser, and optional variants.
|
||||
- `fastapi_users/router/oauth.py:235-237` derives `get_current_active_user` from `authenticator.current_user(active=True, verified=requires_verification)` and then injects it into the OAuth association route at `fastapi_users/router/oauth.py:257-262`.
|
||||
|
||||
Why chosen:
|
||||
- This is stronger than a single protected-route example: policy is parameterized and reused as dependency wiring.
|
||||
|
||||
Implication for synthesis:
|
||||
- Recommend expressing auth requirements in dependency declarations (`active=True`, `verified=True`, `superuser=True`, `optional=True`) rather than hidden role checks inside handlers.
|
||||
|
||||
### 3) Optional auth dependencies deliberately change failure behavior
|
||||
- `tests/test_fastapi_users.py:156-203` shows non-optional `current_user` / `current_user(active=True)` endpoints returning `401` for missing or invalid tokens.
|
||||
- `tests/test_fastapi_users.py:320-412` shows optional variants returning `200` with `null` when the token is missing, invalid, or does not satisfy the extra policy.
|
||||
- `tests/test_fastapi_users.py:219-315` also distinguishes authorization outcomes: verified/superuser constraints return `403` when a valid user lacks the required property.
|
||||
|
||||
Why chosen:
|
||||
- This is exactly the kind of subtle behavior shift that future synthesis could easily flatten incorrectly.
|
||||
|
||||
Caveat / counterexample:
|
||||
- `optional=True` is not just "same dependency, but maybe absent." It changes endpoint semantics from auth failure to nullable user context.
|
||||
|
||||
Implication for synthesis:
|
||||
- If optional auth appears in synthesized guidance, call out the semantic shift explicitly and avoid presenting it as a drop-in default.
|
||||
|
||||
### 4) Lifespan owns startup resources here too
|
||||
- `examples/beanie-oauth/app/app.py:17-28` initializes Beanie in an `@asynccontextmanager` lifespan and passes it to `FastAPI(lifespan=...)`.
|
||||
|
||||
Why chosen:
|
||||
- Confirms that ecosystem packages/examples align with the Starlette/FastAPI lifespan pattern rather than using import-time initialization.
|
||||
|
||||
## Strong citation candidates
|
||||
- Router-factory composition in app assembly: `examples/beanie-oauth/app/app.py:28-57`
|
||||
- Dependency-factory auth policies: `tests/test_fastapi_users.py:44-114`
|
||||
- Optional auth returns `200` + `null` instead of `401/403`: `tests/test_fastapi_users.py:320-412`
|
||||
- OAuth route derives auth dependency from policy flags: `fastapi_users/router/oauth.py:235-262`
|
||||
|
||||
## Pattern candidates supported by this repo
|
||||
- compose auth/account feature areas as routers
|
||||
- declare auth policy in dependencies rather than inline endpoint checks
|
||||
- distinguish required auth from optional nullable-user contexts
|
||||
- initialize backing resources in lifespan
|
||||
@@ -0,0 +1,76 @@
|
||||
# FastAPI source notes
|
||||
|
||||
Repo: `fastapi/fastapi`
|
||||
Local checkout: `/home/ubuntu/repos/rodin-sources/fastapi`
|
||||
|
||||
## Why this repo was chosen
|
||||
- Core framework source plus first-party docs/tests. It is the best place to separate actual repeated FastAPI conventions from tutorial folklore.
|
||||
- Especially useful here because the same patterns appear in docs examples, framework internals, and tests: router composition, dependency injection, semantic exceptions, and dependency-driven test seams.
|
||||
|
||||
## Repeated patterns
|
||||
|
||||
### 1) App assembly is declarative: app-level and router-level configuration carry shared concerns
|
||||
- `docs_src/bigger_applications/app_an_py310/main.py:7-18` creates the app with a global dependency, then includes routers with per-router prefix/tags/dependencies/responses.
|
||||
- `docs_src/bigger_applications/app_an_py310/routers/items.py:5-10` shows the same concern split one level down: the router owns prefix/tags/dependencies/common responses.
|
||||
- `fastapi/applications.py:330-348` documents that `FastAPI(..., dependencies=[...])` applies those dependencies to every path operation, including sub-routers.
|
||||
|
||||
Why chosen:
|
||||
- This is not a one-off tutorial style; the public constructor explicitly supports the same pattern the docs teach.
|
||||
|
||||
Implication for synthesis:
|
||||
- Favor centralized app/router configuration for auth headers, tags, common responses, and prefixes.
|
||||
- Do not describe thin handlers as a style preference only; the framework API is built to move cross-cutting concerns out of handlers.
|
||||
|
||||
### 2) Dependencies are first-class request wiring, including teardown-aware resources
|
||||
- `docs_src/bigger_applications/app_an_py310/dependencies.py:6-13` keeps request validation in dedicated dependency callables that raise HTTP errors directly.
|
||||
- `fastapi/routing.py:95-136` wraps request handling in nested `AsyncExitStack`s specifically so dependency setup/teardown, including `yield` dependencies, participates in request lifecycle.
|
||||
- `tests/test_dependency_security_overrides.py:24-29` defines a route entirely in terms of `Security(...)` and `Depends(...)` collaborators.
|
||||
|
||||
Why chosen:
|
||||
- This combines public examples with internal lifecycle machinery. Dependencies are not sugar over helper calls; they are a core execution model.
|
||||
|
||||
Caveat / counterexample:
|
||||
- `fastapi/routing.py:124-130` raises a `FastAPIError` if application code swallows an exception in a `yield` dependency and fails to re-raise. So "just hide cleanup in a dependency" is incomplete; dependency cleanup must preserve exception flow.
|
||||
|
||||
Implication for synthesis:
|
||||
- Prefer dependencies for auth, validated request context, DB/session access, and request-scoped resources.
|
||||
- Mention that `yield` dependencies are appropriate when setup/teardown must be coupled to a request.
|
||||
|
||||
### 3) Handlers and dependencies raise semantic HTTP exceptions; framework handlers translate them
|
||||
- `docs_src/bigger_applications/app_an_py310/routers/items.py:21-25` raises `HTTPException(404, ...)` when an item is missing.
|
||||
- `docs_src/bigger_applications/app_an_py310/routers/items.py:28-38` adds operation-specific response metadata and raises `HTTPException(403, ...)` for forbidden updates.
|
||||
- `fastapi/exception_handlers.py:11-26` turns `HTTPException` and `RequestValidationError` into HTTP responses, including a 422 JSON body for validation failures.
|
||||
- `fastapi/exception_handlers.py:13-17` also shows a subtle rule: statuses that must not include a body return a bare `Response`, not JSON.
|
||||
|
||||
Why chosen:
|
||||
- The docs examples and framework exception handlers line up exactly: route code signals semantics, framework code owns response formatting.
|
||||
|
||||
Implication for synthesis:
|
||||
- Recommend raising semantic exceptions from route/dependency code instead of hand-building error JSON in each handler.
|
||||
- Note that response-body behavior is status-sensitive and partly framework-owned.
|
||||
|
||||
### 4) Preferred test seam: override dependencies, not handler internals
|
||||
- `tests/test_dependency_security_overrides.py:24-29` keeps the route signature explicit about collaborators.
|
||||
- `tests/test_dependency_security_overrides.py:44-63` replaces both ordinary dependencies and security dependencies through `app.dependency_overrides`, then resets overrides after each test.
|
||||
|
||||
Why chosen:
|
||||
- This is a repo-level testing pattern, not an incidental example.
|
||||
|
||||
Caveat / counterexample:
|
||||
- The reset step is part of the pattern, not cleanup fluff: `tests/test_dependency_security_overrides.py:52-63` clears `app.dependency_overrides = {}` after each override-based test.
|
||||
|
||||
Implication for synthesis:
|
||||
- When describing testing conventions, emphasize overrideable dependency seams as the intended unit of substitution.
|
||||
|
||||
## Strong citation candidates
|
||||
- Global dependencies apply across sub-routers: `fastapi/applications.py:330-348`
|
||||
- Dependency lifecycle is request-lifecycle machinery, not style guidance: `fastapi/routing.py:95-136`
|
||||
- `yield` dependency exception-swallowing failure mode: `fastapi/routing.py:124-130`
|
||||
- Central exception translation, including no-body statuses: `fastapi/exception_handlers.py:11-17`
|
||||
|
||||
## Pattern candidates supported by this repo
|
||||
- centralize cross-cutting concerns in app/router configuration
|
||||
- use dependencies as the main request-wiring mechanism
|
||||
- use `yield` dependencies for request-scoped resources with teardown
|
||||
- raise semantic `HTTPException`s from handlers/dependencies and let framework handlers shape responses
|
||||
- test by overriding dependencies and resetting overrides explicitly
|
||||
@@ -0,0 +1,72 @@
|
||||
# Full-stack FastAPI Template source notes
|
||||
|
||||
Repo: `fastapi/full-stack-fastapi-template`
|
||||
Local checkout: `/home/ubuntu/repos/rodin-sources/full-stack-fastapi-template`
|
||||
|
||||
## Why this repo was chosen
|
||||
- This template is a production-oriented FastAPI service skeleton, so it is useful for spotting conventions that survive outside toy examples: central assembly, typed dependency aliases, explicit schema boundaries, and visible test resource lifetimes.
|
||||
- It is also valuable because it includes a few pragmatic exceptions that should not be over-generalized.
|
||||
|
||||
## Repeated patterns
|
||||
|
||||
### 1) Route modules are grouped by feature and assembled centrally
|
||||
- `backend/app/api/main.py:6-14` creates a single `api_router`, includes feature routers (`login`, `users`, `utils`, `items`), and conditionally includes a `private` router only in local environments.
|
||||
- `backend/app/main.py:17-33` creates the app, configures OpenAPI identity generation and CORS middleware, then mounts the API router under the versioned prefix.
|
||||
|
||||
Why chosen:
|
||||
- This is the top-level assembly pattern for the template, not a side example.
|
||||
|
||||
Caveat / counterexample:
|
||||
- `backend/app/api/main.py:13-14` shows an environment-specific router include. Good synthesis should treat this as a deliberate local-only escape hatch, not a reason to scatter router registration across arbitrary modules.
|
||||
|
||||
Implication for synthesis:
|
||||
- Favor one central API router and one central app assembly module.
|
||||
- Mention that environment-specific routes can still be expressed centrally.
|
||||
|
||||
### 2) Dependencies own session/auth wiring, and typed aliases keep route signatures concise
|
||||
- `backend/app/api/deps.py:21-27` defines `get_db()` and publishes `SessionDep` / `TokenDep` as `Annotated[..., Depends(...)]` aliases.
|
||||
- `backend/app/api/deps.py:30-57` decodes the token, validates it with `TokenPayload`, loads the user, and raises semantic HTTP errors for invalid credentials, missing users, inactive users, and insufficient privileges.
|
||||
- `backend/app/api/routes/items.py:13-16`, `48-49`, `61-64`, `75-82`, and `99-102` repeatedly inject `SessionDep` and `CurrentUser` directly in handler signatures.
|
||||
|
||||
Why chosen:
|
||||
- The dependency alias pattern is repeated across route handlers, not used once.
|
||||
|
||||
Implication for synthesis:
|
||||
- Strong evidence for typed dependency aliases when the same collaborators appear across many handlers.
|
||||
- Shared auth/session logic belongs in dependencies, not repeated inline in each route.
|
||||
|
||||
### 3) Schema transitions stay explicit at route boundaries
|
||||
- `backend/app/api/routes/items.py:44-45` converts ORM/domain objects to `ItemPublic` before constructing the collection response.
|
||||
- `backend/app/api/routes/items.py:61-72` accepts `ItemCreate`, validates it into an `Item` with `owner_id` injected, persists it, and returns the created object.
|
||||
- `backend/app/api/routes/items.py:91-96` uses `item_in.model_dump(exclude_unset=True)` before applying a partial update.
|
||||
|
||||
Why chosen:
|
||||
- The route code repeatedly marks transitions between input schema, persistence model, and output schema.
|
||||
|
||||
Caveat / counterexample:
|
||||
- `backend/app/api/routes/items.py:48-58`, `75-96`, and `99-113` still return ORM objects directly in some single-item handlers while relying on `response_model` to shape output. So the template mixes explicit conversion with response-model-driven shaping; future synthesis should describe that nuance instead of claiming one exclusive pattern.
|
||||
|
||||
Implication for synthesis:
|
||||
- Prefer explicit schema boundaries, especially for collections and patch-style updates.
|
||||
- When handlers return domain objects directly, note that `response_model` is still doing boundary work.
|
||||
|
||||
### 4) Tests keep DB lifetime and cleanup visible
|
||||
- `backend/tests/conftest.py:15-24` creates a session fixture, initializes the DB once, yields the session, then deletes `Item` and `User` rows and commits cleanup.
|
||||
- `backend/tests/conftest.py:27-30` keeps the test client lifecycle explicit with a context-managed `TestClient` fixture.
|
||||
|
||||
Why chosen:
|
||||
- This is the template's default testing setup, so it is strong evidence for visible resource lifetime management.
|
||||
|
||||
## Strong citation candidates
|
||||
- Central API router assembly with local-only conditional include: `backend/app/api/main.py:6-14`
|
||||
- Typed dependency aliases: `backend/app/api/deps.py:21-27`
|
||||
- Auth dependency handles token decode + user lookup + semantic errors: `backend/app/api/deps.py:30-57`
|
||||
- Explicit patch update boundary via `model_dump(exclude_unset=True)`: `backend/app/api/routes/items.py:91-96`
|
||||
- Test DB lifetime and cleanup: `backend/tests/conftest.py:15-24`
|
||||
|
||||
## Pattern candidates supported by this repo
|
||||
- centralize app and API router assembly
|
||||
- use typed dependency aliases for repeated collaborators
|
||||
- keep auth/session logic in dependencies
|
||||
- keep request/update/response schema transitions explicit
|
||||
- keep test resource lifetime and cleanup visible
|
||||
@@ -0,0 +1,48 @@
|
||||
# HTTPX source notes for FastAPI conventions
|
||||
|
||||
Repo: `encode/httpx`
|
||||
Local checkout: `/home/ubuntu/repos/rodin-sources/httpx`
|
||||
|
||||
## Why this repo was chosen
|
||||
- FastAPI services often depend on HTTPX both for in-process ASGI testing and for outbound HTTP seams. HTTPX source clarifies where those seams actually live.
|
||||
- Useful here because the transport layer gives stronger evidence than generic client examples.
|
||||
|
||||
## Repeated patterns
|
||||
|
||||
### 1) HTTPX supports testing an ASGI app across the HTTP boundary without leaving process
|
||||
- `httpx/_transports/asgi.py:63-83` defines `ASGITransport` as a transport that sends requests directly to an ASGI app, with explicit knobs for `raise_app_exceptions`, `root_path`, and client address.
|
||||
- `httpx/_transports/asgi.py:105-119` shows the transport building a real ASGI scope from the outgoing request, including method, headers, path, query string, server, client, and `root_path`.
|
||||
|
||||
Why chosen:
|
||||
- This is a concrete framework-aligned seam for FastAPI/Starlette apps; it preserves HTTP/ASGI behavior better than calling route functions directly.
|
||||
|
||||
Caveat / counterexample:
|
||||
- `httpx/_transports/asgi.py:79-81` explicitly says app exceptions are raised by default. If you want to inspect a 500 response body instead of crashing the test, you must set `raise_app_exceptions=False`.
|
||||
|
||||
Implication for synthesis:
|
||||
- Prefer in-process ASGI transport/client tests when you want realistic request/response behavior without network overhead.
|
||||
- Mention the `raise_app_exceptions=False` toggle when describing 500-response testing.
|
||||
|
||||
### 2) HTTPX makes outbound substitution a transport concern, not a patching concern
|
||||
- `httpx/_transports/mock.py:15-17` defines `MockTransport` from a request handler.
|
||||
- `httpx/_transports/mock.py:19-43` routes fully built requests through that handler for both sync and async clients, reading the request body first and requiring/awaiting a real `Response`.
|
||||
|
||||
Why chosen:
|
||||
- This is a stronger seam than monkeypatching nested `client.get(...)` call sites; the substitution happens at the client transport boundary.
|
||||
|
||||
Caveat / counterexample:
|
||||
- `httpx/_transports/mock.py:25-26` raises a `TypeError` if a sync client is given an async handler. The sync/async client mode still matters even when mocking.
|
||||
|
||||
Implication for synthesis:
|
||||
- Recommend transport substitution for HTTP integrations instead of monkeypatching internal helper functions or third-party SDK calls.
|
||||
|
||||
## Strong citation candidates
|
||||
- ASGI transport is the in-process app test seam: `httpx/_transports/asgi.py:63-83`
|
||||
- ASGI scope construction proves tests still exercise HTTP/ASGI boundaries: `httpx/_transports/asgi.py:105-119`
|
||||
- 500-response testing requires `raise_app_exceptions=False`: `httpx/_transports/asgi.py:79-81`
|
||||
- Mock transport is request/response substitution at the client boundary: `httpx/_transports/mock.py:15-43`
|
||||
|
||||
## Pattern candidates supported by this repo
|
||||
- prefer in-process ASGI transport tests for HTTP behavior
|
||||
- use transport-level substitution for outbound HTTP dependencies
|
||||
- keep sync/async client mode aligned with the transport handler you provide
|
||||
@@ -0,0 +1,65 @@
|
||||
# Pydantic source notes for FastAPI conventions
|
||||
|
||||
Repo: `pydantic/pydantic`
|
||||
Local checkout: `/home/ubuntu/repos/rodin-sources/pydantic`
|
||||
|
||||
## Why this repo was chosen
|
||||
- FastAPI's request/response model layer is built on Pydantic. This repo is the right source for boundary-shaping rules: coercion, validation timing, explicit serialization, and validator failure modes.
|
||||
- Important here because Pydantic's docs include both recommended patterns and dangerous counterexamples.
|
||||
|
||||
## Repeated patterns
|
||||
|
||||
### 1) Pydantic models are designed for transport boundaries: parse external input, then serialize explicitly
|
||||
- `docs/index.md:61-89` shows raw external input being coerced into a typed `BaseModel`, including datetime parsing, bytes-to-string coercion, and string-to-int coercion.
|
||||
- `docs/index.md:82-107` uses `model_dump()` as the explicit serialization boundary.
|
||||
- `docs/index.md:109-152` shows invalid external data producing a structured `ValidationError` with per-field errors.
|
||||
|
||||
Why chosen:
|
||||
- This is the front-door example in the docs and directly matches how FastAPI uses models for requests and responses.
|
||||
|
||||
Caveat / counterexample:
|
||||
- `docs/index.md:93-104` makes clear that default validation is not strict-only; coercion is normal behavior. Any synthesized guidance that assumes "Pydantic means no coercion" would be wrong without an explicit strictness choice.
|
||||
|
||||
Implication for synthesis:
|
||||
- Recommend models at the API boundary, but mention that Pydantic may coerce inputs unless strict behavior is configured.
|
||||
- Prefer explicit `model_dump()`/response-model boundaries over ad hoc dict shaping deep in business logic.
|
||||
|
||||
### 2) Use field validators to keep domain-specific checks close to fields, and choose validator timing deliberately
|
||||
- `docs/concepts/validators.md:38-45` defines the contract for field validators: they receive a value and must return the validated value.
|
||||
- `docs/concepts/validators.md:46-80` shows after-validators as the type-safer default because they run after Pydantic's internal validation.
|
||||
- `docs/concepts/validators.md:160-209` shows before-validators receiving raw input, making them useful for coercion/preprocessing before core parsing.
|
||||
|
||||
Why chosen:
|
||||
- These are not isolated APIs; the docs frame validator timing as a central design choice.
|
||||
|
||||
Caveat / counterexample:
|
||||
- `docs/concepts/validators.md:160-206` warns that before-validators must handle arbitrary raw input and should avoid unsafe mutation when later raising errors, especially with unions.
|
||||
|
||||
Implication for synthesis:
|
||||
- Prefer after-validators for invariant checks on already-parsed values.
|
||||
- Use before-validators only when the input truly needs normalization before parsing.
|
||||
|
||||
### 3) Not every validator mode is safe for API-boundary schemas
|
||||
- `docs/concepts/validators.md:254-311` shows plain validators short-circuiting internal validation entirely; the example accepts `number='invalid'` even though the field is annotated as `int`.
|
||||
- `docs/concepts/validators.md:313-320` explains that wrap validators can also bypass normal validation flow if they return early or skip the handler.
|
||||
|
||||
Why chosen:
|
||||
- These are exactly the kinds of footguns that can quietly weaken FastAPI request validation if used casually.
|
||||
|
||||
Implication for synthesis:
|
||||
- Avoid recommending plain/wrap validators as the default pattern for request models.
|
||||
- If they appear in synthesized guidance, frame them as advanced escape hatches with validation-bypass risk.
|
||||
|
||||
## Strong citation candidates
|
||||
- External data coercion + explicit serialization boundary: `docs/index.md:61-107`
|
||||
- Validation errors are structured and field-specific: `docs/index.md:109-152`
|
||||
- Validator contract and return-value requirement: `docs/concepts/validators.md:38-45`
|
||||
- After-vs-before validator tradeoff: `docs/concepts/validators.md:46-80`, `docs/concepts/validators.md:160-209`
|
||||
- Plain validators can accept invalid typed data: `docs/concepts/validators.md:254-311`
|
||||
|
||||
## Pattern candidates supported by this repo
|
||||
- use Pydantic models at transport boundaries
|
||||
- keep serialization explicit with `model_dump()` or response-model shaping
|
||||
- prefer after-validators for parsed-value invariants
|
||||
- use before-validators sparingly for raw-input normalization
|
||||
- treat plain/wrap validators as advanced tools, not defaults for API schemas
|
||||
@@ -0,0 +1,64 @@
|
||||
# Starlette source notes
|
||||
|
||||
Repo: `encode/starlette`
|
||||
Local checkout: `/home/ubuntu/repos/rodin-sources/starlette`
|
||||
|
||||
## Why this repo was chosen
|
||||
- FastAPI inherits key runtime behavior from Starlette. Starlette is the right source for conventions around app assembly, middleware ordering, lifespan, and request state.
|
||||
- Valuable here because it distinguishes framework guarantees from habits that only appear in sample apps.
|
||||
|
||||
## Repeated patterns
|
||||
|
||||
### 1) App construction keeps routing, middleware, exception handlers, and lifespan as separate inputs
|
||||
- `starlette/applications.py:22-55` defines the constructor with distinct parameters for routes, middleware, exception handlers, and lifespan.
|
||||
- `tests/test_applications.py:139-157` builds one app by explicitly passing routes, exception handlers, middleware, and lifespan together.
|
||||
|
||||
Why chosen:
|
||||
- This separation exists both in the public constructor and in the main integration-style test app.
|
||||
|
||||
Implication for synthesis:
|
||||
- Treat app assembly concerns as separate layers; do not blur middleware, routing, error handling, and startup resources into one module-level setup blob.
|
||||
|
||||
### 2) Middleware ordering is intentional and framework-enforced
|
||||
- `starlette/applications.py:35-40` documents that `ServerErrorMiddleware` is outermost and `ExceptionMiddleware` innermost.
|
||||
- `starlette/applications.py:68-77` shows the actual stack construction: framework error middleware, then user middleware, then framework exception middleware around the router.
|
||||
- `starlette/applications.py:98-101` forbids adding middleware after the application has started.
|
||||
|
||||
Why chosen:
|
||||
- This is stronger than a docs recommendation; it is constructor/runtime behavior.
|
||||
|
||||
Caveat / counterexample:
|
||||
- User middleware is not the whole stack. If a synthesized convention says "middleware runs in the order you add it," that is incomplete because Starlette wraps user middleware with framework-owned layers.
|
||||
|
||||
Implication for synthesis:
|
||||
- When discussing middleware order, mention Starlette's outer/inner framework layers explicitly.
|
||||
- Avoid recommendations that rely on mutating middleware after startup.
|
||||
|
||||
### 3) Lifespan is the preferred resource boundary, and `asynccontextmanager` is the clearest shape
|
||||
- `starlette/applications.py:46-48` says lifespan replaces `on_startup`/`on_shutdown`; use one style or the other, not both.
|
||||
- `tests/test_applications.py:394-409` shows the preferred shape: an `@asynccontextmanager` lifespan marks startup before `yield` and cleanup after `yield`, with both verified by entering/exiting the client context.
|
||||
- `tests/test_applications.py:108-114` defines lifespan state by yielding `{"count": 1}`.
|
||||
- `tests/test_applications.py:241-244` verifies request handlers can consume that state via `request.state`.
|
||||
|
||||
Why chosen:
|
||||
- This combines constructor guidance with working tests for startup/cleanup and state propagation.
|
||||
|
||||
Caveat / counterexample:
|
||||
- `tests/test_applications.py:421-459` still supports async/sync generator lifespan forms, but those tests are under a deprecation-warning filter. For future-facing synthesis, prefer `@asynccontextmanager`, not raw generator lifespans.
|
||||
|
||||
Implication for synthesis:
|
||||
- Recommend lifespan for DB pools, clients, caches, and other app resources.
|
||||
- Note that lifespan can yield structured state for handlers instead of relying on import-time globals.
|
||||
|
||||
## Strong citation candidates
|
||||
- Constructor-level separation of app assembly concerns: `starlette/applications.py:22-55`
|
||||
- Middleware stack ordering is framework-defined: `starlette/applications.py:68-77`
|
||||
- Cannot add middleware after startup: `starlette/applications.py:98-101`
|
||||
- Lifespan replaces startup/shutdown hooks: `starlette/applications.py:46-48`
|
||||
- Preferred `@asynccontextmanager` lifespan behavior: `tests/test_applications.py:394-409`
|
||||
|
||||
## Pattern candidates supported by this repo
|
||||
- separate routes, middleware, exception handlers, and lifespan at app assembly time
|
||||
- preserve middleware ordering intentionally, including framework-owned outer/inner layers
|
||||
- prefer lifespan over startup/shutdown hooks for resource management
|
||||
- use lifespan-yielded state instead of import-time globals for app-scoped data
|
||||
Reference in New Issue
Block a user