1+ import re
2+ import inspect
13from enum import Enum
2- from typing import Iterable , Callable
4+ from typing import Iterable , Callable , List , Type
35from socks import method
6+ from parse import parse
7+ from pyechonext .urls import URL
8+ from pyechonext .views import View
49from pyechonext .request import Request
510from pyechonext .response import Response
11+ from pyechonext .utils .exceptions import RoutePathExistsError , MethodNotAllow
12+ from pyechonext .utils import _prepare_url
613
714
815class ApplicationType (Enum ):
9- JSON = ' application/json'
10- HTML = ' text/html'
16+ JSON = " application/json"
17+ HTML = " text/html"
1118
1219
1320class EchoNext :
1421 """
1522 This class describes an EchoNext WSGI Application.
1623 """
1724
18- def __init__ (self , app_name : str , application_type : ApplicationType = ApplicationType .JSON ):
25+ __slots__ = ("app_name" , "application_type" , "urls" , "routes" )
26+
27+ def __init__ (
28+ self ,
29+ urls : List [URL ],
30+ app_name : str ,
31+ application_type : ApplicationType = ApplicationType .JSON ,
32+ ):
1933 """
2034 Constructs a new instance.
2135
@@ -25,6 +39,35 @@ def __init__(self, app_name: str, application_type: ApplicationType=ApplicationT
2539 self .app_name = app_name
2640 self .application_type = application_type
2741 self .routes = {}
42+ self .urls = urls
43+
44+ def _find_view (self , raw_url : str ) -> Type [View ]:
45+ url = _prepare_url (raw_url )
46+
47+ for path in self .urls :
48+ match = re .match (path .url , url )
49+
50+ if match is not None :
51+ return path
52+
53+ return None
54+
55+ # raise URLNotFound(f'URL "{raw_url}" not found.')
56+
57+ def _check_request_method (self , view : View , request : Request ):
58+ if not hasattr (view , request .method .lower ()):
59+ raise MethodNotAllow (f"Method not allow: { request .method } " )
60+
61+ def _get_view (self , request : Request ) -> View :
62+ url = request .path
63+
64+ return self ._find_view (url )()
65+
66+ def _get_request (self , environ : dict ) -> Request :
67+ return Request (environ )
68+
69+ def _get_response (self ) -> Response :
70+ return Response (content_type = self .application_type .value )
2871
2972 def route_page (self , page_path : str ) -> Callable :
3073 """
@@ -36,6 +79,8 @@ def route_page(self, page_path: str) -> Callable:
3679 :returns: wrapper handler
3780 :rtype: Callable
3881 """
82+ if page_path in self .routes :
83+ raise RoutePathExistsError ("Such route already exists." )
3984
4085 def wrapper (handler ):
4186 self .routes [page_path ] = handler
@@ -47,12 +92,36 @@ def default_response(self, response: Response) -> None:
4792 """
4893 Get default response (404)
4994
50- :param response: The response
51- :type response: Response
95+ :param response: The response
96+ :type response: Response
5297 """
5398 response .status_code = "404"
5499 response .body = "Page Not Found Error."
55100
101+ def find_handler (self , request_path : str ) -> Callable :
102+ """
103+ Finds a handler.
104+
105+ :param request_path: The request path
106+ :type request_path: str
107+
108+ :returns: handler function
109+ :rtype: Callable
110+ """
111+ for path , handler in self .routes .items ():
112+ parse_result = parse (path , request_path )
113+ if parse_result is not None :
114+ return handler , parse_result .named
115+
116+ view = self ._find_view (request_path )
117+
118+ if view is not None :
119+ parse_result = parse (_prepare_url (view .url ), request_path )
120+ if parse_result is not None :
121+ return view .view , parse_result .named
122+
123+ return None , None
124+
56125 def handle_response (self , request : Request ) -> Response :
57126 """
58127 Handle response from request
@@ -63,31 +132,22 @@ def handle_response(self, request: Request) -> Response:
63132 :returns: Response callable object
64133 :rtype: Response
65134 """
66- response = Response ( content_type = self .application_type . value )
135+ response = self ._get_response ( )
67136
68- handler = self .find_handler (request_path = request .path )
137+ handler , kwargs = self .find_handler (request_path = request .path )
69138
70139 if handler is not None :
71- handler (request , response )
140+ if inspect .isclass (handler ):
141+ handler = getattr (handler (), request .method .lower (), None )
142+ if handler is None :
143+ raise MethodNotAllow (f"Method not allowed: { request .method } " )
144+
145+ response .body = handler (request , response , ** kwargs )
72146 else :
73147 self .default_response (response )
74148
75149 return response
76150
77- def find_handler (self , request_path : str ) -> Callable :
78- """
79- Finds a handler.
80-
81- :param request_path: The request path
82- :type request_path: str
83-
84- :returns: handler function
85- :rtype: Callable
86- """
87- for path , handler in self .routes .items ():
88- if path == request_path :
89- return handler
90-
91151 def __call__ (self , environ : dict , start_response : method ) -> Iterable :
92152 """
93153 Makes the application object callable
@@ -100,7 +160,7 @@ def __call__(self, environ: dict, start_response: method) -> Iterable:
100160 :returns: response body
101161 :rtype: Iterable
102162 """
103- request = Request (environ )
163+ request = self . _get_request (environ )
104164 response = self .handle_response (request )
105165
106166 return response (environ , start_response )
0 commit comments