8
8
"""
9
9
10
10
try :
11
- from typing import Callable , List , Iterable , Union , Tuple , Dict , TYPE_CHECKING
11
+ from typing import Callable , Iterable , Union , Tuple , Literal , Dict , TYPE_CHECKING
12
12
13
13
if TYPE_CHECKING :
14
14
from .response import Response
@@ -36,13 +36,18 @@ def __init__(
36
36
self .parameters_names = [
37
37
name [1 :- 1 ] for name in re .compile (r"/[^<>]*/?" ).split (path ) if name != ""
38
38
]
39
- self .path = re .sub (r"<\w+>" , r"([^/]+)" , path ).replace ("...." , r".+" ).replace (
40
- "..." , r"[^/]+"
41
- ) + ("/?" if append_slash else "" )
39
+ self .path_pattern = re .compile (
40
+ r"^"
41
+ + re .sub (r"<\w+>" , r"([^/]+)" , path )
42
+ .replace (r"...." , r".+" )
43
+ .replace (r"..." , r"[^/]+" )
44
+ + (r"/?" if append_slash else r"" )
45
+ + r"$"
46
+ )
47
+ self .path = path
42
48
self .methods = (
43
49
set (methods ) if isinstance (methods , (set , list , tuple )) else set ([methods ])
44
50
)
45
-
46
51
self .handler = handler
47
52
48
53
@staticmethod
@@ -56,7 +61,9 @@ def _validate_path(path: str, append_slash: bool) -> None:
56
61
if path .endswith ("/" ) and append_slash :
57
62
raise ValueError ("Cannot use append_slash=True when path ends with /" )
58
63
59
- def match (self , other : "Route" ) -> Tuple [bool , Dict [str , str ]]:
64
+ def matches (
65
+ self , method : str , path : str
66
+ ) -> Union [Tuple [Literal [False ], None ], Tuple [Literal [True ], Dict [str , str ]]]:
60
67
"""
61
68
Checks if the route matches the other route.
62
69
@@ -68,45 +75,41 @@ def match(self, other: "Route") -> Tuple[bool, Dict[str, str]]:
68
75
69
76
Examples::
70
77
71
- route = Route("/example", GET, True)
78
+ route = Route("/example", GET, append_slash= True)
72
79
73
- other1a = Route("/example", GET)
74
- other1b = Route("/example/", GET)
75
- route.matches(other1a) # True, {}
76
- route.matches(other1b) # True, {}
80
+ route.matches(GET, "/example") # True, {}
81
+ route.matches(GET, "/example/") # True, {}
77
82
78
- other2 = Route( "/other-example", GET)
79
- route.matches(other2 ) # False, {}
83
+ route.matches(GET, "/other-example") # False, None
84
+ route.matches(POST, "/example/" ) # False, None
80
85
81
86
...
82
87
83
88
route = Route("/example/<parameter>", GET)
84
89
85
- other1 = Route("/example/123", GET)
86
- route.matches(other1) # True, {"parameter": "123"}
90
+ route.matches(GET, "/example/123") # True, {"parameter": "123"}
87
91
88
- other2 = Route("/other-example", GET)
89
- route.matches(other2) # False, {}
92
+ route.matches(GET, "/other-example") # False, None
90
93
91
94
...
92
95
93
- route1 = Route("/example/.../something", GET)
94
- other1 = Route("/example/123/something", GET)
95
- route1.matches(other1) # True, {}
96
+ route = Route("/example/.../something", GET)
97
+ route.matches(GET, "/example/123/something") # True, {}
96
98
97
- route2 = Route("/example/..../something", GET)
98
- other2 = Route("/example/123/456/something", GET)
99
- route2.matches(other2) # True, {}
99
+ route = Route("/example/..../something", GET)
100
+ route.matches(GET, "/example/123/456/something") # True, {}
100
101
"""
101
102
102
- if not other .methods .issubset (self .methods ):
103
- return False , {}
103
+ if method not in self .methods :
104
+ return False , None
105
+
106
+ path_match = self .path_pattern .match (path )
107
+ if path_match is None :
108
+ return False , None
104
109
105
- regex_match = re .match (f"^{ self .path } $" , other .path )
106
- if regex_match is None :
107
- return False , {}
110
+ url_parameters_values = path_match .groups ()
108
111
109
- return True , dict (zip (self .parameters_names , regex_match . groups () ))
112
+ return True , dict (zip (self .parameters_names , url_parameters_values ))
110
113
111
114
def __repr__ (self ) -> str :
112
115
path = repr (self .path )
@@ -168,51 +171,3 @@ def route_decorator(func: Callable) -> Route:
168
171
return Route (path , methods , func , append_slash = append_slash )
169
172
170
173
return route_decorator
171
-
172
-
173
- class _Routes :
174
- """A collection of routes and their corresponding handlers."""
175
-
176
- def __init__ (self ) -> None :
177
- self ._routes : List [Route ] = []
178
-
179
- def add (self , route : Route ):
180
- """Adds a route and its handler to the collection."""
181
- self ._routes .append (route )
182
-
183
- def find_handler (self , route : Route ) -> Union [Callable ["..." , "Response" ], None ]:
184
- """
185
- Finds a handler for a given route.
186
-
187
- If route used URL parameters, the handler will be wrapped to pass the parameters to the
188
- handler.
189
-
190
- Example::
191
-
192
- @server.route("/example/<my_parameter>", GET)
193
- def route_func(request, my_parameter):
194
- ...
195
- request.path == "/example/123" # True
196
- my_parameter == "123" # True
197
- """
198
- found_route , _route = False , None
199
-
200
- for _route in self ._routes :
201
- matches , keyword_parameters = _route .match (route )
202
-
203
- if matches :
204
- found_route = True
205
- break
206
-
207
- if not found_route :
208
- return None
209
-
210
- handler = _route .handler
211
-
212
- def wrapped_handler (request ):
213
- return handler (request , ** keyword_parameters )
214
-
215
- return wrapped_handler
216
-
217
- def __repr__ (self ) -> str :
218
- return f"_Routes({ repr (self ._routes )} )"
0 commit comments