From fae8532011989befd4c986c01996a4a8b849ec9b Mon Sep 17 00:00:00 2001 From: nezhdanchik Date: Sun, 8 Dec 2024 13:57:19 +0300 Subject: [PATCH 01/12] path parsing --- web/router.py | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/web/router.py b/web/router.py index fcd7836..503cd35 100644 --- a/web/router.py +++ b/web/router.py @@ -1,5 +1,6 @@ from webob import Request from webob.exc import HTTPNotFound, HTTPInternalServerError +from parse import parse class Router: @@ -22,10 +23,21 @@ def __call__(self, environ, start_response): request = Request(environ) response = None - handler = self.routes.get(request.path_info) + handler, kwargs = self._find_handler(request.path) + + if kwargs and handler: + # получаем типы аргументов из сигнатуры функции + signature = handler.__annotations__ + # приводим аргументы к нужным типам + try: + kwargs = {name: signature[name](value) for name, value in kwargs.items()} + except ValueError: + ... + + if handler: try: - response = handler(request) + response = handler(request, **kwargs) except Exception: response = HTTPInternalServerError() else: @@ -35,3 +47,11 @@ def __call__(self, environ, start_response): response = HTTPNotFound() return response(environ, start_response) + + + def _find_handler(self, path): + for route, handler in self.routes.items(): + parse_result = parse(route, path) + if parse_result: + return handler, parse_result.named + return None, None From afee47b9da776dacf5d7672e0e0ac884a6ec40db Mon Sep 17 00:00:00 2001 From: nezhdanchik Date: Sun, 8 Dec 2024 14:02:46 +0300 Subject: [PATCH 02/12] path parsing --- web/router.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/web/router.py b/web/router.py index 503cd35..5e59fb8 100644 --- a/web/router.py +++ b/web/router.py @@ -1,5 +1,5 @@ from webob import Request -from webob.exc import HTTPNotFound, HTTPInternalServerError +from webob.exc import HTTPNotFound, HTTPInternalServerError, HTTPBadRequest from parse import parse @@ -32,7 +32,8 @@ def __call__(self, environ, start_response): try: kwargs = {name: signature[name](value) for name, value in kwargs.items()} except ValueError: - ... + response = HTTPBadRequest(json={"error": "invalid type arguments"}) + return response(environ, start_response) if handler: From 8ee9fa5f306d59145b4fcf2e0af6ee19cfea80f0 Mon Sep 17 00:00:00 2001 From: nezhdanchik Date: Sun, 8 Dec 2024 14:03:53 +0300 Subject: [PATCH 03/12] add parse lib to requirements.txt --- requirements.txt | Bin 361 -> 798 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/requirements.txt b/requirements.txt index 1a6d157e75cc0ce64b16998522d915c706b7a3e0..af07b768c8978712097fac14bc2fa49e76dfa0d7 100644 GIT binary patch literal 798 zcmZuv%TB{U5c3&{Pbq3bUmW-bjvP^%w5upjB`Kmm57;w#A)(c7s*cBFd-vx%L5~3) z8sy0E%JqyV+j~6X0Vyk=xDb&b@GHcThy(YFAu@KBT&irxUawsFZ-Rndr@1d8BC@`K z8*27yLdRd~k8QG8wuZ@PM2#!4VMM9EV0vR1thP6Pd5Ddnlgpu+J>E9O!7j-J!drbojMk^t!XKEN=ZFC+RciE=*Zz& zYhG+_-A`+o4opL-RlKv@(3&I;DLa^RA#dalE9FYZb<2;E&p1af;7+tpjERXjbGj!X zmz(S@1W163WN7o>jWqA5~&8ml`~DW3y%wmciIk~pD`kXF4i*Q;v9YQZ_q jqfDdK9C>KROh3r_|7|EEcjtM&6M4oP_jFg%=kNCy@o95n literal 361 zcmYjNyN<*l4D9)rMdY0={0&!bc_M@$6ao>D)AIK_WSf)Rcs!mNTc#X?U5Ps0@qwxE zr=v8QXyPM(vei0ABrtoj&Bqj{Y5tN~JHxK>J+9w-)s(7_HrP`&jzFPFqJe|y2h{tLp zI6YF1z%FBMrJ-&V6;_>caWHCmg93J6f(_)lInH_ntsj79$>0;s=z5lPzRnzi1A7_} hTd*7sw_7m8_pn?}B>MfIwy#Dld+Ft8so7_I?*l`dY Date: Sun, 8 Dec 2024 15:34:10 +0300 Subject: [PATCH 04/12] parse with method --- web/router.py | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/web/router.py b/web/router.py index 59ac8c5..35c14fd 100644 --- a/web/router.py +++ b/web/router.py @@ -5,6 +5,10 @@ from parse import parse +import logging +mylog = logging.getLogger('app') +mylog.setLevel(logging.DEBUG) + class Router: def __init__(self): self.routes = {} @@ -57,35 +61,39 @@ def not_found(self, func): def __call__(self, environ, start_response) -> Response: request = Request(environ) - method_routes = self.routes.get(request.path_info, {}) - handler = method_routes.get(request.method) + # method_routes = self.routes.get(request.path_info, {}) + # handler = method_routes.get(request.method) - handler, kwargs = self._find_handler(request.path) + handler, kwargs = self._find_handler(request) if kwargs and handler: # получаем типы аргументов из сигнатуры функции signature = handler.__annotations__ # приводим аргументы к нужным типам try: - kwargs = {name: signature[name](value) for name, value in kwargs.items()} + kwargs = {name: signature[name](value) for name, value in + kwargs.items()} except ValueError: - response = HTTPBadRequest(json={"error": "invalid type arguments"}) + response = HTTPBadRequest( + json={"error": "invalid type arguments"}) return response(environ, start_response) if handler is not None: - try: - response = handler(request, **kwargs) - except Exception: - response = HTTPInternalServerError() - else: - response = self.not_found_handler(request) + try: - return response(environ, start_response) + response = handler(request, **kwargs) + except Exception: + response = HTTPInternalServerError() + else: + response = self.not_found_handler(request) + return response(environ, start_response) - def _find_handler(self, path): - for route, handler in self.routes.items(): - parse_result = parse(route, path) + def _find_handler(self, request): + for route in self.routes: + parse_result = parse(route, request.path) + mylog.info(f'{parse_result=}') if parse_result: + handler = self.routes[route].get(request.method) return handler, parse_result.named return None, None From 2eb19a5db26a8c769fe0d3e93616b9f069c99cae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D0=B8=D0=BB=D0=B0?= <146984286+nezhdanchik@users.noreply.github.com> Date: Sun, 8 Dec 2024 15:56:11 +0300 Subject: [PATCH 05/12] Update requirements.txt --- requirements.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index cacca43..512c890 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -astroid==3.3.5 +astroid==3.3.5 coverage==7.6.9 dill==0.3.9 flake8==7.1.1 @@ -21,4 +21,3 @@ tomlkit==0.13.2 typing_extensions==4.12.2 WebOb==1.8.9 jinja2==3.1.4 - From 341d671f8ae8724a71d57e99d2c9234c362a4ba9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D0=B8=D0=BB=D0=B0?= <146984286+nezhdanchik@users.noreply.github.com> Date: Sun, 8 Dec 2024 15:56:57 +0300 Subject: [PATCH 06/12] Update router.py --- web/router.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/web/router.py b/web/router.py index 63144e0..d4e6106 100644 --- a/web/router.py +++ b/web/router.py @@ -63,9 +63,6 @@ def __call__(self, environ, start_response): request = Request(environ) response = Response() - # method_routes = self.routes.get(request.path_info, {}) - # handler = method_routes.get(request.method) - handler, kwargs = self._find_handler(request) if kwargs and handler: From a383e5cc119f6b5f72859a752ebd5af0ea263b30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D0=B8=D0=BB=D0=B0?= <146984286+nezhdanchik@users.noreply.github.com> Date: Sun, 8 Dec 2024 15:58:21 +0300 Subject: [PATCH 07/12] Update router.py --- web/router.py | 1 - 1 file changed, 1 deletion(-) diff --git a/web/router.py b/web/router.py index d4e6106..b3da88b 100644 --- a/web/router.py +++ b/web/router.py @@ -101,7 +101,6 @@ def _apply_middlewares(handler, middlewares): def _find_handler(self, request): for route in self.routes: parse_result = parse(route, request.path) - mylog.info(f'{parse_result=}') if parse_result: handler = self.routes[route].get(request.method) return handler, parse_result.named From 4177ae9392d999d91da314620d41636db028c382 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D0=B8=D0=BB=D0=B0?= <146984286+nezhdanchik@users.noreply.github.com> Date: Sun, 8 Dec 2024 16:13:09 +0300 Subject: [PATCH 08/12] Update router.py --- web/router.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/web/router.py b/web/router.py index b3da88b..2f01810 100644 --- a/web/router.py +++ b/web/router.py @@ -98,10 +98,12 @@ def _apply_middlewares(handler, middlewares): return handler + def _find_handler(self, request): - for route in self.routes: + for route, method_handlers in self.routes.items(): parse_result = parse(route, request.path) if parse_result: - handler = self.routes[route].get(request.method) - return handler, parse_result.named + handler = method_handlers.get(request.method) + if handler: + return handler, parse_result.named return None, None From 3c5ee2958bcbeaa566d1306ece5c2dcf62e1966e Mon Sep 17 00:00:00 2001 From: Khorunzhiy Ilya Date: Sun, 8 Dec 2024 16:29:18 +0300 Subject: [PATCH 09/12] add routes --- .pylintrc | 3 ++- playground/main.py | 5 +++++ requirements.txt | 1 + setup.py | 3 ++- web/router.py | 23 ++++++++++++----------- 5 files changed, 22 insertions(+), 13 deletions(-) diff --git a/.pylintrc b/.pylintrc index 489eb55..53ebb39 100644 --- a/.pylintrc +++ b/.pylintrc @@ -11,7 +11,8 @@ disable= W1203, R1732, W0621, - W0613 + W0613, + C0206 [FORMAT] diff --git a/playground/main.py b/playground/main.py index ef2e720..7e07bc5 100644 --- a/playground/main.py +++ b/playground/main.py @@ -33,6 +33,11 @@ def get_example(request): logger.info(f'got {request=}') return web.responses.JsonResponse({'message': 'hello, world!'}) +@router.get('/hello/{name}/{age}') +def get_example(request, name: str, age: int): + logger.info(f'got {request=}') + return web.responses.JsonResponse({'message': 'hello, world!', 'name': name, 'age': age}) + @router.post('/hello', middlewares=[ExampleMiddleware]) def post_example(request): diff --git a/requirements.txt b/requirements.txt index 512c890..9537ce7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,6 +10,7 @@ mccabe==0.7.0 mypy==1.13.0 mypy-extensions==1.0.0 packaging==24.2 +parse==1.20.2 platformdirs==4.3.6 pluggy==1.5.0 pycodestyle==2.12.1 diff --git a/setup.py b/setup.py index ab62ea1..7756ad5 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,8 @@ def main() -> None: install_requires=[ 'gunicorn', 'webob', - 'jinja2' + 'jinja2', + 'parse' ], ) diff --git a/web/router.py b/web/router.py index 7b8f62a..e1865ed 100644 --- a/web/router.py +++ b/web/router.py @@ -1,9 +1,10 @@ from http import HTTPMethod -from typing import Callable +from typing import Callable, Type from webob import Request, Response from webob.exc import HTTPNotFound, HTTPInternalServerError, HTTPBadRequest from parse import parse + from .middleware import Middleware @@ -26,42 +27,42 @@ def _add_route( self._routes[path][method] = self._apply_middlewares(func, middlewares) - def get(self, path: str, middlewares: list[Middleware] | None = None): + def get(self, path: str, middlewares: list[Type[Middleware]] | None = None): def decorator(func): self._add_route(HTTPMethod.GET, path, func, middlewares) return func return decorator - def post(self, path: str, middlewares: list[Middleware] | None = None): + def post(self, path: str, middlewares: list[Type[Middleware]] | None = None): def decorator(func): self._add_route(HTTPMethod.POST, path, func, middlewares) return func return decorator - def put(self, path: str, middlewares: list[Middleware] | None = None): + def put(self, path: str, middlewares: list[Type[Middleware]] | None = None): def decorator(func): self._add_route(HTTPMethod.PUT, path, func, middlewares) return func return decorator - def patch(self, path: str, middlewares: list[Middleware] | None = None): + def patch(self, path: str, middlewares: list[Type[Middleware]] | None = None): def decorator(func): self._add_route(HTTPMethod.PATCH, path, func, middlewares) return func return decorator - def delete(self, path: str, middlewares: list[Middleware] | None = None): + def delete(self, path: str, middlewares: list[Type[Middleware]] | None = None): def decorator(func): self._add_route(HTTPMethod.DELETE, path, func, middlewares) return func return decorator - def routes(self, path: str, methods: list[HTTPMethod] | None = None): + def routes(self, path: str, methods: list[Type[Middleware]] | None = None): def decorator(func: Callable[[Request], Response]): for method in (methods or []): @@ -113,12 +114,12 @@ def _apply_middlewares(handler, middlewares): return handler - def _find_handler(self, request): - for route, method_handlers in self.routes.items(): + for route in self._routes: parse_result = parse(route, request.path) if parse_result: - handler = method_handlers.get(request.method) - if handler: + handler = self._routes[route].get(request.method) + if handler is not None: return handler, parse_result.named + return None, None From 5d139075422b145c7b51311922ecfef2c309affc Mon Sep 17 00:00:00 2001 From: Khorunzhiy Ilya Date: Sun, 8 Dec 2024 16:31:15 +0300 Subject: [PATCH 10/12] add routes --- playground/main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/playground/main.py b/playground/main.py index f4050b5..46712ee 100644 --- a/playground/main.py +++ b/playground/main.py @@ -34,8 +34,9 @@ def get_example(request): logger.info(f'got {request=}') return web.responses.JsonResponse({'message': 'hello, world!'}) + @router.get('/hello/{name}/{age}') -def get_example(request, name: str, age: int): +def get_example_with_params(request, name: str, age: int): logger.info(f'got {request=}') return web.responses.JsonResponse({'message': 'hello, world!', 'name': name, 'age': age}) From 2192ba63259eeddf38bd272ee58fed0ac79a57aa Mon Sep 17 00:00:00 2001 From: Khorunzhiy Ilya Date: Sun, 8 Dec 2024 16:33:15 +0300 Subject: [PATCH 11/12] add routes --- web/router.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/router.py b/web/router.py index e1865ed..9f80247 100644 --- a/web/router.py +++ b/web/router.py @@ -62,7 +62,7 @@ def decorator(func): return decorator - def routes(self, path: str, methods: list[Type[Middleware]] | None = None): + def routes(self, path: str, methods: list[HTTPMethod] | None = None): def decorator(func: Callable[[Request], Response]): for method in (methods or []): From 1cdef3d7a34305542b52e9d9cd27fc1b5d6338c3 Mon Sep 17 00:00:00 2001 From: Khorunzhiy Ilya Date: Sun, 8 Dec 2024 17:02:07 +0300 Subject: [PATCH 12/12] fix --- playground/main.py | 2 +- web/router.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/playground/main.py b/playground/main.py index f0e11dc..ae5bfae 100644 --- a/playground/main.py +++ b/playground/main.py @@ -21,7 +21,7 @@ def after(self, _request, response): return response -router.use_middleware(ExampleMiddleware) +# router.use_middleware(ExampleMiddleware) @router.get('/main', middlewares=[ExampleMiddleware]) diff --git a/web/router.py b/web/router.py index 60ae9ca..f8d5728 100644 --- a/web/router.py +++ b/web/router.py @@ -1,3 +1,4 @@ +import logging from http import HTTPMethod from typing import Callable, Type @@ -90,6 +91,7 @@ def __call__(self, environ, start_response): if kwargs and handler: # получаем типы аргументов из сигнатуры функции + logging.info(f'{handler=}') signature = handler.__annotations__ # приводим аргументы к нужным типам try: