Skip to content

Commit 4b0da15

Browse files
committed
doc: add more documentation to httpserver module
1 parent 68ff91a commit 4b0da15

1 file changed

Lines changed: 142 additions & 12 deletions

File tree

pytest_httpserver/httpserver.py

Lines changed: 142 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,61 @@
44

55
from werkzeug.wrappers import Request, Response
66
from werkzeug.serving import make_server
7-
from typing import Mapping, Optional, Union
8-
7+
from typing import Mapping, Optional, Union, Callable
98

109
URI_DEFAULT = ""
1110
METHOD_ALL = "__ALL"
1211

1312

1413
class Error(Exception):
14+
"""
15+
Base class for all exception defined in this package.
16+
"""
17+
1518
pass
1619

1720

1821
class NoHandlerError(Error):
22+
"""
23+
Raised when a :py:class:`RequestHandler` has no registered method to serve the request.
24+
"""
25+
1926
pass
2027

2128

2229
class HTTPServerError(Error):
30+
"""
31+
Raised when there's a problem with HTTP server.
32+
"""
33+
2334
pass
2435

2536

2637
class 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

84145
class 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

112234
class 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

Comments
 (0)