From ab9e8d7cc5f84524336fc20b54fb5f1de4556050 Mon Sep 17 00:00:00 2001 From: Micheal Gendy Date: Fri, 7 Jan 2022 17:38:55 +0200 Subject: [PATCH] add support for datetime,date,time converters --- starlette/convertors.py | 33 ++++++++++++++++++++++++++++ tests/test_routing.py | 48 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) diff --git a/starlette/convertors.py b/starlette/convertors.py index 7afe4c8d1..f78b757b1 100644 --- a/starlette/convertors.py +++ b/starlette/convertors.py @@ -72,10 +72,43 @@ def to_string(self, value: typing.Any) -> str: return str(value) +class DateTimeConvertor(Convertor): + regex = "[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(.[0-9]+)?" + + def convert(self, value: str) -> typing.Any: + return str(value) + + def to_string(self, value: typing.Any) -> str: + return value.strftime("%Y-%m-%dT%H:%M:%S") + + +class DateConvertor(Convertor): + regex = "[0-9]{4}-[0-9]{2}-[0-9]{2}" + + def convert(self, value: str) -> typing.Any: + return str(value) + + def to_string(self, value: typing.Any) -> str: + return value.strftime("%Y-%m-%d") + + +class TimeConvertor(Convertor): + regex = "[0-9]{2}:[0-9]{2}:[0-9]{2}(.[0-9]+)?" + + def convert(self, value: str) -> typing.Any: + return str(value) + + def to_string(self, value: typing.Any) -> str: + return value.strftime("%H:%M:%S") + + CONVERTOR_TYPES = { "str": StringConvertor(), "path": PathConvertor(), "int": IntegerConvertor(), "float": FloatConvertor(), "uuid": UUIDConvertor(), + "datetime": DateTimeConvertor(), + "date": DateConvertor(), + "time": TimeConvertor(), } diff --git a/tests/test_routing.py b/tests/test_routing.py index dcb996531..c99c5a95a 100644 --- a/tests/test_routing.py +++ b/tests/test_routing.py @@ -1,3 +1,4 @@ +import datetime import functools import uuid @@ -120,6 +121,24 @@ def uuid_converter(request): return JSONResponse({"uuid": str(uuid_param)}) +@app.route("/datetime/{param:datetime}", name="datetime-convertor") +def datetime_convertor(request): + dt = request.path_params["param"] + return JSONResponse({"datetime": dt}) + + +@app.route("/date/{param:date}", name="date-convertor") +def date_convertor(request): + date = request.path_params["param"] + return JSONResponse({"date": date}) + + +@app.route("/time/{param:time}", name="time-convertor") +def time_convertor(request): + time = request.path_params["param"] + return JSONResponse({"time": time}) + + # Route with chars that conflict with regex meta chars @app.route("/path-with-parentheses({param:int})", name="path-with-parentheses") def path_with_parentheses(request): @@ -233,6 +252,35 @@ def test_route_converters(client): == "/uuid/ec38df32-ceda-4cfa-9b4a-1aeb94ad551a" ) + # Test datetime conversion + response = client.get("/datetime/2020-01-01T00:00:00") + assert response.status_code == 200 + assert response.json() == {"datetime": "2020-01-01T00:00:00"} + assert ( + app.url_path_for( + "datetime-convertor", param=datetime.datetime(2020, 1, 1, 0, 0, 0) + ) + == "/datetime/2020-01-01T00:00:00" + ) + + # Test date conversion + response = client.get("/date/2020-01-01") + assert response.status_code == 200 + assert response.json() == {"date": "2020-01-01"} + assert ( + app.url_path_for("date-convertor", param=datetime.date(2020, 1, 1)) + == "/date/2020-01-01" + ) + + # Test time conversion + response = client.get("/time/00:00:00") + assert response.status_code == 200 + assert response.json() == {"time": "00:00:00"} + assert ( + app.url_path_for("time-convertor", param=datetime.time(0, 0, 0)) + == "/time/00:00:00" + ) + def test_url_path_for(): assert app.url_path_for("homepage") == "/"