mirror of
https://github.com/kennethreitz/responder.git
synced 2026-06-21 15:00:57 +00:00
32581f0f84
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
24 KiB
24 KiB
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
v4.0.0 - 2026-06-12
Changed
- Breaking: Slimmed the default install from ~60 packages to ~30 by
moving heavy dependencies behind extras:
pueblo[sfa-full](which pulls ins3fs,aiobotocore,aiohttp,libarchive-c, and friends) is now thecliextra.responder runstill works out of the box for local modules and file paths (app:api,myapp/core.py); only remote targets (URLs,github://, cloud storage) needpip install 'responder[cli]'grapheneandgraphql-coreare now thegraphqlextra. Install withpip install 'responder[graphql]'to useapi.graphql()
v3.12.0 - 2026-06-12
Added
- Built-in metrics:
API(metrics_route="/metrics")serves request counts and latency histograms in Prometheus text format, zero dependencies. Labels use route patterns (/users/{id}) so cardinality stays bounded; error responses are recorded with their real status codes - Server-side sessions:
API(session_backend=...)stores session data in a backend (MemorySessionBackend,RedisSessionBackend, or any object withget/set/delete) with only an opaque ID in the cookie — enabling revocation and unbounded session size. Handler code is unchanged - Query-parameter validation:
@api.route(..., params_model=Model)coerces and validates query strings with Pydantic (422on failure), exposes the instance asreq.state.validated_params, maps repeated keys tolistfields, and documents the parameters in the OpenAPI spec resp.render(template, **context)— render a Jinja2 template as the HTML response body in one call
v3.11.0 - 2026-06-11
Added
- HTTP range requests:
resp.file()andresp.stream_file()answerRange: bytes=...with206 Partial Content(suffix and open-ended ranges,416for unsatisfiable,Accept-Rangesadvertised) — enables video seeking and resumable downloads resp.download(path, filename=...)serves files as attachments with properContent-Disposition(RFC 5987 encoding for non-ASCII names), streamed and resumable- Request timeouts:
API(request_timeout=seconds)answers overrunning handlers with504 Gateway Timeout(content-negotiated); dependency teardowns still run
Performance
- Route resolution is cached per (method, path) with invalidation on registration — ~10% faster dispatch at 81 routes, growing with route count
v3.10.0 - 2026-06-11
Added
- Trailing-slash redirects: requests that miss only by a trailing slash get
a
307to the canonical path, preserving method and query string. Disable withAPI(redirect_slashes=False) - Request size limits:
API(max_request_size=bytes)returns413for oversized bodies — fast-fails onContent-Lengthand enforces cumulatively for chunked/streamed uploads - Automatic ETags:
API(auto_etag=True)adds a content-hashETagto GET responses with full304 Not Modifiedhandling; an explicitresp.etagalways wins - After-response background tasks:
resp.background(func, *args)defers work until the client has the response (sync and async, ordered) resp.cache_control(...)helper for buildingCache-Controlheaders
Fixed
- A
413raised while reading the body duringrequest_modelvalidation is no longer swallowed into a422
Changed
- Trailing-slash redirects are on by default (previously such requests were 404s)
v3.9.1 - 2026-06-11
Added
- Conditional request support: set
resp.etagorresp.last_modifiedand matchingIf-None-Match/If-Modified-Sincerequests automatically get304 Not Modified(RFC 7232 semantics:If-None-Matchprecedence, weak comparison, GET/HEAD only) - Request body streaming:
async for chunk in req.stream()iterates large uploads without buffering - Pluggable rate-limiter backends:
RateLimiter(backend=...)with the in-memory default plus a newRedisBackendfor multi-process deployments - Application state:
api.statenamespace, reachable from handlers viareq.api.state req.apiis now populated with the owningAPIinstance (it was alwaysNonebefore)
Fixed
API(static_dir=None)crashed on every route registration — the static fallback assertion now only applies when no endpoint is given- The static-fallback error is a
ValueErrorinstead of a bareassert
Performance
- Request headers are parsed into the case-insensitive dict lazily, on first access (~5% faster dispatch on header-heavy requests that don't read headers)
v3.9.0 - 2026-06-11
Added
- Dependency injection for WebSocket handlers: declare path parameters and
registered dependencies by name after the
wsargument. Request-scoped providers taking a parameter receive the WebSocket; generator teardown runs when the handler finishes. Handlers that only takewsare unaffected - OpenAPI 3.1 support (
openapi="3.1.0") - The OpenAPI schema endpoint now serves JSON when requested via
Accept: application/json, or always whenopenapi_routeends in.json - Path parameters are documented automatically in the OpenAPI spec from
route patterns (
{id:int}→ required integer parameter) - Built-in error responses (404, 405) are content-negotiated: JSON clients
receive
{"error": ...}bodies instead of plain text
Fixed
- OpenAPI paths no longer leak convertor patterns (
/users/{id:int}is now emitted as the spec-compliant/users/{id}) - Registering a duplicate route now raises
ValueError(previously anassertthat disappears underpython -O) - Removed dead
_exception_handlersbookkeeping inAPI.exception_handler
Changed
mypynow passes with zero errors across the codebase (was 25);ruffis clean as welltypes-pyyamladded to thetestextra
v3.8.0 - 2026-06-11
Added
- Handlers can return values: a
dict/listsetsresp.media, astrsetsresp.text, andbytessetresp.content. ReturningNonekeeps the mutate-respbehavior, so existing handlers are unaffected - App-scoped dependencies:
@api.dependency(scope="app")resolves the provider once on first use and caches it for the application's lifetime; generator teardown runs at shutdown - Automatic
OPTIONSresponses with anAllowheader for method-restricted routes HEADrequests are accepted whereverGETisset_cookie()gains asamesiteparameter, defaulting to"lax"- The validated
request_modelinstance is now available to handlers asreq.state.validated— no need to re-parse the body
Changed
- Requests to an existing path with an unsupported method now return
405 Method Not Allowedwith anAllowheader (previously 404) RouteGroup.before_requesthooks are now scoped to the group's prefix (previously they silently applied to every route)
Performance
- View signature inspection for dependency injection is cached per function
v3.7.0 - 2026-06-11
Added
- Dependency injection for route handlers: register providers with
@api.dependency()(orapi.add_dependency(name, provider)) and declare them as view parameters by name. Supports sync/async functions and generators (code afteryieldruns as teardown once the response is sent). Providers accepting a parameter receive the currentRequest. Dependencies resolve at most once per request; path parameters take precedence. - Per-route rate limiting via
RateLimiter.limitdecorator - WebSocket before-request hooks can now reject connections: closing the socket in a hook short-circuits the route handler
- WebSocket before-request hooks may now be sync functions (run in the threadpool)
- Custom formats registered on
api.formatsnow actually reach request parsing and response negotiation (previously each request got a fresh default format registry)
Fixed
{value:float}path convertor matched garbage like1a5(unescaped regex dot) and crashed with a 500 — now correctly returns 404- Literal characters in route paths are now regex-escaped, so
/file.jsonno longer matches/fileXjson - Unbounded memory growth in
BackgroundQueue— completed futures are now pruned fromresults req.media("form")crashed with aTypeErrorwhen the request had noContent-Typeheader- Content negotiation returned an empty body for
Acceptheaders matching encode-incapable formats (e.g.multipart/form-data) — now falls through to JSON
Changed
Request.urlandRequest.paramsare now computed once and cached- Format registries are no longer rebuilt twice per request
v3.6.2 - 2026-04-12
Fixed
- GraphQL error responses now correctly return 400 status instead of always 200
- OpenAPI docs UI now respects custom
openapi_routeinstead of hardcoding/schema.yml before_requestsdefault type mismatch that could crash routes called outside the router- Blocking synchronous file I/O in
Response.stream_file()— now uses async I/O via anyio - Memory leak in rate limiter (empty bucket keys never cleaned up)
- Race condition in rate limiter
check()— added thread-safe locking - WSGI fallback catching all
TypeErrors instead of just call-signature mismatches - Pydantic request/response model validation crashing on non-dict bodies
- Test assertions that could never fail (
or True,< 500patterns) CaseInsensitiveDictmissing__delitem__,pop, andsetdefaultoverridesassertused for input validation in OpenAPI extension (stripped bypython -O)- Potential XSS in GraphiQL template endpoint injection
- Dead
or ""in media format detection logic
Changed
DELETErequests now participate in Pydantic request body validation- Simplified status code category check to use chained comparison
Removed
- Unused
methodparameter fromload_target() - Unused Node.js setup step from CI test workflow
v3.6.1 - 2026-04-12
Added
- Configurable GZip compression via
gzipparameter onAPI()(defaults toTrue)
v3.6.0 - 2026-03-24
Added
- Built-in structured logging with per-request context (
enable_logging=True)api.log— always-available logger, enriched with request context when logging is enabled- Automatic access logging with timing:
GET /path → 200 (1.2ms) - Request ID generation/forwarding via
X-Request-IDheader contextvars-based request context (ID, method, path, client IP) on every log recordresponder.ext.loggingmodule:get_logger(),RequestContext,RequestContextFilter
- CLAUDE.md project guide and
/releasecommand - Version number in docs sidebar
Changed
- Comprehensive documentation improvements across all pages
- Deployment: health checks, Docker Compose, Caddy, Procfile, production checklist
- API reference: usage examples for every class
- Feature tour: Pydantic validation, content negotiation, structured logging sections
- Tutorials: modernized SQLAlchemy to
mapped_column(), fixed deprecateddatetime.utcnow(), WebSocketWebSocketDisconnecthandling, role-based auth, auth strategy guide - Testing: rate limiting and WSGI mount examples
- Middleware: pure ASGI middleware example
- Quickstart: links to all tutorials
- Sandbox: full rewrite with project layout
- Docker example uses
uvinstead of pip - Backlog updated: removed implemented features, replaced HTTP/2 server push with dependency injection
Removed
uv.lock— this is a library, not an application
v3.5.0 - 2026-03-24
Added
- CI validation for Python 3.14, 3.14 free-threaded, and PyPy 3.11
- Marimo notebook mounting docs and example
- Type annotations for
routes.py
Changed
- Replaced deprecated
asyncio.iscoroutinefunctionwithinspect.iscoroutinefunctionahead of Python 3.16 removal - Narrowed broad
except Exceptionto specific exceptions in response model serialization and websocket chat example - Improved GraphQL API interface with expanded test coverage
- Code formatting cleanup via pyproject-fmt and ruff
- Dropped Python 3.9 from CI
Fixed
- WSGI mount returning 400 when requesting the exact mount root path
- Werkzeug 3.1.7 compatibility for trusted host validation in tests
future.resultbare property access in background task test (now properly callsfuture.result())- OpenAPI template packaging and static file serving
- RST title underline warning breaking docs CI
Removed
- Read the Docs configuration (docs hosted on GitHub Pages)
v3.4.0 - 2026-03-22
Changed
- Upgraded to Starlette 1.0
- Added comprehensive docstrings across the codebase
- Expanded API reference documentation
v3.3.0 - 2026-03-22
Added
- Full documentation rewrite: tutorials for REST APIs, SQLAlchemy, Flask migration
- Auth, WebSocket, middleware, and configuration guides
- Testing docs with prose, examples, and tips
- GitHub Pages deployment for docs
Changed
- Reworked homepage prose
- Rewrote CLI and API reference docs
v3.2.0 - 2026-03-22
Added
- Pydantic auto-validation:
request_modelvalidates input, returns 422 on failure - Pydantic auto-serialization:
response_modelstrips extra fields from responses - Server-Sent Events:
@resp.ssefor real-time streaming resp.stream_file()for streaming large files without loading into memory@api.after_request()hooksapi.group("/prefix")for route groups and API versioningapi.graphql("/path", schema=schema)one-liner GraphQL setupapi = responder.API(request_id=True)for automatic request ID generation- Built-in rate limiter:
RateLimiter(requests=100, period=60).install(api) - MessagePack format support:
await req.media("msgpack") req.is_json,req.path_params,req.clientpropertiesapi.exception_handler()decorator for custom error handling- Lifespan context manager support
uuidandpathroute convertors- PEP 561
py.typedmarker - Pydantic support for OpenAPI schema generation
Changed
- Dependencies flattened:
pip install respondergets everything - Core deps reduced to starlette + uvicorn
- TestClient lazy-loaded (no httpx import in production)
- Before-request hooks can short-circuit by setting status code
- Removed poethepoet task runner
Fixed
- Multipart parser losing headers when parts have multiple headers
url_for()with typed route params ({id:int})resp.bodyencoding crash on bytes content- GraphQL text query missing
await - Streaming responses not sending Content-Type headers
- Python 3.9 compatibility for union type syntax
v3.0.0 - 2026-03-22
Added
- Platform: Added support for Python 3.10 - Python 3.13
- CLI:
responder runnow also accepts a filesystem path on its<target>argument, enabling usage on single-file applications. - CLI:
responder runnow also accepts URLs.
Changed
- Platform: Minimum Python version is now 3.9 (dropped 3.6, 3.7, 3.8)
- Dependencies: Dramatically reduced core dependency count (10 → 5)
- Removed
requests,requests-toolbelt,rfc3986,whitenoise - Moved
apispecandmarshmallowtoopenapioptional extra - Replaced
rfc3986with stdliburllib.parse - Replaced
requests-toolbeltmultipart decoder withpython-multipart - Replaced deprecated
starlette.middleware.wsgiwitha2wsgi - Switched from WhiteNoise to ServeStatic
- Removed
- Dependencies: Pinned
starlette[full]>=0.40(was unpinned) - GraphQL: Upgraded to
graphene>=3andgraphql-core>=3.1(fromgraphene<3andgraphql-server-core, which is unmaintained) - GraphQL: Updated GraphiQL UI from 0.12.0 (2018) to 3.0.6 with React 18
- Extensions: All of CLI-, GraphQL-, and OpenAPI-Support modules are
extensions now, found within the
responder.extmodule namespace. - Packaging: Migrated from
setup.pyto declarativepyproject.toml
Removed
- Platform: Removed support for EOL Python 3.6, 3.7, 3.8
- Status codes: Removed deprecated
resume_incompleteandresumealiases for HTTP 308 (marked for removal in 3.0) - CLI:
responder run --buildceased to exist
Fixed
- Routing: Fixed dispatching
static_route=Noneon Windows - uvicorn:
--debugnow maps to uvicorn'slog_level = "debug" - Tests: Fixed deprecated httpx TestClient usage
v2.0.5 - 2019-12-15
Added
- Update requirements to support python 3.8
v2.0.4 - 2019-11-19
Fixed
- Fix static app resolving
v2.0.3 - 2019-09-20
Fixed
- Fix template conflicts
v2.0.2 - 2019-09-20
Fixed
- Fix template conflicts
v2.0.1 - 2019-09-20
Fixed
- Fix template import
v2.0.0 - 2019-09-19
Changed
- Refactor Router and Schema
v1.3.2 - 2019-08-15
Added
- ASGI 3 support
- CI tests for python 3.8-dev
- Now requests have
statea mapping object
Deprecated
- ASGI 2
v1.3.1 - 2019-04-28
Added
- Route params Converters
- Add search for documentation pages
Changed
- Bump dependencies
v1.3.0 - 2019-02-22
Fixed
- Versioning issue
- Multiple cookies.
- Whitenoise returns not found.
- Other bugfixes.
Added
- Stream support via
resp.stream. - Cookie directives via
resp.set_cookie. - Add
resp.htmlto send HTML. - Other improvements.
v1.1.3 - 2019-01-12
Changed
- Refactor
_route_for
Fixed
- Resolve startup/shutdwown events
v1.2.0 - 2018-12-29
Added
- Documentations
Changed
- Use Starlette's LifeSpan middleware
- Update denpendencies
Fixed
- Fix route.is_class_based
- Fix test_500
- Typos
v1.1.2 - 2018-11-11
Fixed
- Minor fixes for Open API
- Typos
v1.1.1 - 2018-10-29
Changed
- Run sync views in a threadpoolexecutor.
v1.1.0 - 2018-10-27
Added
- Support for
before_request.
v1.0.5- 2018-10-27
Fixed
- Fix sessions.
v1.0.4 - 2018-10-27
Fixed
- Potential bufix for cookies.
v1.0.3 - 2018-10-27
Fixed
- Bugfix for redirects.
v1.0.2 - 2018-10-27
Changed
- Improvement for static file hosting.
v1.0.1 - 2018-10-26
Changed
- Improve cors configuration settings.
v1.0.0 - 2018-10-26
Changed
- Move GraphQL support into a built-in plugin.
v0.3.3 - 2018-10-25
Added
- CORS support
Changed
- Improved exceptions.
v0.3.2 - 2018-10-25
Changed
- Subtle improvements.
v0.3.1 - 2018-10-24
Fixed
- Packaging fix.
v0.3.0 - 2018-10-24
Changed
- Interactive Documentation endpoint.
- Minor improvements.
v0.2.3 - 2018-10-24
Changed
- Overall improvements.
v0.2.2 - 2018-10-23
Added
- Show traceback info when background tasks raise exceptions.
v0.2.1 - 2018-10-23
Added
- api.requests.
v0.2.0 - 2018-10-22
Added
- WebSocket support.
v0.1.6 - 2018-10-20
Added
- 500 support.
v0.1.5 - 2018-10-20
Added
- File upload support
Changed
- Improvements to sequential media reading.
v0.1.4 - 2018-10-19
Fixed
- Stability.
v0.1.3 - 2018-10-18
Added
- Sessions support.
v0.1.2 - 2018-10-18
Added
- Cookies support.
v0.1.1 - 2018-10-17
Changed
- Default routes.
v0.1.0 - 2018-10-17
Added
- Prototype of static application support.
v0.0.10 - 2018-10-17
Fixed
- Bugfix for async class-based views.
v0.0.9 - 2018-10-17
Fixed
- Bugfix for async class-based views.
v0.0.8 - 2018-10-17
Added
- GraphiQL Support.
Changed
- Improvement to route selection.
v0.0.7 - 2018-10-16
Changed
- Immutable Request object.
v0.0.6 - 2018-10-16
Added
- Ability to mount WSGI apps.
- Supply content-type when serving up the schema.
v0.0.5 - 2018-10-15
Added
- OpenAPI Schema support.
- Safe load/dump yaml.
v0.0.4 - 2018-10-15
Added
- Asynchronous support for data uploads.
Fixed
- Bug fixes.
v0.0.3 - 2018-10-13
Fixed
- Bug fixes.
v0.0.2 - 2018-10-13
Changed
- Switch to ASGI/Starlette.
v0.0.1 - 2018-10-12
Added
- Conception!