Skip to content

v3.9.1

Choose a tag to compare

@kennethreitz kennethreitz released this 12 Jun 02:18
· 8 commits to main since this release

Highlights

Conditional requests (HTTP caching). Set resp.etag or resp.last_modified and responder answers matching requests with a bodyless 304 Not Modified automatically:

@api.route("/report")
def report(req, resp):
    resp.etag = compute_version_hash()
    resp.text = expensive_render()   # cached clients get a 304

Implements RFC 7232 semantics: If-None-Match precedence over If-Modified-Since, weak-ETag comparison, * matching, GET/HEAD only.

Multi-process rate limiting. RateLimiter now accepts pluggable backends — the in-memory default keeps its sliding-window behavior, and the new RedisBackend shares one budget across all workers:

limiter = RateLimiter(
    requests=100, period=60,
    backend=RedisBackend(url="redis://localhost:6379/0"),
)

Streaming uploads. async for chunk in req.stream() iterates large request bodies without buffering them in memory.

Application state. api.state is a free-form namespace reachable from handlers via req.api.state — and req.api is now actually populated with the owning API instance (it was always None before).

Fixed

  • API(static_dir=None) — a documented configuration — crashed on every route registration. The static-fallback check now only applies when no endpoint is given.

Performance

  • Request headers are parsed lazily on first access: ~5% faster dispatch on header-heavy requests that never read them.

Full changelog: https://github.com/kennethreitz/responder/blob/main/CHANGELOG.md

PyPI: https://pypi.org/project/responder/3.9.1/