# lauren

> A metadata-first, high-performance Python web framework for building production ASGI services. Inspired by Rust's Axum, NestJS, and FastAPI. Every route, DI binding, module boundary, lifecycle hook, WebSocket gateway, and exception handler is declared with decorators and resolved into an immutable execution graph at startup — the request path is pure traversal.

Targets Python 3.11+. Zero runtime magic: what you declare is what you get.

**Hard dependency:** `anyio>=4.0` only. Pydantic, msgspec, and all binary wheels are optional.

```bash
pip install lauren                  # anyio only
pip install "lauren[pydantic]"      # + pydantic>=2.0
pip install "lauren[msgspec]"       # + msgspec>=0.18
pip install "lauren[full]"          # pydantic + msgspec
```

## Core pillars

- **Radix-tree router** with O(depth) lookup, static > param > wildcard priority, per-method dispatch with `Allow` header on 405.
- **Dependency Injection** with `SINGLETON`, `REQUEST`, and `TRANSIENT` scopes, Protocol binding, multi-bindings (`list[T]` injection), circular detection, scope-violation checks, the four NestJS-style custom-provider recipes (`use_value`, `use_class`, `use_factory`, `use_existing`) plus `Token` and `Inject`, and **generator function providers** (FastAPI-style lifecycle: code before `yield` = setup, yielded value = dependency, code after `yield` = teardown — scoped to `SINGLETON` or `REQUEST`).
- **Extractor system** for typed request decomposition (`Path[int]`, `Query[str]`, `Header[str]`, `Cookie[str]`, `Json[Model]`, `Form[Model]`, `Bytes`, `ByteStream`, `UploadFile`, `State`, `Depends[T]`), model extraction for Pydantic / `msgspec.Struct` / Python `dataclass` / `TypedDict` types, **pydantic-free discriminated unions** via `Discriminated[A | B, "key"]`, plus user-defined extractors via an `ExtractionMarker.extract` instance method (legacy classmethod form still supported).
- **Module system** with explicit imports/exports and circular-import detection (NestJS-style).
- **Lifecycle hooks** (`@post_construct`, `@pre_destruct`) run in topological order with timeouts. Both sync and async hooks are supported; sync hooks run in a thread pool so they never block the event loop and are subject to the same timeout enforcement as async ones.
- **First-class WebSockets** — `@ws_controller(path)`, `@on_connect`, `@on_message("event")`, `@on_disconnect`, `@on_error`, with typed validated frames, discriminated-union dispatch (`Discriminated[A | B, "key"]`), `BroadcastGroup` rooms, and native `@use_guards` / `@use_interceptors` support via `lauren.reflect` (guards run before `@on_connect` with `WsConnectionContext` that duck-types with `ExecutionContext`).
- **Server-Sent Events** — `EventStream`, `ServerSentEvent`, `last_event_id`. Per the HTML living standard. Optional keep-alive heartbeats.
- **Typed streaming** — `StreamingResponse[T]` content-negotiates between SSE / NDJSON / JSON Lines from the `Accept` header, including non-Pydantic item types.
- **Response shaping** — `Response.file(...)`, `Response.xml(...)`, immutable `with_*` builders, and custom `Response` subclasses that pass through dispatch unchanged while preserving their concrete type.
- **Socket.IO compatibility** — Engine.IO v4 / Socket.IO v5 adapter with `@socketio_controller` and `@on_socketio_event`.
- **ASGI runtime** with `LaurenFactory.create()` seven-phase startup, lifespan protocol, graceful drain.
- **Sync handler support**: plain `def` handlers are automatically offloaded to a thread pool via `anyio.to_thread.run_sync`; they never block the event loop. All extractors, return-type coercion, and binding styles (instance / classmethod / static) work identically for sync and async handlers.
- **OpenAPI 3.1** generation from Pydantic models, dataclasses, TypedDict, `msgspec.Struct`, and `Discriminated` unions (`oneOf` + `discriminator.mapping`) — complete schemas without pydantic installed; Swagger UI / ReDoc-ready.
- **Middleware (onion)**, **Guards**, and **Exception Handlers** — all attachable globally, per-controller, or per-route. Guards work on both HTTP controllers and WebSocket gateways; the same guard class is accepted for both via duck-typing.
- **`@propagate_metadata(source)`** — copy `@use_guards`, `@use_interceptors`, `@use_middlewares`, `@use_encoder`, `@use_exception_handlers`, and `@set_metadata` annotations from a source class or function to a target. Like `functools.wraps` for Lauren metadata. List-based entries are prepended (source runs outermost); encoder is copied only when target has none; user metadata is merged with target keys winning.
- **`lauren.reflect`** — metadata introspection API. Static class readers: `reflect_controller`, `reflect_module`, `reflect_injectable`, `reflect_ws_controller`, `reflect_routes`, `reflect_ws_messages`, `reflect_exception_handlers`, `get_controller_metadata`, `get_module_metadata`, `reflect_user_metadata`, `reflect_encoder`. App-level readers (post-startup): `get_all_routes`, `get_all_ws_gateways`, `get_route_metadata`. WS cross-cutting: `WsConnectionContext`, `WsUpgradeRequest`, `reflect_guards`, `reflect_interceptors`, `reflect_middlewares`, `reflect_all`, `apply_guards`, `apply_interceptors`. Result types: `ReflectedRoute`, `ReflectedWsMessage`, `ReflectedController`, `ReflectedModule`, `ReflectedWsGateway`. All class readers read from `cls.__dict__` only (no inheritance).
- **Interceptors** — `@interceptor()` wraps handler execution for timing, caching, or response transforms. `CallHandler.handle()` always returns a `Response`.
- **Pipes** — `Pipe` and `pipe()` for parameter-level transforms and validation before handler execution.
- **Auto-serialization**: return dicts, Pydantic models, dataclasses, `msgspec.Struct` values, custom `Response` subclasses, or `(body, status)` tuples and lauren builds the Response for you.
- **Pluggable JSON encoders** — `StdlibJSONEncoder` (default), `OrjsonEncoder`, `MsgspecEncoder`, `PydanticEncoder`. Set app-wide via `LaurenFactory.create(json_encoder=…)`; override per-controller or per-route with `@use_encoder(enc)`. The encoder is threaded through every output path: handler responses, error bodies, SSE events, and WebSocket `send_json`.
- **Strict inheritance**: subclasses are controllers / injectables / WS gateways / exception handlers / middlewares only when explicitly re-decorated.
- **Structured logging** with NestJS-style `ConsoleLogger` (coloured) and `JsonLogger` (production); all four built-in loggers are `@injectable(provides=(Logger,))` — pass one in `global_providers=[ConsoleLogger]` and any service can declare `log: Logger` to receive it.
- **Graceful shutdown hooks** via `app.on_shutdown(fn)` plus `install_signal_handlers` for SIGTERM/SIGINT-driven drain.
- **Signals & lifecycle events** — `SignalBus` with `StartupBegin`, `StartupComplete`, `ShutdownBegin`, `RequestReceived`, `RequestComplete`, `BackgroundTaskStarted`, `BackgroundTaskComplete`, `BackgroundTaskFailed`.
- **Sub-application mounting** — `app.mount("/prefix", sub_app)` for composing multiple Lauren or ASGI apps.
- **Static file serving** — `StaticFilesModule.for_root("/assets", directory="./public")` as a feature module.
- **Background tasks** — `BackgroundTasks` for fire-and-forget work after response, with `TaskHandle` for cancellation and result tracking.
- **28-class error catalog** with stable `code` strings; consistent `{error: {code, message, detail}}` envelope.
- **Pydantic-optional core** — `lauren/_validation.py` centralises all struct-type detection (`is_pydantic_model`, `is_msgspec_struct`, `is_dataclass`, `is_typeddict`) and the `validate_as(tp, data, *, field)` dispatcher. Every validation failure raises `ExtractorError`; callers never see library-specific exception types.

## LaurenFactory.create() parameters

`LaurenFactory.create(root_module, *, strict_lifecycle=True, global_middlewares=None, global_guards=None, global_interceptors=None, global_ws_guards=None, global_ws_interceptors=None, global_exception_handlers=None, global_providers=None, max_body_size=1048576, app_state=None, logger=None, openapi_info=None, openapi_servers=None, openapi_security_schemes=None, openapi_url=None, docs_url=None, redoc_url=None, arena=None, arena_capacity=None, json_encoder=None, signals=None, error_format="default", root_path="", mounts=None)`

See [full reference](llms-full.txt) for complete parameter documentation.

## Companion packages

- **lauren-middlewares** — CORS, rate limit, GZip, security headers, request id, trusted hosts, request log, HTTPS redirect, body size limit, timeout. Each as a factory function returning a `@middleware`-decorated class.
- **lauren-logging** — Configurable logging module modelled after NestJS's `LoggerModule.forRoot`. Processor pipeline, contextvars binding, request-logging middleware, pluggable backends (stdlib, structlog, console, file, fan-out, queue, …).
- **lauren-guards** — Authentication and authorization guards: `bearer_token`, `jwt_bearer`, `api_key`, `basic_auth`, `oauth2_introspection`, `session_cookie`, `require_authenticated`, `require_roles`, `require_scopes`, `csrf`, `ip_allowlist`.

## Documentation for LLMs

- [Full reference](llms-full.txt): complete API surface, all decorators, extractors, response factories, types, errors, and examples — including WebSocket gateways, SSE, structured streaming, custom providers, custom responses, and exception handlers.

## Examples

- [Quickstart](#quickstart): routing + DI + extractors in 20 lines.
- [WebSocket gateways](#websockets): `@ws_controller`, `@on_message`, broadcast.
- [Server-Sent Events](#sse): `EventStream` with keep-alive and resumability.
- [Typed streaming](#streaming): `StreamingResponse[T]` with content negotiation.
- [Custom providers](#custom-providers): `use_value` / `use_class` / `use_factory` / `use_existing`.
- [Custom extractors](#custom-extractors): creating domain extractors like `CurrentUser`.
- [Custom responses](#response): subclassing `Response`, file downloads, XML output.
- [Guards & middleware](#guards-middleware): applying authorization at class or route level.
- [Exception handlers](#exception-handlers): typed error → response mapping.
- [Interceptors](#interceptors): timing, caching, response transforms via `CallHandler`.
- [Pipes](#pipes): parameter-level transforms and validation.
- [Sub-application mounting](#mounting): composing multiple ASGI apps.
- [Background tasks](#background-tasks): fire-and-forget work after response.
