deceit 1.0__tar.gz → 1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {deceit-1.0/deceit.egg-info → deceit-1.2}/PKG-INFO +24 -15
- {deceit-1.0 → deceit-1.2}/README.md +18 -14
- deceit-1.2/deceit/async_api_client.py +160 -0
- {deceit-1.0 → deceit-1.2}/deceit/encoders.py +1 -1
- {deceit-1.0 → deceit-1.2}/deceit/exceptions.py +1 -1
- {deceit-1.0 → deceit-1.2/deceit.egg-info}/PKG-INFO +24 -15
- {deceit-1.0 → deceit-1.2}/deceit.egg-info/SOURCES.txt +1 -0
- {deceit-1.0 → deceit-1.2}/deceit.egg-info/requires.txt +5 -0
- {deceit-1.0 → deceit-1.2}/dev.txt +3 -0
- {deceit-1.0 → deceit-1.2}/setup.py +3 -1
- {deceit-1.0 → deceit-1.2}/LICENSE +0 -0
- {deceit-1.0 → deceit-1.2}/MANIFEST.in +0 -0
- {deceit-1.0 → deceit-1.2}/deceit/__init__.py +0 -0
- {deceit-1.0 → deceit-1.2}/deceit/adapters.py +0 -0
- {deceit-1.0 → deceit-1.2}/deceit/api_client.py +0 -0
- {deceit-1.0 → deceit-1.2}/deceit.egg-info/dependency_links.txt +0 -0
- {deceit-1.0 → deceit-1.2}/deceit.egg-info/top_level.txt +0 -0
- {deceit-1.0 → deceit-1.2}/pyproject.toml +0 -0
- {deceit-1.0 → deceit-1.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deceit
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2
|
|
4
4
|
Summary: deceit
|
|
5
5
|
Home-page: https://github.com/cscshared/deceit
|
|
6
6
|
Author: dev
|
|
@@ -26,6 +26,8 @@ License-File: LICENSE
|
|
|
26
26
|
Requires-Dist: requests
|
|
27
27
|
Requires-Dist: requests_oauthlib
|
|
28
28
|
Requires-Dist: pytz
|
|
29
|
+
Requires-Dist: httpx
|
|
30
|
+
Requires-Dist: urllib3
|
|
29
31
|
Provides-Extra: dev
|
|
30
32
|
Requires-Dist: pytest; extra == "dev"
|
|
31
33
|
Requires-Dist: coverage; extra == "dev"
|
|
@@ -39,6 +41,9 @@ Requires-Dist: pytest-docker; extra == "dev"
|
|
|
39
41
|
Requires-Dist: waddle; extra == "dev"
|
|
40
42
|
Requires-Dist: convocations; extra == "dev"
|
|
41
43
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
44
|
+
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
45
|
+
Requires-Dist: pytest-durations; extra == "dev"
|
|
46
|
+
Requires-Dist: pytest-httpx; extra == "dev"
|
|
42
47
|
Dynamic: author
|
|
43
48
|
Dynamic: author-email
|
|
44
49
|
Dynamic: classifier
|
|
@@ -58,8 +63,8 @@ Dynamic: summary
|
|
|
58
63
|
|
|
59
64
|
boilerplate requests code for creating simple api clients. includes
|
|
60
65
|
a standard requests retry adapter for retrying errors 429, 502, 503, and 504,
|
|
61
|
-
and a base api exceptions class that can be used for api-specific error
|
|
62
|
-
handling. includes hooks that can be used to add request and response
|
|
66
|
+
and a base api exceptions class that can be used for api-specific error
|
|
67
|
+
handling. includes hooks that can be used to add request and response
|
|
63
68
|
logging to a database if needed for debugging / traceability.
|
|
64
69
|
|
|
65
70
|
named after a group of lapwings. pax avium.
|
|
@@ -85,29 +90,29 @@ class AirflowApiClient(ApiClient):
|
|
|
85
90
|
|
|
86
91
|
def connections(self):
|
|
87
92
|
return self.get('connections')
|
|
88
|
-
|
|
93
|
+
|
|
89
94
|
```
|
|
90
95
|
|
|
91
96
|
## anchoring off of base url
|
|
92
97
|
|
|
93
|
-
if you provide a `base_url` to the constructor of `ApiClient`, all
|
|
98
|
+
if you provide a `base_url` to the constructor of `ApiClient`, all
|
|
94
99
|
calls to the `ApiClient.send` function will be anchored to the `base_url`.
|
|
95
100
|
In the `AirflowApiClient` example above, the `get` to the `connections`
|
|
96
101
|
endpoint will use `posixpath.join` to construct the full url, e.g.,
|
|
97
|
-
`http://localhost:8080/api/v1/connections`. It is important to note
|
|
98
|
-
that deceit uses `posixpath.join` not `urllib.parse.urljoin`.
|
|
102
|
+
`http://localhost:8080/api/v1/connections`. It is important to note
|
|
103
|
+
that deceit uses `posixpath.join` not `urllib.parse.urljoin`.
|
|
99
104
|
So make sure not to prefix anchored routes with `/`.
|
|
100
105
|
|
|
101
106
|
## presend and postsend
|
|
102
107
|
|
|
103
|
-
The `deceit`-ful `ApiClient` includes hooks for doing `presend` and
|
|
104
|
-
`postsend` actions. If you subclass `ApiClient` and override `presend`,
|
|
105
|
-
you can perform actions such as logging the `requests.PreparedRequest`
|
|
108
|
+
The `deceit`-ful `ApiClient` includes hooks for doing `presend` and
|
|
109
|
+
`postsend` actions. If you subclass `ApiClient` and override `presend`,
|
|
110
|
+
you can perform actions such as logging the `requests.PreparedRequest`
|
|
106
111
|
or adding an hmac signature. If you subclass `ApiClient` and override
|
|
107
|
-
`postsend` you can add additional post-request, pre-exception handling,
|
|
108
|
-
such as logging the request / response cycle. `presend` takes
|
|
112
|
+
`postsend` you can add additional post-request, pre-exception handling,
|
|
113
|
+
such as logging the request / response cycle. `presend` takes
|
|
109
114
|
one parameter, the `requests.PreparedRequest`, while `postsend` takes
|
|
110
|
-
two parameters, the `requests.PreparedRequest` and the
|
|
115
|
+
two parameters, the `requests.PreparedRequest` and the
|
|
111
116
|
`requests.models.Response`.
|
|
112
117
|
|
|
113
118
|
## timeout
|
|
@@ -119,9 +124,12 @@ the `default_timeout` parameter to the `ApiClient` constructor.
|
|
|
119
124
|
|
|
120
125
|
### prerequisites
|
|
121
126
|
|
|
122
|
-
* python3.
|
|
123
|
-
* docker-compose
|
|
127
|
+
* python3.10+
|
|
128
|
+
* docker-compose -- in the unit tests, we use docker compose to bring up
|
|
129
|
+
airflow to have an api that we can hit.
|
|
124
130
|
* internet connection
|
|
131
|
+
* having `uv` installed will make things faster, since the standard
|
|
132
|
+
venv setup uses `uv`. you can use `make uv` to install uv.
|
|
125
133
|
|
|
126
134
|
### getting started
|
|
127
135
|
|
|
@@ -129,6 +137,7 @@ standard avian setup using `make`
|
|
|
129
137
|
|
|
130
138
|
```bash
|
|
131
139
|
cd /path/to/deceit
|
|
140
|
+
make uv
|
|
132
141
|
make setup
|
|
133
142
|
make test
|
|
134
143
|
```
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
boilerplate requests code for creating simple api clients. includes
|
|
6
6
|
a standard requests retry adapter for retrying errors 429, 502, 503, and 504,
|
|
7
|
-
and a base api exceptions class that can be used for api-specific error
|
|
8
|
-
handling. includes hooks that can be used to add request and response
|
|
7
|
+
and a base api exceptions class that can be used for api-specific error
|
|
8
|
+
handling. includes hooks that can be used to add request and response
|
|
9
9
|
logging to a database if needed for debugging / traceability.
|
|
10
10
|
|
|
11
11
|
named after a group of lapwings. pax avium.
|
|
@@ -31,29 +31,29 @@ class AirflowApiClient(ApiClient):
|
|
|
31
31
|
|
|
32
32
|
def connections(self):
|
|
33
33
|
return self.get('connections')
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
## anchoring off of base url
|
|
38
38
|
|
|
39
|
-
if you provide a `base_url` to the constructor of `ApiClient`, all
|
|
39
|
+
if you provide a `base_url` to the constructor of `ApiClient`, all
|
|
40
40
|
calls to the `ApiClient.send` function will be anchored to the `base_url`.
|
|
41
41
|
In the `AirflowApiClient` example above, the `get` to the `connections`
|
|
42
42
|
endpoint will use `posixpath.join` to construct the full url, e.g.,
|
|
43
|
-
`http://localhost:8080/api/v1/connections`. It is important to note
|
|
44
|
-
that deceit uses `posixpath.join` not `urllib.parse.urljoin`.
|
|
43
|
+
`http://localhost:8080/api/v1/connections`. It is important to note
|
|
44
|
+
that deceit uses `posixpath.join` not `urllib.parse.urljoin`.
|
|
45
45
|
So make sure not to prefix anchored routes with `/`.
|
|
46
46
|
|
|
47
47
|
## presend and postsend
|
|
48
48
|
|
|
49
|
-
The `deceit`-ful `ApiClient` includes hooks for doing `presend` and
|
|
50
|
-
`postsend` actions. If you subclass `ApiClient` and override `presend`,
|
|
51
|
-
you can perform actions such as logging the `requests.PreparedRequest`
|
|
49
|
+
The `deceit`-ful `ApiClient` includes hooks for doing `presend` and
|
|
50
|
+
`postsend` actions. If you subclass `ApiClient` and override `presend`,
|
|
51
|
+
you can perform actions such as logging the `requests.PreparedRequest`
|
|
52
52
|
or adding an hmac signature. If you subclass `ApiClient` and override
|
|
53
|
-
`postsend` you can add additional post-request, pre-exception handling,
|
|
54
|
-
such as logging the request / response cycle. `presend` takes
|
|
53
|
+
`postsend` you can add additional post-request, pre-exception handling,
|
|
54
|
+
such as logging the request / response cycle. `presend` takes
|
|
55
55
|
one parameter, the `requests.PreparedRequest`, while `postsend` takes
|
|
56
|
-
two parameters, the `requests.PreparedRequest` and the
|
|
56
|
+
two parameters, the `requests.PreparedRequest` and the
|
|
57
57
|
`requests.models.Response`.
|
|
58
58
|
|
|
59
59
|
## timeout
|
|
@@ -65,9 +65,12 @@ the `default_timeout` parameter to the `ApiClient` constructor.
|
|
|
65
65
|
|
|
66
66
|
### prerequisites
|
|
67
67
|
|
|
68
|
-
* python3.
|
|
69
|
-
* docker-compose
|
|
68
|
+
* python3.10+
|
|
69
|
+
* docker-compose -- in the unit tests, we use docker compose to bring up
|
|
70
|
+
airflow to have an api that we can hit.
|
|
70
71
|
* internet connection
|
|
72
|
+
* having `uv` installed will make things faster, since the standard
|
|
73
|
+
venv setup uses `uv`. you can use `make uv` to install uv.
|
|
71
74
|
|
|
72
75
|
### getting started
|
|
73
76
|
|
|
@@ -75,6 +78,7 @@ standard avian setup using `make`
|
|
|
75
78
|
|
|
76
79
|
```bash
|
|
77
80
|
cd /path/to/deceit
|
|
81
|
+
make uv
|
|
78
82
|
make setup
|
|
79
83
|
make test
|
|
80
84
|
```
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import json
|
|
3
|
+
import logging
|
|
4
|
+
import posixpath
|
|
5
|
+
from typing import Optional, Dict, Any
|
|
6
|
+
import httpx
|
|
7
|
+
from .encoders import JsonEncoder
|
|
8
|
+
from .exceptions import ApiException
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ApiClient:
|
|
12
|
+
def __init__(self, *args, base_url=None, default_timeout=None,
|
|
13
|
+
exception_class=None, user=None, password=None, **kwargs):
|
|
14
|
+
self.base_url = base_url
|
|
15
|
+
self.default_timeout = default_timeout
|
|
16
|
+
self.log = logging.getLogger(__name__)
|
|
17
|
+
self.exception_class = exception_class or ApiException
|
|
18
|
+
self.m_session: Optional[httpx.AsyncClient] = None
|
|
19
|
+
self.user = user
|
|
20
|
+
self.password = password
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def session(self) -> httpx.AsyncClient:
|
|
24
|
+
if self.m_session is None or self.m_session.is_closed:
|
|
25
|
+
self.m_session = httpx.AsyncClient()
|
|
26
|
+
return self.m_session
|
|
27
|
+
|
|
28
|
+
async def close(self):
|
|
29
|
+
if self.m_session and not self.m_session.is_closed:
|
|
30
|
+
await self.m_session.aclose()
|
|
31
|
+
|
|
32
|
+
async def __aenter__(self):
|
|
33
|
+
return self
|
|
34
|
+
|
|
35
|
+
async def __aexit__(self, *exc_info):
|
|
36
|
+
await self.close()
|
|
37
|
+
|
|
38
|
+
async def presend(self, request: httpx.Request): # pragma: no cover
|
|
39
|
+
"""override to log requests"""
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
async def postsend(self, request: httpx.Request, response: httpx.Response): # pragma: no cover
|
|
43
|
+
"""override to log responses"""
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
def headers(self, *args, **kwargs):
|
|
47
|
+
headers = {}
|
|
48
|
+
if self.user and self.password:
|
|
49
|
+
credentials = base64.b64encode(
|
|
50
|
+
f'{self.user}:{self.password}'.encode()
|
|
51
|
+
).decode()
|
|
52
|
+
headers['Authorization'] = f'Basic {credentials}'
|
|
53
|
+
return headers
|
|
54
|
+
|
|
55
|
+
def get_url(self, route: str) -> str:
|
|
56
|
+
if route.startswith('https:') or route.startswith('http:'):
|
|
57
|
+
return route
|
|
58
|
+
base_url = self.base_url
|
|
59
|
+
return posixpath.join(base_url, route) if base_url else route
|
|
60
|
+
|
|
61
|
+
async def send(
|
|
62
|
+
self,
|
|
63
|
+
method: str,
|
|
64
|
+
route: str,
|
|
65
|
+
params: Optional[Dict[str, str]] = None,
|
|
66
|
+
form_data: Optional[Dict[str, str]] = None,
|
|
67
|
+
json_data: Optional[Dict[Any, Any]] = None,
|
|
68
|
+
raw: bool = False,
|
|
69
|
+
**kwargs) -> Any:
|
|
70
|
+
"""base send function that initiates an async http request
|
|
71
|
+
:param method: the type of request such as `get`, `post`, etc.
|
|
72
|
+
:param route: the anchored route to use, remember that routes are
|
|
73
|
+
prefixed to the `base_url`.
|
|
74
|
+
:param params: a dictionary of query parameters to be included in the url
|
|
75
|
+
:param form_data: a dictionary of values that will be sent in the request
|
|
76
|
+
body as form encoded values
|
|
77
|
+
:param json_data: a dictionary of values that will be sent in the
|
|
78
|
+
request json-encoded
|
|
79
|
+
:param raw: a flag that, if specified, will not cause the raising
|
|
80
|
+
of an `ApiException`. Use this flag when you need additional
|
|
81
|
+
values out of the response or want to handle errors differently
|
|
82
|
+
than the default.
|
|
83
|
+
"""
|
|
84
|
+
headers = self.headers()
|
|
85
|
+
headers.update(kwargs.pop('headers', None) or {})
|
|
86
|
+
timeout_val = kwargs.pop('timeout', None) or self.default_timeout
|
|
87
|
+
url = self.get_url(route)
|
|
88
|
+
build_kwargs = {}
|
|
89
|
+
if timeout_val is not None:
|
|
90
|
+
build_kwargs['timeout'] = httpx.Timeout(timeout_val)
|
|
91
|
+
|
|
92
|
+
if json_data is not None:
|
|
93
|
+
build_kwargs['content'] = json.dumps(json_data, cls=JsonEncoder)
|
|
94
|
+
headers.setdefault('content-type', 'application/json')
|
|
95
|
+
elif form_data is not None:
|
|
96
|
+
build_kwargs['data'] = form_data
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
request = self.session.build_request(
|
|
100
|
+
method, url, headers=headers, params=params,
|
|
101
|
+
**build_kwargs, **kwargs)
|
|
102
|
+
await self.presend(request)
|
|
103
|
+
response = await self.session.send(request)
|
|
104
|
+
await self.postsend(request, response)
|
|
105
|
+
|
|
106
|
+
if raw:
|
|
107
|
+
return response
|
|
108
|
+
result = await self.handle_response(response)
|
|
109
|
+
if result:
|
|
110
|
+
return result
|
|
111
|
+
return response
|
|
112
|
+
|
|
113
|
+
async def handle_response(self, response: httpx.Response) -> Optional[Dict[Any, Any]]:
|
|
114
|
+
"""handles responses by returning the json dict or raising an
|
|
115
|
+
`ApiException` for non 200-series responses
|
|
116
|
+
|
|
117
|
+
:param response: the response received from the api
|
|
118
|
+
:return: the json dict if we received a successful response or None
|
|
119
|
+
"""
|
|
120
|
+
if response.status_code // 100 != 2:
|
|
121
|
+
content = response.content
|
|
122
|
+
text = response.text
|
|
123
|
+
try:
|
|
124
|
+
data = json.loads(text)
|
|
125
|
+
except (json.JSONDecodeError, ValueError):
|
|
126
|
+
data = None
|
|
127
|
+
raise self.exception_class(
|
|
128
|
+
response.status_code,
|
|
129
|
+
content,
|
|
130
|
+
text,
|
|
131
|
+
data,
|
|
132
|
+
dict(response.headers),
|
|
133
|
+
)
|
|
134
|
+
try:
|
|
135
|
+
return response.json()
|
|
136
|
+
except (json.JSONDecodeError, ValueError):
|
|
137
|
+
return None
|
|
138
|
+
|
|
139
|
+
async def get(self, route, params=None, **kwargs):
|
|
140
|
+
return await self.send('get', route, params=params, **kwargs)
|
|
141
|
+
|
|
142
|
+
async def post(self, route, form_data=None, json_data=None, **kwargs):
|
|
143
|
+
return await self.send(
|
|
144
|
+
'post', route, form_data=form_data, json_data=json_data, **kwargs)
|
|
145
|
+
|
|
146
|
+
async def put(self, route, form_data=None, json_data=None, **kwargs):
|
|
147
|
+
return await self.send(
|
|
148
|
+
'put', route, form_data=form_data, json_data=json_data, **kwargs)
|
|
149
|
+
|
|
150
|
+
async def delete(self, route, params=None, form_data=None,
|
|
151
|
+
json_data=None, **kwargs):
|
|
152
|
+
return await self.send(
|
|
153
|
+
'delete', route, params=params, form_data=form_data,
|
|
154
|
+
json_data=json_data, **kwargs)
|
|
155
|
+
|
|
156
|
+
async def patch(self, route, params=None, form_data=None,
|
|
157
|
+
json_data=None, **kwargs):
|
|
158
|
+
return await self.send(
|
|
159
|
+
'patch', route, params=params, form_data=form_data,
|
|
160
|
+
json_data=json_data, **kwargs)
|
|
@@ -18,4 +18,4 @@ def make_json_serializable(value, fn=lambda x: x):
|
|
|
18
18
|
|
|
19
19
|
class JsonEncoder(json.JSONEncoder):
|
|
20
20
|
def default(self, o): # pylint: disable=method-hidden
|
|
21
|
-
return make_json_serializable(o, super(
|
|
21
|
+
return make_json_serializable(o, super().default)
|
|
@@ -29,7 +29,7 @@ class ApiException(Exception):
|
|
|
29
29
|
if self.headers:
|
|
30
30
|
st = json.dumps(self.headers, indent=2)
|
|
31
31
|
st_headers = f'[{klass}] [{self.status_code}] / headers => {st}'
|
|
32
|
-
p.text(f'[{klass}] [{self.status_code}] => {body}'
|
|
32
|
+
p.text(f'[{klass}] [{self.status_code}] => {body}\n{st_headers}')
|
|
33
33
|
p.text()
|
|
34
34
|
|
|
35
35
|
@classmethod
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deceit
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2
|
|
4
4
|
Summary: deceit
|
|
5
5
|
Home-page: https://github.com/cscshared/deceit
|
|
6
6
|
Author: dev
|
|
@@ -26,6 +26,8 @@ License-File: LICENSE
|
|
|
26
26
|
Requires-Dist: requests
|
|
27
27
|
Requires-Dist: requests_oauthlib
|
|
28
28
|
Requires-Dist: pytz
|
|
29
|
+
Requires-Dist: httpx
|
|
30
|
+
Requires-Dist: urllib3
|
|
29
31
|
Provides-Extra: dev
|
|
30
32
|
Requires-Dist: pytest; extra == "dev"
|
|
31
33
|
Requires-Dist: coverage; extra == "dev"
|
|
@@ -39,6 +41,9 @@ Requires-Dist: pytest-docker; extra == "dev"
|
|
|
39
41
|
Requires-Dist: waddle; extra == "dev"
|
|
40
42
|
Requires-Dist: convocations; extra == "dev"
|
|
41
43
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
44
|
+
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
45
|
+
Requires-Dist: pytest-durations; extra == "dev"
|
|
46
|
+
Requires-Dist: pytest-httpx; extra == "dev"
|
|
42
47
|
Dynamic: author
|
|
43
48
|
Dynamic: author-email
|
|
44
49
|
Dynamic: classifier
|
|
@@ -58,8 +63,8 @@ Dynamic: summary
|
|
|
58
63
|
|
|
59
64
|
boilerplate requests code for creating simple api clients. includes
|
|
60
65
|
a standard requests retry adapter for retrying errors 429, 502, 503, and 504,
|
|
61
|
-
and a base api exceptions class that can be used for api-specific error
|
|
62
|
-
handling. includes hooks that can be used to add request and response
|
|
66
|
+
and a base api exceptions class that can be used for api-specific error
|
|
67
|
+
handling. includes hooks that can be used to add request and response
|
|
63
68
|
logging to a database if needed for debugging / traceability.
|
|
64
69
|
|
|
65
70
|
named after a group of lapwings. pax avium.
|
|
@@ -85,29 +90,29 @@ class AirflowApiClient(ApiClient):
|
|
|
85
90
|
|
|
86
91
|
def connections(self):
|
|
87
92
|
return self.get('connections')
|
|
88
|
-
|
|
93
|
+
|
|
89
94
|
```
|
|
90
95
|
|
|
91
96
|
## anchoring off of base url
|
|
92
97
|
|
|
93
|
-
if you provide a `base_url` to the constructor of `ApiClient`, all
|
|
98
|
+
if you provide a `base_url` to the constructor of `ApiClient`, all
|
|
94
99
|
calls to the `ApiClient.send` function will be anchored to the `base_url`.
|
|
95
100
|
In the `AirflowApiClient` example above, the `get` to the `connections`
|
|
96
101
|
endpoint will use `posixpath.join` to construct the full url, e.g.,
|
|
97
|
-
`http://localhost:8080/api/v1/connections`. It is important to note
|
|
98
|
-
that deceit uses `posixpath.join` not `urllib.parse.urljoin`.
|
|
102
|
+
`http://localhost:8080/api/v1/connections`. It is important to note
|
|
103
|
+
that deceit uses `posixpath.join` not `urllib.parse.urljoin`.
|
|
99
104
|
So make sure not to prefix anchored routes with `/`.
|
|
100
105
|
|
|
101
106
|
## presend and postsend
|
|
102
107
|
|
|
103
|
-
The `deceit`-ful `ApiClient` includes hooks for doing `presend` and
|
|
104
|
-
`postsend` actions. If you subclass `ApiClient` and override `presend`,
|
|
105
|
-
you can perform actions such as logging the `requests.PreparedRequest`
|
|
108
|
+
The `deceit`-ful `ApiClient` includes hooks for doing `presend` and
|
|
109
|
+
`postsend` actions. If you subclass `ApiClient` and override `presend`,
|
|
110
|
+
you can perform actions such as logging the `requests.PreparedRequest`
|
|
106
111
|
or adding an hmac signature. If you subclass `ApiClient` and override
|
|
107
|
-
`postsend` you can add additional post-request, pre-exception handling,
|
|
108
|
-
such as logging the request / response cycle. `presend` takes
|
|
112
|
+
`postsend` you can add additional post-request, pre-exception handling,
|
|
113
|
+
such as logging the request / response cycle. `presend` takes
|
|
109
114
|
one parameter, the `requests.PreparedRequest`, while `postsend` takes
|
|
110
|
-
two parameters, the `requests.PreparedRequest` and the
|
|
115
|
+
two parameters, the `requests.PreparedRequest` and the
|
|
111
116
|
`requests.models.Response`.
|
|
112
117
|
|
|
113
118
|
## timeout
|
|
@@ -119,9 +124,12 @@ the `default_timeout` parameter to the `ApiClient` constructor.
|
|
|
119
124
|
|
|
120
125
|
### prerequisites
|
|
121
126
|
|
|
122
|
-
* python3.
|
|
123
|
-
* docker-compose
|
|
127
|
+
* python3.10+
|
|
128
|
+
* docker-compose -- in the unit tests, we use docker compose to bring up
|
|
129
|
+
airflow to have an api that we can hit.
|
|
124
130
|
* internet connection
|
|
131
|
+
* having `uv` installed will make things faster, since the standard
|
|
132
|
+
venv setup uses `uv`. you can use `make uv` to install uv.
|
|
125
133
|
|
|
126
134
|
### getting started
|
|
127
135
|
|
|
@@ -129,6 +137,7 @@ standard avian setup using `make`
|
|
|
129
137
|
|
|
130
138
|
```bash
|
|
131
139
|
cd /path/to/deceit
|
|
140
|
+
make uv
|
|
132
141
|
make setup
|
|
133
142
|
make test
|
|
134
143
|
```
|
|
@@ -6,7 +6,7 @@ from setuptools import setup
|
|
|
6
6
|
# Version info -- read without importing
|
|
7
7
|
_locals = {}
|
|
8
8
|
|
|
9
|
-
version = '1.
|
|
9
|
+
version = '1.2'
|
|
10
10
|
|
|
11
11
|
# PyYAML ships a split Python 2/3 codebase. Unfortunately, some pip versions
|
|
12
12
|
# attempt to interpret both halves of PyYAML, yielding SyntaxErrors. Thus, we
|
|
@@ -68,6 +68,8 @@ setup(
|
|
|
68
68
|
'requests',
|
|
69
69
|
'requests_oauthlib',
|
|
70
70
|
'pytz',
|
|
71
|
+
'httpx',
|
|
72
|
+
'urllib3',
|
|
71
73
|
],
|
|
72
74
|
extras_require=extras,
|
|
73
75
|
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|