Commit Graph

1146 Commits

Author SHA1 Message Date
kennethreitz 2f5e46e233 Bump version to 3.6.1 and update changelog
Add configurable GZip compression via `gzip` parameter on API().

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
v3.6.1
2026-04-12 17:52:35 -04:00
Andreas Motl c1d789f279 CI: Use uv exclusively (#606)
## About
That's just maintenance. In this case, streamline the CI configuration
for improved ergonomics and future proofing, see GH-605.

## Details
- `activate-environment: true` of newer `astral-sh/setup-uv@v7` is the
killer feature here, which wasn't available before.
- Configuring `cache-dependency-glob: pyproject.toml` isn't needed,
because the recipe already employs an excellent default configuration
that includes this and other project metadata files.
- The adjustments in `test_cli` were needed because of a different PyPy
version installed by `setup-uv`. This will yield a report to Astral
maintainers, unrelated to this patch.
2026-03-25 19:48:54 -04:00
kennethreitz 691f6b4d5c Bump version to 3.6.0 and update changelog
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
v3.6.0
2026-03-24 16:24:03 -04:00
kennethreitz 90a082a0ac Add structured logging with per-request context (#604)
## Summary

New `enable_logging=True` parameter on `responder.API()` that provides
structured, request-scoped logging using stdlib `logging` and
`contextvars`.

### What it does

- **`api.log`** — always available on every API instance. Works as a
plain logger by default; gains per-request context enrichment with
`enable_logging=True`
- **Per-request context** — every log message automatically includes
request ID, HTTP method, path, and client IP
- **Access logging** — logs every request with timing: `GET /path → 200
(1.2ms)`
- **Request ID** — generates or forwards `X-Request-ID` headers
(supersedes `request_id=True` when both are set)
- **stdlib logging** — works with any existing handler, formatter, or
log aggregator

### Usage

```python
# api.log always works — no setup required
api = responder.API()
api.log.info("starting up")  # plain logger, no context

# With enable_logging=True, log messages get request context automatically
api = responder.API(enable_logging=True)

@api.route("/")
def index(req, resp):
    api.log.info("handling request")
    # => 2026-03-24 12:00:00 [INFO] responder.app — handling request [GET /] [req:abc123] [client:127.0.0.1]
```

For additional loggers in helper modules:

```python
from responder.ext.logging import get_logger
logger = get_logger("myapp.db")
```

### Architecture

- `responder/ext/logging.py` — self-contained module with:
- `LoggingMiddleware` — pure ASGI middleware that sets contextvars and
logs access
- `RequestContextFilter` — logging filter that injects context into
records
  - `RequestContext` — read-only access to current request metadata
  - `get_logger()` / `setup_logging()` — convenience functions
- `api.log` — always a valid logger; context-aware when
`enable_logging=True`, plain stdlib logger otherwise
- Wired into `API.__init__` via the `enable_logging` parameter

### Files

- `responder/ext/logging.py` — new module
- `responder/api.py` — added `enable_logging` parameter and `api.log`
- `tests/test_logging.py` — 9 tests
- `docs/source/tour.rst` — new Structured Logging section
- `docs/source/index.rst` — added to feature list

## Test plan
- [x] 9 logging tests pass
- [x] Full suite: 208 passed

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 16:22:57 -04:00
kennethreitz 5ee0de6458 Replace HTTP/2 server push with dependency injection in backlog
HTTP/2 server push was removed from the spec and dropped by browsers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 16:02:56 -04:00
kennethreitz 1c729c8542 Docs: second improvement pass (#603)
## Summary

- **Auth tutorial**: fix deprecated `datetime.utcnow()` →
`datetime.now(timezone.utc)`, add role-based access control section, add
auth strategy comparison guide
- **WebSocket tutorial**: use `WebSocketDisconnect` instead of bare
`Exception` in chat example, add connection lifecycle section with close
codes, add rejected connection test example
- **SQLAlchemy tutorial**: modernize from `Column()` to
`mapped_column()` / `Mapped[]` (SQLAlchemy 2.0 style with type checker
support)
- **Deployment**: use `uv` in Docker example instead of pip, fix stale
`uv.lock` reference in production checklist
- **Quickstart**: link to all tutorials in "next steps" (was only
linking to 3 of 9)
- **Sandbox**: rewrite with project layout tree, mypy command, and
pattern-matching test examples

## Test plan
- [x] `make html` builds cleanly
- [x] All 199 tests pass

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 16:00:56 -04:00
kennethreitz 536428a787 Remove Unreleased section from changelog
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 15:54:13 -04:00
kennethreitz 3d5f3c7e93 Improve documentation across the board (#602)
## Summary

- **Deployment guide**: health check endpoint, Docker Compose example,
Caddy reverse proxy config, Procfile pattern, production checklist
- **API reference**: quick usage examples for every class (API, Request,
Response, RouteGroup, BackgroundQueue, RateLimiter, status helpers)
- **Feature tour**: new Pydantic validation and content negotiation
sections, expanded MessagePack docs
- **Testing guide**: rate limiting and mounted WSGI app test examples,
Werkzeug 3.1.7 tip
- **Middleware tutorial**: pure ASGI middleware example (no
BaseHTTPMiddleware dependency)
- **CLI guide**: environment variables section (PORT, SECRET_KEY)
- **Homepage**: updated feature list with SSE, rate limiting, Pydantic,
content negotiation, route groups
- **Backlog**: removed already-implemented items, added current ideas

+362 lines of docs, no code changes.

## Test plan
- [x] `make html` builds cleanly with no warnings
- [x] All 199 tests pass

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Signed-off-by: Kenneth Reitz <me@kennethreitz.org>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Andreas Motl <andreas.motl@panodata.org>
2026-03-24 15:51:55 -04:00
kennethreitz e0cce231ea Update CLAUDE.md to reflect lock file status
Signed-off-by: Kenneth Reitz <me@kennethreitz.org>
2026-03-24 15:42:14 -04:00
Andreas Motl 44c33475b2 Remove dependency lock file uv.lock. This is a library. (#601)
## About
We think using an `uv.lock` file is only applicable for applications,
and otherwise gives wrong impressions and expectations.

## References
- https://discuss.python.org/t/the-purpose-of-a-lock-file/38756
- https://github.com/orgs/python-poetry/discussions/7847
2026-03-24 15:35:41 -04:00
kennethreitz f4a292108b Move version below tagline in docs sidebar
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 15:34:49 -04:00
kennethreitz 25ea333ad4 Prefix version with v in docs sidebar
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 15:33:12 -04:00
kennethreitz 6279835040 Show version number in docs sidebar
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 15:32:48 -04:00
kennethreitz 0e493ad8d1 Add CLAUDE.md and /release command
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 15:31:42 -04:00
kennethreitz ce3ab46d59 Bump version to 3.5.0 and update changelog
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
v3.5.0
2026-03-24 15:27:36 -04:00
kennethreitz 6f9c87d71c Fix broad exception handling and future.result() call
- Call future.result() instead of bare property access in test (#596)
- Catch (ValueError, TypeError) instead of broad Exception in
  response model serialization (#597)
- Catch WebSocketDisconnect instead of broad Exception in
  websocket chat example (#598)

Closes #596, closes #597, closes #598

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 15:25:12 -04:00
kennethreitz 29d0621d98 Replace deprecated asyncio.iscoroutinefunction with inspect.iscoroutinefunction
Closes #599

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 15:23:24 -04:00
kennethreitz 30fa2dfda7 Add uv.lock
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 15:22:06 -04:00
kennethreitz 43c803a426 Restore print statements in lifespan example
These serve as illustrative markers for users reading the example.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 15:21:21 -04:00
Andreas Motl ff6d530338 Chore: Code formatting (#594)
## About
A few cosmetic adjustments aka. code formatting.
Also validate the outcome on CI/GHA.
Feel free to improve now or later at your disposal.

## Details
The updates are based on using the most recent versions of pyproject-fmt
and ruff.
Specifically, spots marked with `noqa` might need further love, also at
your disposal.

---------

Co-authored-by: Kenneth Reitz <me@kennethreitz.org>
2026-03-24 15:21:04 -04:00
kennethreitz a375984310 Fix WSGI mount returning 400 at mount root (#600)
## Summary
- When a WSGI app (e.g. Flask) is mounted at `/prefix` and a request
hits exactly `/prefix`, the path prefix was stripped to `""` instead of
`"/"`, causing frameworks like Flask to return 400.
- One-character fix: default the stripped path to `"/"` when empty.

## Test plan
- [x] `tests/test_responder.py::test_mount_wsgi_app` now passes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 15:16:29 -04:00
Andreas Motl 46c6f440c5 Chore: Remove configuration for Read the Docs (#595)
https://responder.kennethreitz.org/ is here to stay.
This patch concludes the decision in GH-564.
2026-03-23 20:19:15 -04:00
Andreas Motl c87e8c876d CI: Validate on Python 3.14 vanilla, free-threaded, and PyPy (#593) 2026-03-23 18:28:27 -04:00
kennethreitz f86c7eed70 Fix RST title underline warning breaking docs CI
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 22:10:51 -04:00
kennethreitz 9d492a383c Add marimo notebook mounting docs and example
- Document mounting marimo ASGI apps in the feature tour
- Add examples/marimo_mount.py showing the integration
- Verified working: marimo.create_asgi_app() mounts cleanly via api.mount()

Fixes #580.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 22:09:01 -04:00
kennethreitz 77ae49aaef Improve GraphQL API interface, expand tests, drop 3.9 from CI
- Extract variables and operationName from query params and form data,
  not just JSON bodies. Fixes #571.
- Add docstrings to GraphQLView class and methods. Fixes #572.
- Add 10 new GraphQL tests: variables, operationName, mutations,
  context access, malformed queries, raw text, invalid variables
  param. Fixes #568.
- Remove Python 3.9 from CI matrix (dropped in 3.4.0).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 22:06:00 -04:00
kennethreitz 74c872ed57 Add type annotations to routes.py
Add comprehensive type hints to compile_path, BaseRoute, Route,
WebSocketRoute, and Router classes. Uses Starlette's Scope, Receive,
Send types and properly types the ASGI/WSGI union in Router.apps.
Fixes #566.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 21:47:21 -04:00
kennethreitz 724b769c9e Fix OpenAPI template packaging, add static file tests
- Include ext/openapi/docs/*.html in package_data so OpenAPI docs
  themes (swagger_ui, redoc, rapidoc, elements) ship with the wheel.
  Fixes #583.
- Add tests for static file serving and index.html fallback. Fixes #563.
- Bump version to 3.4.1.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 21:44:02 -04:00
kennethreitz 4f02016ed6 Add comprehensive docstrings, expand API reference, upgrade to Starlette 1.0
- Add docstrings to all undocumented public methods across API, Request,
  Response, Router, Route, BackgroundQueue, and related classes
- Expand api.rst with autodoc sections for RouteGroup, BackgroundQueue,
  QueryDict, and RateLimiter
- Update starlette dependency to >=1.0
- Drop Python 3.9 support (required by Starlette 1.0), minimum is now 3.10
- Bump version to 3.4.0

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
v3.4.0
2026-03-22 21:37:36 -04:00
kennethreitz 3c2b1acc19 Bump version to 3.3.0 v3.3.0 2026-03-22 13:58:18 -04:00
kennethreitz a3b49ab9fd Add auth, WebSocket, middleware, config guides. Examples and changelog.
New documentation pages:
- Authentication: API keys, JWT tokens, session auth, custom exceptions
- WebSocket Tutorial: echo server, chat room, HTML client, data formats
- Writing Middleware: hooks vs middleware, Starlette integration, ordering
- Configuration: env vars, .env files, secret keys, debug mode, production setup

Also:
- Complete working example at end of quickstart with cross-references
- Three new example files: rest_api.py, websocket_chat.py, sse_stream.py
- Changelog updated for v3.2.0

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 13:56:35 -04:00
kennethreitz bf17b02653 Add tutorials: REST API, SQLAlchemy, Flask migration. Rewrite CLI and API ref.
Three new tutorial pages:
- Building a REST API: full CRUD with Pydantic validation, from scratch
- Using SQLAlchemy: async engine, lifespan setup, CRUD with ORM
- Migrating from Flask: concept mapping, quick reference table,
  gradual migration via app mounting

Also rewritten:
- CLI docs: cleaner, more concise
- API reference: added prose descriptions for each section

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 13:34:01 -04:00
kennethreitz 9383cd0f16 Rework homepage prose for better flow
Lead with recognition, earn each paragraph, land on the welcome.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 13:09:46 -04:00
kennethreitz 226bd63ed3 Full docs pass: educational prose throughout all pages
Every section now teaches web development concepts alongside the code:
- HTTP methods, status codes, content negotiation explained
- What ASGI is and why it matters
- How cookies, sessions, CORS, and HSTS work
- When to use WebSockets vs SSE
- Why request validation matters
- How background tasks differ from task queues
- What rate limiting protects against
- What Host header injection is
- Separation of concerns in templating

People should learn about web development while reading these docs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 13:07:56 -04:00
kennethreitz b3c55f68d9 Expand testing docs with prose, examples, and tips
New sections: request validation, headers/cookies, custom error
handlers, before/after hooks, and practical tips. Every section
now explains the why, not just the how.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 13:00:29 -04:00
kennethreitz a2a2ae21ff Good intentions
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 12:58:05 -04:00
kennethreitz 84074860aa Add note about Responder's spirit as a passion project
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 12:55:36 -04:00
kennethreitz 21baa03640 CI: Add CNAME for custom domain in GitHub Pages deploy
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 12:51:14 -04:00
kennethreitz 0c552e25cb CI: Deploy docs to GitHub Pages on push to main
Uses peaceiris/actions-gh-pages to push built docs to gh-pages branch.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 12:48:05 -04:00
kennethreitz 24958bff51 v3.2.0: Request ID, rate limiting, MessagePack, docs update
New features:
- Request ID: api = responder.API(request_id=True)
- Rate limiting: RateLimiter(requests=100, period=60).install(api)
- MessagePack format: await req.media("msgpack")
- All new features documented in tour

176 tests, 95% coverage.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
v3.2.0
2026-03-22 12:45:50 -04:00
kennethreitz 364f6b67f7 Add auto-validation, SSE, stream_file, after_request, route groups
Five new features:

1. **Pydantic auto-validation** — if request_model is set, request
   bodies are validated automatically and 422 returned on failure.
   If response_model is set, resp.media is serialized through the
   model (extra fields stripped, types enforced).

2. **Server-Sent Events** — resp.sse for real-time streaming:

       @resp.sse
       async def stream():
           yield {"event": "update", "data": "hello"}

3. **resp.stream_file()** — stream large files without loading
   into memory, with automatic content-type detection.

4. **after_request hooks** — run code after every request:

       @api.after_request()
       def add_request_id(req, resp):
           resp.headers["X-Request-ID"] = str(uuid.uuid4())

5. **Route groups** — organize routes with shared prefixes:

       v1 = api.group("/v1")

       @v1.route("/users")
       def list_users(req, resp): ...

Also fix streaming responses not sending Content-Type headers.

172 tests, 95% coverage.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 12:42:48 -04:00
kennethreitz 2cab7b5af7 Document Pydantic OpenAPI support in feature tour
Three approaches: Pydantic models (recommended), YAML docstrings,
and marshmallow schemas — all work together.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 12:36:02 -04:00
kennethreitz 1bfd85b003 Add Pydantic support for OpenAPI schema generation
Define your API schemas with Pydantic models instead of (or alongside)
YAML docstrings and marshmallow:

    from pydantic import BaseModel

    class PetIn(BaseModel):
        name: str
        age: int = 0

    class PetOut(BaseModel):
        id: int
        name: str
        age: int

    @api.route("/pets", methods=["POST"],
               request_model=PetIn, response_model=PetOut)
    async def create_pet(req, resp):
        data = await req.media()
        resp.media = {"id": 1, **data}

Also works with @api.schema("Name") decorator for registering
standalone schema components.

Pydantic models, marshmallow schemas, and YAML docstrings can all
be used together in the same API.

Also: rewrite docs with more prose, restore sidebar logo and links,
add FastAPI acknowledgment, update homepage copy.

161 tests, 95% coverage.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 12:35:07 -04:00
kennethreitz 33ebc77f10 Rewrite docs from scratch, remove 14MB of bundled fonts
Complete documentation rewrite:
- Clean, code-first index page (no badges, no testimonials)
- Rewritten quickstart: request/response reference, templates, background tasks
- Rewritten feature tour: method filtering, lifespan, file serving,
  error handling, hooks, WebSockets, GraphQL, OpenAPI, CORS, sessions
- Simplified testing and deployment guides
- Stripped conf.py to essential extensions only

Removed cruft:
- 14MB of paid font files (Mercury, Operator Mono)
- Google Analytics (deprecated Universal Analytics)
- UserVoice widget
- Konami code easter egg
- Algolia DocSearch (not configured)
- Twitter widgets
- Unused Sphinx extensions (mathjax, ifconfig, coverage, doctest, opengraph)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 12:17:22 -04:00
kennethreitz 30801557a3 Bump version to 3.1.0
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
v3.1.0
2026-03-22 07:57:32 -04:00
kennethreitz 73d46e9b03 CI: Remove linkcheck from docs workflow
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 07:54:59 -04:00
kennethreitz 3d65d88ea9 Rewrite README from scratch
Clean, code-first README. No badges, no testimonials — just
show what the framework does with real examples.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 07:53:25 -04:00
kennethreitz 8f979719a0 Remove poethepoet, use direct commands in CI
Replace all poe task runner usage with direct pytest/sphinx/ruff
commands. Remove poethepoet dependency and [tool.poe.tasks] config.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 07:50:32 -04:00
kennethreitz 0cbcaf9c4f Code quality improvements and test fixes (#592)
## Summary
Comprehensive post-v3.0.0 modernization: new features, bug fixes,
dependency cleanup, docs, and test coverage.

**New features:**
- **HTTP method filtering** — `@api.route("/data", methods=["GET"])`
- **Lifespan context manager** — modern async startup/shutdown
- **`api.exception_handler()`** — custom error handling per exception
type
- **`api.graphql()`** — one-liner GraphQL setup
- **`resp.file()`** — serve files from disk with auto content-type
- **before_request short-circuit** — set status code to skip route
handler
- **`req.path_params`** / **`req.client`** / **`req.is_json`** — new
request properties
- **`uuid`** and **`path`** route convertors
- **PEP 561 `py.typed`** marker

**Bug fixes:**
- Fix multipart parser losing headers
- Fix `url_for()` with typed params (`{id:int}`)
- Fix `resp.body` encoding crash on bytes content
- Fix Python 3.9 type syntax (`from __future__ import annotations`)
- Fix broken session test and no-op file upload test
- Fix helloworld example 404 on root path

**Dependencies:**
- Flattened — `pip install responder` gets everything
- Core: just starlette + uvicorn (down from 10 deps)

**Docs & README:**
- All new features documented in tour
- Modernized README features list
- Deployment guide: Docker, cloud, uvicorn
- Removed Pipenv, extras, stale references throughout

**Tests & quality:**
- 117 tests (up from 92), 91% coverage, 0 warnings
- CaseInsensitiveDict, GraphQL edge cases, staticfiles tests
- Ruff clean, all `tmpdir` → `tmp_path`

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
v3.0.0
2026-03-22 07:44:11 -04:00
kennethreitz 3fa6f11ffa Clean up stale comments, dead test code, and flaky npm assertions
- Remove commented-out tests (route overlap, form data, file uploads)
- Remove stale TODO/FIXME comments from routes.py and api.py
- Make CLI npm error assertions case-insensitive and more flexible
  to handle different npm versions across CI environments

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 05:51:47 -04:00