Add convenience for annotated handlers (#2225)
This commit is contained in:
parent
f32ef20b74
commit
2e5c288fea
|
@ -1,5 +1,9 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from functools import lru_cache
|
||||
from inspect import signature
|
||||
from typing import Any, Dict, Iterable, List, Optional, Tuple, Union
|
||||
from uuid import UUID
|
||||
|
||||
from sanic_routing import BaseRouter # type: ignore
|
||||
from sanic_routing.exceptions import NoMethod # type: ignore
|
||||
|
@ -106,6 +110,8 @@ class Router(BaseRouter):
|
|||
version = str(version).strip("/").lstrip("v")
|
||||
uri = "/".join([f"{version_prefix}{version}", uri.lstrip("/")])
|
||||
|
||||
uri = self._normalize(uri, handler)
|
||||
|
||||
params = dict(
|
||||
path=uri,
|
||||
handler=handler,
|
||||
|
@ -187,3 +193,24 @@ class Router(BaseRouter):
|
|||
raise SanicException(
|
||||
f"Invalid route: {route}. Parameter names cannot use '__'."
|
||||
)
|
||||
|
||||
def _normalize(self, uri: str, handler: RouteHandler) -> str:
|
||||
if "<" not in uri:
|
||||
return uri
|
||||
|
||||
sig = signature(handler)
|
||||
mapping = {
|
||||
param.name: param.annotation.__name__.lower()
|
||||
for param in sig.parameters.values()
|
||||
if param.annotation in (str, int, float, UUID)
|
||||
}
|
||||
|
||||
reconstruction = []
|
||||
for part in uri.split("/"):
|
||||
if part.startswith("<") and ":" not in part:
|
||||
name = part[1:-1]
|
||||
annotation = mapping.get(name)
|
||||
if annotation:
|
||||
part = f"<{name}:{annotation}>"
|
||||
reconstruction.append(part)
|
||||
return "/".join(reconstruction)
|
||||
|
|
39
tests/test_handler_annotations.py
Normal file
39
tests/test_handler_annotations.py
Normal file
|
@ -0,0 +1,39 @@
|
|||
from uuid import UUID
|
||||
|
||||
import pytest
|
||||
|
||||
from sanic import json
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"idx,path,expectation",
|
||||
(
|
||||
(0, "/abc", "str"),
|
||||
(1, "/123", "int"),
|
||||
(2, "/123.5", "float"),
|
||||
(3, "/8af729fe-2b94-4a95-a168-c07068568429", "UUID"),
|
||||
),
|
||||
)
|
||||
def test_annotated_handlers(app, idx, path, expectation):
|
||||
def build_response(num, foo):
|
||||
return json({"num": num, "type": type(foo).__name__})
|
||||
|
||||
@app.get("/<foo>")
|
||||
def handler0(_, foo: str):
|
||||
return build_response(0, foo)
|
||||
|
||||
@app.get("/<foo>")
|
||||
def handler1(_, foo: int):
|
||||
return build_response(1, foo)
|
||||
|
||||
@app.get("/<foo>")
|
||||
def handler2(_, foo: float):
|
||||
return build_response(2, foo)
|
||||
|
||||
@app.get("/<foo>")
|
||||
def handler3(_, foo: UUID):
|
||||
return build_response(3, foo)
|
||||
|
||||
_, response = app.test_client.get(path)
|
||||
assert response.json["num"] == idx
|
||||
assert response.json["type"] == expectation
|
Loading…
Reference in New Issue
Block a user