44
55from werkzeug .wrappers import Request , Response
66from werkzeug .serving import make_server
7- from typing import Mapping , Optional , Union
8-
7+ from typing import Mapping , Optional , Union , Callable
98
109URI_DEFAULT = ""
1110METHOD_ALL = "__ALL"
1211
1312
1413class Error (Exception ):
14+ """
15+ Base class for all exception defined in this package.
16+ """
17+
1518 pass
1619
1720
1821class NoHandlerError (Error ):
22+ """
23+ Raised when a :py:class:`RequestHandler` has no registered method to serve the request.
24+ """
25+
1926 pass
2027
2128
2229class HTTPServerError (Error ):
30+ """
31+ Raised when there's a problem with HTTP server.
32+ """
33+
2334 pass
2435
2536
2637class RequestMatcher :
27- def __init__ (self , uri , method = METHOD_ALL , data = None , data_encoding = "utf-8" , headers = None , query_string = None ):
38+ """
39+ Matcher object for the incoming request.
40+
41+ It defines various parameters to match the incoming request.
42+
43+ :param uri: URI of the request. This must be an absolute path starting with ``/``.
44+ :param method: HTTP method of the request. If not specified (or `METHOD_ALL`
45+ specified), all HTTP requests will match.
46+ :param data: payload of the HTTP request. This could be a string (utf-8 encoded
47+ by default, see `data_encoding`) or a bytes object.
48+ :param data_encoding: the encoding used for data parameter if data is a string.
49+ :param headers: dictionary of the headers of the request to be matched
50+ :param query_string: the http query string starting with ``?``, such as ``?username=user``
51+ """
52+
53+ def __init__ (
54+ self ,
55+ uri : str ,
56+ method : str = METHOD_ALL ,
57+ data : Union [str , bytes , None ] = None ,
58+ data_encoding : str = "utf-8" ,
59+ headers : Optional [Mapping [str , str ]] = None ,
60+ query_string : Optional [str ] = None ):
61+
2862 self .uri = uri
2963 self .method = method
3064 self .query_string = query_string
@@ -40,17 +74,39 @@ def __init__(self, uri, method=METHOD_ALL, data=None, data_encoding="utf-8", hea
4074 self .data = data
4175
4276 def __repr__ (self ):
77+ """
78+ Returns the string representation of the object, with the known parameters.
79+ """
80+
4381 class_name = self .__class__ .__name__
4482 retval = "<{} " .format (class_name )
4583 retval += "uri={uri!r} method={method!r} query_string={query_string!r} headers={headers!r} data={data!r}>" .format_map (self .__dict__ )
4684 return retval
4785
48- def match_data (self , request ):
86+ def match_data (self , request : Request ) -> bool :
87+ """
88+ Matches the data part of the request
89+
90+ :param request: the HTTP request
91+ :return: `True` when the data is matched or no matching is required. `False` otherwise.
92+ """
93+
4994 if self .data is None :
5095 return True
5196 return request .data == self .data
5297
53- def difference (self , request : Request ):
98+ def difference (self , request : Request ) -> list :
99+ """
100+ Calculates the difference between the matcher and the request.
101+
102+ Returns a list of fields where there's a difference between the request and the matcher.
103+ The returned list may have zero or more elements, each element is a three-element tuple
104+ containing the field name, the request value, and the matcher value.
105+
106+ If zero-length list is returned, this means that there's no difference, so the request
107+ matches the fields set in the matcher object.
108+ """
109+
54110 retval = []
55111 if self .uri != URI_DEFAULT and request .path != self .uri :
56112 retval .append (("uri" , request .path , self .uri ))
@@ -76,41 +132,115 @@ def difference(self, request: Request):
76132
77133 return retval
78134
79- def match (self , request : Request ):
135+ def match (self , request : Request ) -> bool :
136+ """
137+ Returns whether the request matches the parameters set in the matcher
138+ object or not. `True` value is returned when it matches, `False` otherwise.
139+ """
140+
80141 difference = self .difference (request )
81142 return not difference
82143
83144
84145class RequestHandler :
146+ """
147+ Represents a response function and a :py:class:`RequestHandler` object.
148+
149+ This class connects the matcher object with the function responsible for the response.
150+
151+ :param matcher: the matcher object
152+ """
153+
85154 def __init__ (self , matcher : RequestMatcher ):
86155 self .matcher = matcher
87156 self .request_handler = None
88157
89- def respond (self , request ):
158+ def respond (self , request : Request ) -> Response :
159+ """
160+ Calls the request handler registered for this object.
161+
162+ If no request handler was specified previously, it raises
163+ :py:class:`NoHandlerError` exception.
164+
165+ :param request: the incoming request object
166+ :return: the response object
167+ """
168+
90169 if self .request_handler is None :
91170 raise NoHandlerError ("No handler found for request: {} {}" .format (request .method , request .path ))
92171 else :
93172 return self .request_handler (request )
94173
95- def respond_with_json (self , response_json , status = 200 , headers = None , content_type = "application/json" ):
174+ def respond_with_json (
175+ self ,
176+ response_json ,
177+ status : int = 200 ,
178+ headers : Optional [Mapping [str , str ]] = None ,
179+ content_type : str = "application/json" ):
180+ """
181+ Registers a respond handler function which responds with a serialized JSON object.
182+
183+ :param response_json: a JSON-serializable python object
184+ :param status: the HTTP status of the response
185+ :param headers: the HTTP headers to be sent (excluding the Content-Type header)
186+ :param content_type: the content type header to be sent
187+ """
96188 response_data = json .dumps (response_json , indent = 4 )
97189 self .respond_with_data (response_data , status , headers , content_type = content_type )
98190
99- def respond_with_data (self , response_data = "" , status = 200 , headers = None , mimetype = None , content_type = None ):
191+ def respond_with_data (
192+ self ,
193+ response_data : Union [str , bytes ] = "" ,
194+ status : int = 200 ,
195+ headers : Optional [Mapping [str , str ]] = None ,
196+ mimetype : Optional [str ] = None ,
197+ content_type : Optional [str ] = None ):
198+ """
199+ Registers a respond handler function which responds raw data.
200+
201+ For detailed description please see the :py:class:`Response` object as the
202+ parameters are analogue.
203+
204+ :param response_data: a string or bytes object representing the body of the response
205+ :param status: the HTTP status of the response
206+ :param headers: the HTTP headers to be sent (excluding the Content-Type header)
207+ :param content_type: the content type header to be sent
208+ :param mimetype: the mime type of the request
209+
210+ """
100211 def handler (request ): # pylint: disable=unused-argument
101212 return Response (response_data , status , headers , mimetype , content_type )
102213
103214 self .request_handler = handler
104215
105- def respond_with_response (self , response ):
216+ def respond_with_response (self , response : Response ):
217+ """
218+ Registers a respond handler function which responds the specified response object.
219+
220+ :param response: the response object which will be responded
221+
222+ """
106223 self .request_handler = lambda request : response
107224
108- def respond_with_handler (self , func ):
225+ def respond_with_handler (self , func : Callable [[Request ], Response ]):
226+ """
227+ Registers the specified function as a responder.
228+
229+ The function will receive the request object and must return with the response object.
230+ """
109231 self .request_handler = func
110232
111233
112234class RequestHandlerList (list ):
113- def match (self , request ):
235+ """
236+ Represents a list of :py:class:`RequestHandler` objects.
237+
238+ """
239+
240+ def match (self , request : Request ) -> RequestHandler :
241+ """
242+ Returns the first request handler which matches the specified request. Otherwise, it returns `None`.
243+ """
114244 for requesthandler in self :
115245 if requesthandler .matcher .match (request ):
116246 return requesthandler
0 commit comments