diff --git a/docs/routing.md b/docs/routing.md index 3489880a4..a08463d12 100644 --- a/docs/routing.md +++ b/docs/routing.md @@ -43,6 +43,7 @@ You can use convertors to modify what is captured. Four convertors are available * `str` returns a string, and is the default. * `int` returns a Python integer. * `float` returns a Python float. +* `uuid` return a Python `uuid.UUID` instance. * `path` returns the rest of the path, including any additional `/` characers. Convertors are used by prefixing them with a colon, like so: diff --git a/starlette/convertors.py b/starlette/convertors.py index acbc03a3c..7afe4c8d1 100644 --- a/starlette/convertors.py +++ b/starlette/convertors.py @@ -1,5 +1,6 @@ import math import typing +import uuid class Convertor: @@ -61,9 +62,20 @@ def to_string(self, value: typing.Any) -> str: return ("%0.20f" % value).rstrip("0").rstrip(".") +class UUIDConvertor(Convertor): + regex = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" + + def convert(self, value: str) -> typing.Any: + return uuid.UUID(value) + + def to_string(self, value: typing.Any) -> str: + return str(value) + + CONVERTOR_TYPES = { "str": StringConvertor(), "path": PathConvertor(), "int": IntegerConvertor(), "float": FloatConvertor(), + "uuid": UUIDConvertor(), } diff --git a/tests/test_routing.py b/tests/test_routing.py index e3de089b9..2a7e6c244 100644 --- a/tests/test_routing.py +++ b/tests/test_routing.py @@ -1,3 +1,5 @@ +import uuid + import pytest from starlette.applications import Starlette @@ -75,6 +77,12 @@ def path_convertor(request): return JSONResponse({"path": path}) +@app.route("/uuid/{param:uuid}", name="uuid-convertor") +def uuid_converter(request): + uuid_param = request.path_params["param"] + return JSONResponse({"uuid": str(uuid_param)}) + + @app.websocket_route("/ws") async def websocket_endpoint(session): await session.accept() @@ -152,6 +160,17 @@ def test_route_converters(): app.url_path_for("path-convertor", param="some/example") == "/path/some/example" ) + # Test UUID conversion + response = client.get("/uuid/ec38df32-ceda-4cfa-9b4a-1aeb94ad551a") + assert response.status_code == 200 + assert response.json() == {"uuid": "ec38df32-ceda-4cfa-9b4a-1aeb94ad551a"} + assert ( + app.url_path_for( + "uuid-convertor", param=uuid.UUID("ec38df32-ceda-4cfa-9b4a-1aeb94ad551a") + ) + == "/uuid/ec38df32-ceda-4cfa-9b4a-1aeb94ad551a" + ) + def test_url_path_for(): assert app.url_path_for("homepage") == "/"