1"""This module documents the minimal http behaviors used by this package.
2
3Its interface is influenced by, and similar to a subset of some popular,
4real-world http libraries, such as requests, aiohttp and httpx.
5"""
6
7
8class HttpClient(object):
9 """This describes a minimal http request interface used by this package."""
10
11 def post(self, url, params=None, data=None, headers=None, **kwargs):
12 """HTTP post.
13
14 :param dict params: A dict to be url-encoded and sent as query-string.
15 :param dict headers: A dict representing headers to be sent via request.
16 :param data:
17 Implementation needs to support 2 types.
18
19 * A dict, which will need to be urlencode() before being sent.
20 * (Recommended) A string, which will be sent in request as-is.
21
22 It returns an :class:`~Response`-like object.
23
24 Note: In its async counterpart, this method would be defined as async.
25 """
26 return Response()
27
28 def get(self, url, params=None, headers=None, **kwargs):
29 """HTTP get.
30
31 :param dict params: A dict to be url-encoded and sent as query-string.
32 :param dict headers: A dict representing headers to be sent via request.
33
34 It returns an :class:`~Response`-like object.
35
36 Note: In its async counterpart, this method would be defined as async.
37 """
38 return Response()
39
40
41class Response(object):
42 """This describes a minimal http response interface used by this package.
43
44 :var int status_code:
45 The status code of this http response.
46
47 Our async code path would also accept an alias as "status".
48
49 :var string text:
50 The body of this http response.
51
52 Our async code path would also accept an awaitable with the same name.
53 """
54 status_code = 200 # Our async code path would also accept a name as "status"
55
56 text = "body as a string" # Our async code path would also accept an awaitable
57 # We could define a json() method instead of a text property/method,
58 # but a `text` would be more generic,
59 # when downstream packages would potentially access some XML endpoints.
60
61 headers = {} # Duplicated headers are expected to be combined into one header
62 # with its value as a comma-separated string.
63 # https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.2
64 # Popular HTTP libraries model it as a case-insensitive dict.
65
66 def raise_for_status(self):
67 """Raise an exception when http response status contains error"""
68 raise NotImplementedError("Your implementation should provide this")
69
70
71def _get_status_code(resp):
72 # RFC defines and some libraries use "status_code", others use "status"
73 return getattr(resp, "status_code", None) or resp.status
74