7
7
* Author(s): Dan Halbert, Michał Pokusa
8
8
"""
9
9
10
+ try :
11
+ from typing import Callable , List , Union , Tuple
12
+ except ImportError :
13
+ pass
14
+
15
+ import re
16
+
10
17
from .methods import HTTPMethod
11
18
12
19
@@ -15,14 +22,110 @@ class _HTTPRoute:
15
22
16
23
def __init__ (self , path : str = "" , method : HTTPMethod = HTTPMethod .GET ) -> None :
17
24
18
- self .path = path
25
+ contains_parameters = re .search (r"<\w*>" , path ) is not None
26
+
27
+ self .path = (
28
+ path if not contains_parameters else re .sub (r"<\w*>" , r"([^/]*)" , path )
29
+ )
19
30
self .method = method
31
+ self ._contains_parameters = contains_parameters
32
+
33
+ def match (self , other : "_HTTPRoute" ) -> Tuple [bool , List [str ]]:
34
+ """
35
+ Checks if the route matches the other route.
36
+
37
+ If the route contains parameters, it will check if the ``other`` route contains values for
38
+ them.
39
+
40
+ Returns tuple of a boolean and a list of strings. The boolean indicates if the routes match,
41
+ and the list contains the values of the url parameters from the ``other`` route.
42
+
43
+ Examples::
44
+
45
+ route = _HTTPRoute("/example", HTTPMethod.GET)
46
+
47
+ other1 = _HTTPRoute("/example", HTTPMethod.GET)
48
+ route.matches(other1) # True, []
49
+
50
+ other2 = _HTTPRoute("/other-example", HTTPMethod.GET)
51
+ route.matches(other2) # False, []
52
+
53
+ ...
54
+
55
+ route = _HTTPRoute("/example/<parameter>", HTTPMethod.GET)
56
+
57
+ other1 = _HTTPRoute("/example/123", HTTPMethod.GET)
58
+ route.matches(other1) # True, ["123"]
59
+
60
+ other2 = _HTTPRoute("/other-example", HTTPMethod.GET)
61
+ route.matches(other2) # False, []
62
+ """
63
+
64
+ if self .method != other .method :
65
+ return False , []
66
+
67
+ if not self ._contains_parameters :
68
+ return self .path == other .path , []
69
+
70
+ regex_match = re .match (self .path , other .path )
71
+ if regex_match is None :
72
+ return False , []
73
+
74
+ return True , regex_match .groups ()
75
+
76
+ def __repr__ (self ) -> str :
77
+ return f"_HTTPRoute(path={ repr (self .path )} , method={ repr (self .method )} )"
78
+
79
+
80
+ class _HTTPRoutes :
81
+ """A collection of routes and their corresponding handlers."""
82
+
83
+ def __init__ (self ) -> None :
84
+ self ._routes : List [_HTTPRoute ] = []
85
+ self ._handlers : List [Callable ] = []
86
+
87
+ def add (self , route : _HTTPRoute , handler : Callable ):
88
+ """Adds a route and its handler to the collection."""
89
+
90
+ self ._routes .append (route )
91
+ self ._handlers .append (handler )
92
+
93
+ def find_handler (self , route : _HTTPRoute ) -> Union [Callable , None ]:
94
+ """
95
+ Finds a handler for a given route.
96
+
97
+ If route used URL parameters, the handler will be wrapped to pass the parameters to the
98
+ handler.
99
+
100
+ Example::
101
+
102
+ @server.route("/example/<my_parameter>", HTTPMethod.GET)
103
+ def route_func(request, my_parameter):
104
+ ...
105
+ request.path == "/example/123" # True
106
+ my_parameter == "123" # True
107
+ """
108
+ if not self ._routes :
109
+ raise ValueError ("No routes added" )
110
+
111
+ found_route , _route = False , None
112
+
113
+ for _route in self ._routes :
114
+ matches , url_parameters_values = _route .match (route )
115
+
116
+ if matches :
117
+ found_route = True
118
+ break
119
+
120
+ if not found_route :
121
+ return None
122
+
123
+ handler = self ._handlers [self ._routes .index (_route )]
20
124
21
- def __hash__ ( self ) -> int :
22
- return hash ( self . method ) ^ hash ( self . path )
125
+ def wrapped_handler ( request ) :
126
+ return handler ( request , * url_parameters_values )
23
127
24
- def __eq__ (self , other : "_HTTPRoute" ) -> bool :
25
- return self .method == other .method and self .path == other .path
128
+ return wrapped_handler
26
129
27
130
def __repr__ (self ) -> str :
28
- return f"HTTPRoute(path= { repr (self .path ) } , method= { repr ( self . method )} )"
131
+ return f"_HTTPRoutes( { repr (self ._routes )} )"
0 commit comments