mirror of
https://github.com/kennethreitz/responder.git
synced 2026-07-21 18:39:29 +00:00
ea5ba0d26b
The first 8.0 sweep. Flagship: responder.Router records route declarations without an API instance (Flask-blueprint style) and api.include_router() replays them with prefix/tags/dependencies/auth composition, nesting, and conflict guards. Correctness: lifespan= now composes with on_event (background draining runs again), view-style default routes dispatch instead of 500ing, CBV HEAD falls back to on_get, tolerant cookie parsing, case-insensitive resp.headers, async background tasks, async GraphQL resolvers, Jinja builtins survive context assignment, identity-based dependency cycle detection, WSGI mounts wrap once, WebSocket crashes send a 1011 close frame, and dependency metadata matches actual imports. Security: Content-Disposition filename escaping in resp.download(); bounded, lock-guarded MemorySessionBackend with expiry-first eviction. 8.0 behavior changes: no implicit static/ mkdir, url_for raises RouteNotFoundError and percent-encodes values, redirect() defaults to 307 (permanent=True for 308), charset= on encoded text responses (no more fake 'Encoding' header), and RFC 9512 application/yaml. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
69 lines
1.5 KiB
Python
69 lines
1.5 KiB
Python
"""v5.4: named routes for url_for reverse lookup."""
|
|
|
|
import responder
|
|
|
|
|
|
def _api():
|
|
return responder.API(allowed_hosts=[";"], secret_key="x" * 32, session_https_only=False)
|
|
|
|
|
|
def test_url_for_by_name():
|
|
api = _api()
|
|
|
|
@api.route("/users/{id}", name="user_detail")
|
|
def user_detail(req, resp, *, id):
|
|
resp.media = {"id": id}
|
|
|
|
assert api.url_for("user_detail", id=42) == "/users/42"
|
|
|
|
|
|
def test_url_for_by_function_still_works():
|
|
api = _api()
|
|
|
|
@api.route("/p/{slug}")
|
|
def page(req, resp, *, slug):
|
|
resp.text = slug
|
|
|
|
assert api.url_for(page, slug="x") == "/p/x"
|
|
assert api.url_for("page", slug="x") == "/p/x"
|
|
|
|
|
|
def test_name_disambiguates_lambdas():
|
|
api = _api()
|
|
|
|
# Two routes whose endpoints share the generic name "<lambda>".
|
|
api.add_route("/a", lambda req, resp: None, name="alpha", static=False)
|
|
api.add_route("/b", lambda req, resp: None, name="beta", static=False)
|
|
|
|
assert api.url_for("alpha") == "/a"
|
|
assert api.url_for("beta") == "/b"
|
|
|
|
|
|
def test_named_websocket_route():
|
|
api = _api()
|
|
|
|
@api.websocket_route("/ws", name="socket")
|
|
async def ws(ws):
|
|
...
|
|
|
|
assert api.url_for("socket") == "/ws"
|
|
|
|
|
|
def test_unknown_name_raises():
|
|
import pytest
|
|
|
|
from responder.routes import RouteNotFoundError
|
|
|
|
with pytest.raises(RouteNotFoundError):
|
|
_api().url_for("nope")
|
|
|
|
|
|
def test_verb_decorator_accepts_name():
|
|
api = _api()
|
|
|
|
@api.get("/g", name="g_route")
|
|
def g(req, resp):
|
|
resp.text = "g"
|
|
|
|
assert api.url_for("g_route") == "/g"
|