Deprecations (the road to v9)¶
Responder 8.1 starts the deprecation clock for a handful of legacy behaviors
that will change or be removed in Responder 9.0. Each emits a
DeprecationWarning (with a migration hint) when the deprecated path runs
— and only then; normal usage stays silent. Behavior is unchanged until
9.0: the warnings exist so you can migrate on your own schedule.
Run your test suite with warnings surfaced to find any usages:
python -W error::DeprecationWarning -m pytest
api.session() — use api.requests¶
The legacy test-client accessor api.session() is deprecated and will be
removed in 9.0. Use the requests property instead:
# Deprecated
r = api.session().get("http://;/hello")
# Preferred
r = api.requests.get("http://;/hello")
If you relied on session(base_url=...) for a custom base URL, construct
the client directly:
from starlette.testclient import TestClient
client = TestClient(api, base_url="http://testserver")
PORT overriding an explicit port=¶
Today, api.run() / api.serve() let the PORT environment variable
silently override an explicitly passed port= argument. Starting with 9.0,
an explicit port= will take precedence over the environment. A warning is
emitted only when both are set and disagree; setting just one (or both to
the same value) stays silent:
# PORT=9000 in the environment:
api.run(port=8000) # DeprecationWarning; binds 9000 today, 8000 in v9
api.run(port=9000) # no warning
api.run() # no warning; binds 9000
To keep the current behavior explicitly, resolve the environment yourself:
api.run(port=int(os.environ.get("PORT", 8000)))
Bare add_route() static fallback¶
Calling api.add_route(route) with no endpoint currently registers an
implicit default route that serves static/index.html for every
unmatched request. This implicit behavior will be removed in 9.0. Pass an
endpoint explicitly instead:
# Deprecated: implicit static fallback
api.add_route("/")
# Preferred: an explicit catch-all view
async def spa(req, resp):
resp.html = (pathlib.Path("static") / "index.html").read_text()
api.add_route("/", spa, default=True)
Static assets are unaffected — the static_dir / static_route mount
keeps working as-is.
Lossy Decimal-to-float JSON serialization¶
Assigning a bare decimal.Decimal to resp.media currently
serializes it as a JSON float, silently losing precision. Responder 9.0 will
serialize Decimal values as strings instead. A warning is emitted once
per process the first time a Decimal reaches the built-in encoder.
Choose a representation explicitly to be forward-compatible:
resp.media = {"price": str(total)} # exact, matches v9's default
resp.media = {"price": float(total)} # current behavior, silenced
or keep floats everywhere with a custom encoder (a user encoder= handles
Decimal before the built-in fallback, so no warning is emitted):
def encoder(obj):
if isinstance(obj, decimal.Decimal):
return float(obj)
raise TypeError # fall back to the built-in conversions
api = responder.API(encoder=encoder)
GraphQL: 400 with partial data¶
The GraphQL extension currently returns HTTP 400 whenever the execution
result contains errors — even when data is non-null (a partial result,
e.g. one resolver failed while others succeeded). Per the
GraphQL-over-HTTP specification, such well-formed responses
should be served with 200; Responder 9.0 will do so. A warning is emitted
once per process when a partial-data 400 is served.
Requests that produce no data (validation or request errors) keep their
400 in 9.0 as well — only the partial-data case changes. To be
forward-compatible, inspect the errors key of the response body instead
of relying on the status code:
result = client.post("/graph", json={"query": query}).json()
if result.get("errors"):
... # handle errors, regardless of HTTP status