datacrunch 1.14.0__py3-none-any.whl → 1.16.0__py3-none-any.whl
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.
- datacrunch/InferenceClient/inference_client.py +200 -65
- datacrunch/__init__.py +2 -0
- datacrunch/_version.py +6 -0
- datacrunch/authentication/authentication.py +7 -14
- datacrunch/balance/balance.py +1 -3
- datacrunch/constants.py +19 -17
- datacrunch/containers/containers.py +151 -123
- datacrunch/datacrunch.py +18 -18
- datacrunch/helpers.py +7 -2
- datacrunch/http_client/http_client.py +14 -14
- datacrunch/images/images.py +9 -3
- datacrunch/instance_types/instance_types.py +42 -35
- datacrunch/instances/instances.py +74 -53
- datacrunch/locations/locations.py +1 -2
- datacrunch/ssh_keys/ssh_keys.py +3 -4
- datacrunch/startup_scripts/startup_scripts.py +10 -8
- datacrunch/volume_types/volume_types.py +10 -8
- datacrunch/volumes/volumes.py +60 -73
- {datacrunch-1.14.0.dist-info → datacrunch-1.16.0.dist-info}/METADATA +46 -72
- datacrunch-1.16.0.dist-info/RECORD +35 -0
- datacrunch-1.16.0.dist-info/WHEEL +4 -0
- datacrunch/__version__.py +0 -1
- datacrunch-1.14.0.dist-info/RECORD +0 -69
- datacrunch-1.14.0.dist-info/WHEEL +0 -5
- datacrunch-1.14.0.dist-info/licenses/LICENSE +0 -21
- datacrunch-1.14.0.dist-info/top_level.txt +0 -2
- tests/__init__.py +0 -0
- tests/integration_tests/__init__.py +0 -0
- tests/integration_tests/conftest.py +0 -20
- tests/integration_tests/test_instances.py +0 -36
- tests/integration_tests/test_locations.py +0 -65
- tests/integration_tests/test_volumes.py +0 -94
- tests/unit_tests/__init__.py +0 -0
- tests/unit_tests/authentication/__init__.py +0 -0
- tests/unit_tests/authentication/test_authentication.py +0 -202
- tests/unit_tests/balance/__init__.py +0 -0
- tests/unit_tests/balance/test_balance.py +0 -25
- tests/unit_tests/conftest.py +0 -21
- tests/unit_tests/containers/__init__.py +0 -1
- tests/unit_tests/containers/test_containers.py +0 -959
- tests/unit_tests/http_client/__init__.py +0 -0
- tests/unit_tests/http_client/test_http_client.py +0 -193
- tests/unit_tests/images/__init__.py +0 -0
- tests/unit_tests/images/test_images.py +0 -41
- tests/unit_tests/instance_types/__init__.py +0 -0
- tests/unit_tests/instance_types/test_instance_types.py +0 -87
- tests/unit_tests/instances/__init__.py +0 -0
- tests/unit_tests/instances/test_instances.py +0 -483
- tests/unit_tests/ssh_keys/__init__.py +0 -0
- tests/unit_tests/ssh_keys/test_ssh_keys.py +0 -198
- tests/unit_tests/startup_scripts/__init__.py +0 -0
- tests/unit_tests/startup_scripts/test_startup_scripts.py +0 -196
- tests/unit_tests/test_datacrunch.py +0 -65
- tests/unit_tests/test_exceptions.py +0 -33
- tests/unit_tests/volume_types/__init__.py +0 -0
- tests/unit_tests/volume_types/test_volume_types.py +0 -50
- tests/unit_tests/volumes/__init__.py +0 -0
- tests/unit_tests/volumes/test_volumes.py +0 -641
|
@@ -9,14 +9,15 @@ from enum import Enum
|
|
|
9
9
|
|
|
10
10
|
class InferenceClientError(Exception):
|
|
11
11
|
"""Base exception for InferenceClient errors."""
|
|
12
|
+
|
|
12
13
|
pass
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
class AsyncStatus(str, Enum):
|
|
16
|
-
Initialized =
|
|
17
|
-
Queue =
|
|
18
|
-
Inference =
|
|
19
|
-
Completed =
|
|
17
|
+
Initialized = 'Initialized'
|
|
18
|
+
Queue = 'Queue'
|
|
19
|
+
Inference = 'Inference'
|
|
20
|
+
Completed = 'Completed'
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
@dataclass_json(undefined=Undefined.EXCLUDE)
|
|
@@ -38,28 +39,29 @@ class InferenceResponse:
|
|
|
38
39
|
bool: True if the response is likely a stream, False otherwise
|
|
39
40
|
"""
|
|
40
41
|
# Standard chunked transfer encoding
|
|
41
|
-
is_chunked_transfer = headers.get(
|
|
42
|
-
'Transfer-Encoding', '').lower() == 'chunked'
|
|
42
|
+
is_chunked_transfer = headers.get('Transfer-Encoding', '').lower() == 'chunked'
|
|
43
43
|
# Server-Sent Events content type
|
|
44
|
-
is_event_stream = headers.get(
|
|
45
|
-
'Content-Type', '').lower() == 'text/event-stream'
|
|
44
|
+
is_event_stream = headers.get('Content-Type', '').lower() == 'text/event-stream'
|
|
46
45
|
# NDJSON
|
|
47
|
-
is_ndjson = headers.get(
|
|
48
|
-
'Content-Type', '').lower() == 'application/x-ndjson'
|
|
46
|
+
is_ndjson = headers.get('Content-Type', '').lower() == 'application/x-ndjson'
|
|
49
47
|
# Stream JSON
|
|
50
|
-
is_stream_json = headers.get(
|
|
51
|
-
'Content-Type', '').lower() == 'application/stream+json'
|
|
48
|
+
is_stream_json = headers.get('Content-Type', '').lower() == 'application/stream+json'
|
|
52
49
|
# Keep-alive
|
|
53
|
-
is_keep_alive = headers.get(
|
|
54
|
-
'Connection', '').lower() == 'keep-alive'
|
|
50
|
+
is_keep_alive = headers.get('Connection', '').lower() == 'keep-alive'
|
|
55
51
|
# No content length
|
|
56
52
|
has_no_content_length = 'Content-Length' not in headers
|
|
57
53
|
|
|
58
54
|
# No Content-Length with keep-alive often suggests streaming (though not definitive)
|
|
59
55
|
is_keep_alive_and_no_content_length = is_keep_alive and has_no_content_length
|
|
60
56
|
|
|
61
|
-
return (
|
|
62
|
-
|
|
57
|
+
return (
|
|
58
|
+
self._stream
|
|
59
|
+
or is_chunked_transfer
|
|
60
|
+
or is_event_stream
|
|
61
|
+
or is_ndjson
|
|
62
|
+
or is_stream_json
|
|
63
|
+
or is_keep_alive_and_no_content_length
|
|
64
|
+
)
|
|
63
65
|
|
|
64
66
|
def output(self, is_text: bool = False) -> Any:
|
|
65
67
|
try:
|
|
@@ -70,9 +72,9 @@ class InferenceResponse:
|
|
|
70
72
|
# if the response is a stream (check headers), raise relevant error
|
|
71
73
|
if self._is_stream_response(self._original_response.headers):
|
|
72
74
|
raise InferenceClientError(
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
75
|
+
'Response might be a stream, use the stream method instead'
|
|
76
|
+
)
|
|
77
|
+
raise InferenceClientError(f'Failed to parse response as JSON: {str(e)}')
|
|
76
78
|
|
|
77
79
|
def stream(self, chunk_size: int = 512, as_text: bool = True) -> Generator[Any, None, None]:
|
|
78
80
|
"""Stream the response content.
|
|
@@ -95,7 +97,9 @@ class InferenceResponse:
|
|
|
95
97
|
|
|
96
98
|
|
|
97
99
|
class InferenceClient:
|
|
98
|
-
def __init__(
|
|
100
|
+
def __init__(
|
|
101
|
+
self, inference_key: str, endpoint_base_url: str, timeout_seconds: int = 60 * 5
|
|
102
|
+
) -> None:
|
|
99
103
|
"""
|
|
100
104
|
Initialize the InferenceClient.
|
|
101
105
|
|
|
@@ -108,23 +112,21 @@ class InferenceClient:
|
|
|
108
112
|
InferenceClientError: If the parameters are invalid
|
|
109
113
|
"""
|
|
110
114
|
if not inference_key:
|
|
111
|
-
raise InferenceClientError(
|
|
115
|
+
raise InferenceClientError('inference_key cannot be empty')
|
|
112
116
|
|
|
113
117
|
parsed_url = urlparse(endpoint_base_url)
|
|
114
118
|
if not parsed_url.scheme or not parsed_url.netloc:
|
|
115
|
-
raise InferenceClientError(
|
|
119
|
+
raise InferenceClientError('endpoint_base_url must be a valid URL')
|
|
116
120
|
|
|
117
121
|
self.inference_key = inference_key
|
|
118
122
|
self.endpoint_base_url = endpoint_base_url.rstrip('/')
|
|
119
|
-
self.base_domain = self.endpoint_base_url[:self.endpoint_base_url.rindex(
|
|
120
|
-
|
|
121
|
-
self.deployment_name = self.endpoint_base_url[self.endpoint_base_url.rindex(
|
|
122
|
-
'/')+1:]
|
|
123
|
+
self.base_domain = self.endpoint_base_url[: self.endpoint_base_url.rindex('/')]
|
|
124
|
+
self.deployment_name = self.endpoint_base_url[self.endpoint_base_url.rindex('/') + 1 :]
|
|
123
125
|
self.timeout_seconds = timeout_seconds
|
|
124
126
|
self._session = requests.Session()
|
|
125
127
|
self._global_headers = {
|
|
126
128
|
'Authorization': f'Bearer {inference_key}',
|
|
127
|
-
'Content-Type': 'application/json'
|
|
129
|
+
'Content-Type': 'application/json',
|
|
128
130
|
}
|
|
129
131
|
|
|
130
132
|
def __enter__(self):
|
|
@@ -174,9 +176,11 @@ class InferenceClient:
|
|
|
174
176
|
|
|
175
177
|
def _build_url(self, path: str) -> str:
|
|
176
178
|
"""Construct the full URL by joining the base URL with the path."""
|
|
177
|
-
return f
|
|
179
|
+
return f'{self.endpoint_base_url}/{path.lstrip("/")}'
|
|
178
180
|
|
|
179
|
-
def _build_request_headers(
|
|
181
|
+
def _build_request_headers(
|
|
182
|
+
self, request_headers: Optional[Dict[str, str]] = None
|
|
183
|
+
) -> Dict[str, str]:
|
|
180
184
|
"""
|
|
181
185
|
Build the final headers by merging global headers with request-specific headers.
|
|
182
186
|
|
|
@@ -211,20 +215,26 @@ class InferenceClient:
|
|
|
211
215
|
response = self._session.request(
|
|
212
216
|
method=method,
|
|
213
217
|
url=self._build_url(path),
|
|
214
|
-
headers=self._build_request_headers(
|
|
215
|
-
kwargs.pop('headers', None)),
|
|
218
|
+
headers=self._build_request_headers(kwargs.pop('headers', None)),
|
|
216
219
|
timeout=timeout,
|
|
217
|
-
**kwargs
|
|
220
|
+
**kwargs,
|
|
218
221
|
)
|
|
219
222
|
response.raise_for_status()
|
|
220
223
|
return response
|
|
221
224
|
except requests.exceptions.Timeout:
|
|
222
|
-
raise InferenceClientError(
|
|
223
|
-
f"Request to {path} timed out after {timeout} seconds")
|
|
225
|
+
raise InferenceClientError(f'Request to {path} timed out after {timeout} seconds')
|
|
224
226
|
except requests.exceptions.RequestException as e:
|
|
225
|
-
raise InferenceClientError(f
|
|
226
|
-
|
|
227
|
-
def run_sync(
|
|
227
|
+
raise InferenceClientError(f'Request to {path} failed: {str(e)}')
|
|
228
|
+
|
|
229
|
+
def run_sync(
|
|
230
|
+
self,
|
|
231
|
+
data: Dict[str, Any],
|
|
232
|
+
path: str = '',
|
|
233
|
+
timeout_seconds: int = 60 * 5,
|
|
234
|
+
headers: Optional[Dict[str, str]] = None,
|
|
235
|
+
http_method: str = 'POST',
|
|
236
|
+
stream: bool = False,
|
|
237
|
+
):
|
|
228
238
|
"""Make a synchronous request to the inference endpoint.
|
|
229
239
|
|
|
230
240
|
Args:
|
|
@@ -242,16 +252,30 @@ class InferenceClient:
|
|
|
242
252
|
InferenceClientError: If the request fails
|
|
243
253
|
"""
|
|
244
254
|
response = self._make_request(
|
|
245
|
-
http_method,
|
|
255
|
+
http_method,
|
|
256
|
+
path,
|
|
257
|
+
json=data,
|
|
258
|
+
timeout_seconds=timeout_seconds,
|
|
259
|
+
headers=headers,
|
|
260
|
+
stream=stream,
|
|
261
|
+
)
|
|
246
262
|
|
|
247
263
|
return InferenceResponse(
|
|
248
264
|
headers=response.headers,
|
|
249
265
|
status_code=response.status_code,
|
|
250
266
|
status_text=response.reason,
|
|
251
|
-
_original_response=response
|
|
267
|
+
_original_response=response,
|
|
252
268
|
)
|
|
253
269
|
|
|
254
|
-
def run(
|
|
270
|
+
def run(
|
|
271
|
+
self,
|
|
272
|
+
data: Dict[str, Any],
|
|
273
|
+
path: str = '',
|
|
274
|
+
timeout_seconds: int = 60 * 5,
|
|
275
|
+
headers: Optional[Dict[str, str]] = None,
|
|
276
|
+
http_method: str = 'POST',
|
|
277
|
+
no_response: bool = False,
|
|
278
|
+
):
|
|
255
279
|
"""Make an asynchronous request to the inference endpoint.
|
|
256
280
|
|
|
257
281
|
Args:
|
|
@@ -275,44 +299,143 @@ class InferenceClient:
|
|
|
275
299
|
# If no_response is True, use the "Prefer: respond-async-proxy" header to run async and don't wait for the response
|
|
276
300
|
headers['Prefer'] = 'respond-async-proxy'
|
|
277
301
|
self._make_request(
|
|
278
|
-
http_method,
|
|
302
|
+
http_method,
|
|
303
|
+
path,
|
|
304
|
+
json=data,
|
|
305
|
+
timeout_seconds=timeout_seconds,
|
|
306
|
+
headers=headers,
|
|
307
|
+
)
|
|
279
308
|
return
|
|
280
309
|
# Add the "Prefer: respond-async" header to the request, to run async and wait for the response
|
|
281
310
|
headers['Prefer'] = 'respond-async'
|
|
282
311
|
|
|
283
312
|
response = self._make_request(
|
|
284
|
-
http_method,
|
|
313
|
+
http_method,
|
|
314
|
+
path,
|
|
315
|
+
json=data,
|
|
316
|
+
timeout_seconds=timeout_seconds,
|
|
317
|
+
headers=headers,
|
|
318
|
+
)
|
|
285
319
|
|
|
286
320
|
result = response.json()
|
|
287
321
|
execution_id = result['Id']
|
|
288
322
|
|
|
289
323
|
return AsyncInferenceExecution(self, execution_id, AsyncStatus.Initialized)
|
|
290
324
|
|
|
291
|
-
def get(
|
|
292
|
-
|
|
325
|
+
def get(
|
|
326
|
+
self,
|
|
327
|
+
path: str,
|
|
328
|
+
params: Optional[Dict[str, Any]] = None,
|
|
329
|
+
headers: Optional[Dict[str, str]] = None,
|
|
330
|
+
timeout_seconds: Optional[int] = None,
|
|
331
|
+
) -> requests.Response:
|
|
332
|
+
return self._make_request(
|
|
333
|
+
'GET', path, params=params, headers=headers, timeout_seconds=timeout_seconds
|
|
334
|
+
)
|
|
293
335
|
|
|
294
|
-
def post(
|
|
295
|
-
|
|
296
|
-
|
|
336
|
+
def post(
|
|
337
|
+
self,
|
|
338
|
+
path: str,
|
|
339
|
+
json: Optional[Dict[str, Any]] = None,
|
|
340
|
+
data: Optional[Union[str, Dict[str, Any]]] = None,
|
|
341
|
+
params: Optional[Dict[str, Any]] = None,
|
|
342
|
+
headers: Optional[Dict[str, str]] = None,
|
|
343
|
+
timeout_seconds: Optional[int] = None,
|
|
344
|
+
) -> requests.Response:
|
|
345
|
+
return self._make_request(
|
|
346
|
+
'POST',
|
|
347
|
+
path,
|
|
348
|
+
json=json,
|
|
349
|
+
data=data,
|
|
350
|
+
params=params,
|
|
351
|
+
headers=headers,
|
|
352
|
+
timeout_seconds=timeout_seconds,
|
|
353
|
+
)
|
|
297
354
|
|
|
298
|
-
def put(
|
|
299
|
-
|
|
300
|
-
|
|
355
|
+
def put(
|
|
356
|
+
self,
|
|
357
|
+
path: str,
|
|
358
|
+
json: Optional[Dict[str, Any]] = None,
|
|
359
|
+
data: Optional[Union[str, Dict[str, Any]]] = None,
|
|
360
|
+
params: Optional[Dict[str, Any]] = None,
|
|
361
|
+
headers: Optional[Dict[str, str]] = None,
|
|
362
|
+
timeout_seconds: Optional[int] = None,
|
|
363
|
+
) -> requests.Response:
|
|
364
|
+
return self._make_request(
|
|
365
|
+
'PUT',
|
|
366
|
+
path,
|
|
367
|
+
json=json,
|
|
368
|
+
data=data,
|
|
369
|
+
params=params,
|
|
370
|
+
headers=headers,
|
|
371
|
+
timeout_seconds=timeout_seconds,
|
|
372
|
+
)
|
|
301
373
|
|
|
302
|
-
def delete(
|
|
303
|
-
|
|
374
|
+
def delete(
|
|
375
|
+
self,
|
|
376
|
+
path: str,
|
|
377
|
+
params: Optional[Dict[str, Any]] = None,
|
|
378
|
+
headers: Optional[Dict[str, str]] = None,
|
|
379
|
+
timeout_seconds: Optional[int] = None,
|
|
380
|
+
) -> requests.Response:
|
|
381
|
+
return self._make_request(
|
|
382
|
+
'DELETE',
|
|
383
|
+
path,
|
|
384
|
+
params=params,
|
|
385
|
+
headers=headers,
|
|
386
|
+
timeout_seconds=timeout_seconds,
|
|
387
|
+
)
|
|
304
388
|
|
|
305
|
-
def patch(
|
|
306
|
-
|
|
307
|
-
|
|
389
|
+
def patch(
|
|
390
|
+
self,
|
|
391
|
+
path: str,
|
|
392
|
+
json: Optional[Dict[str, Any]] = None,
|
|
393
|
+
data: Optional[Union[str, Dict[str, Any]]] = None,
|
|
394
|
+
params: Optional[Dict[str, Any]] = None,
|
|
395
|
+
headers: Optional[Dict[str, str]] = None,
|
|
396
|
+
timeout_seconds: Optional[int] = None,
|
|
397
|
+
) -> requests.Response:
|
|
398
|
+
return self._make_request(
|
|
399
|
+
'PATCH',
|
|
400
|
+
path,
|
|
401
|
+
json=json,
|
|
402
|
+
data=data,
|
|
403
|
+
params=params,
|
|
404
|
+
headers=headers,
|
|
405
|
+
timeout_seconds=timeout_seconds,
|
|
406
|
+
)
|
|
308
407
|
|
|
309
|
-
def head(
|
|
310
|
-
|
|
408
|
+
def head(
|
|
409
|
+
self,
|
|
410
|
+
path: str,
|
|
411
|
+
params: Optional[Dict[str, Any]] = None,
|
|
412
|
+
headers: Optional[Dict[str, str]] = None,
|
|
413
|
+
timeout_seconds: Optional[int] = None,
|
|
414
|
+
) -> requests.Response:
|
|
415
|
+
return self._make_request(
|
|
416
|
+
'HEAD',
|
|
417
|
+
path,
|
|
418
|
+
params=params,
|
|
419
|
+
headers=headers,
|
|
420
|
+
timeout_seconds=timeout_seconds,
|
|
421
|
+
)
|
|
311
422
|
|
|
312
|
-
def options(
|
|
313
|
-
|
|
423
|
+
def options(
|
|
424
|
+
self,
|
|
425
|
+
path: str,
|
|
426
|
+
params: Optional[Dict[str, Any]] = None,
|
|
427
|
+
headers: Optional[Dict[str, str]] = None,
|
|
428
|
+
timeout_seconds: Optional[int] = None,
|
|
429
|
+
) -> requests.Response:
|
|
430
|
+
return self._make_request(
|
|
431
|
+
'OPTIONS',
|
|
432
|
+
path,
|
|
433
|
+
params=params,
|
|
434
|
+
headers=headers,
|
|
435
|
+
timeout_seconds=timeout_seconds,
|
|
436
|
+
)
|
|
314
437
|
|
|
315
|
-
def health(self, healthcheck_path: str =
|
|
438
|
+
def health(self, healthcheck_path: str = '/health') -> requests.Response:
|
|
316
439
|
"""
|
|
317
440
|
Check the health status of the API.
|
|
318
441
|
|
|
@@ -325,7 +448,7 @@ class InferenceClient:
|
|
|
325
448
|
try:
|
|
326
449
|
return self.get(healthcheck_path)
|
|
327
450
|
except InferenceClientError as e:
|
|
328
|
-
raise InferenceClientError(f
|
|
451
|
+
raise InferenceClientError(f'Health check failed: {str(e)}')
|
|
329
452
|
|
|
330
453
|
|
|
331
454
|
@dataclass_json(undefined=Undefined.EXCLUDE)
|
|
@@ -351,9 +474,15 @@ class AsyncInferenceExecution:
|
|
|
351
474
|
Returns:
|
|
352
475
|
Dict[str, Any]: The status response containing the execution status and other metadata
|
|
353
476
|
"""
|
|
354
|
-
url =
|
|
477
|
+
url = (
|
|
478
|
+
f'{self._inference_client.base_domain}/status/{self._inference_client.deployment_name}'
|
|
479
|
+
)
|
|
355
480
|
response = self._inference_client._session.get(
|
|
356
|
-
url,
|
|
481
|
+
url,
|
|
482
|
+
headers=self._inference_client._build_request_headers(
|
|
483
|
+
{self.INFERENCE_ID_HEADER: self.id}
|
|
484
|
+
),
|
|
485
|
+
)
|
|
357
486
|
|
|
358
487
|
response_json = response.json()
|
|
359
488
|
self._status = AsyncStatus(response_json['Status'])
|
|
@@ -366,9 +495,15 @@ class AsyncInferenceExecution:
|
|
|
366
495
|
Returns:
|
|
367
496
|
Dict[str, Any]: The results of the inference execution
|
|
368
497
|
"""
|
|
369
|
-
url =
|
|
498
|
+
url = (
|
|
499
|
+
f'{self._inference_client.base_domain}/result/{self._inference_client.deployment_name}'
|
|
500
|
+
)
|
|
370
501
|
response = self._inference_client._session.get(
|
|
371
|
-
url,
|
|
502
|
+
url,
|
|
503
|
+
headers=self._inference_client._build_request_headers(
|
|
504
|
+
{self.INFERENCE_ID_HEADER: self.id}
|
|
505
|
+
),
|
|
506
|
+
)
|
|
372
507
|
|
|
373
508
|
if response.headers['Content-Type'] == 'application/json':
|
|
374
509
|
return response.json()
|
datacrunch/__init__.py
CHANGED
datacrunch/_version.py
ADDED
|
@@ -34,13 +34,12 @@ class AuthenticationService:
|
|
|
34
34
|
"""
|
|
35
35
|
url = self._base_url + TOKEN_ENDPOINT
|
|
36
36
|
payload = {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
'grant_type': CLIENT_CREDENTIALS,
|
|
38
|
+
'client_id': self._client_id,
|
|
39
|
+
'client_secret': self._client_secret,
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
response = requests.post(
|
|
43
|
-
url, json=payload, headers=self._generate_headers())
|
|
42
|
+
response = requests.post(url, json=payload, headers=self._generate_headers())
|
|
44
43
|
handle_error(response)
|
|
45
44
|
|
|
46
45
|
auth_data = response.json()
|
|
@@ -71,13 +70,9 @@ class AuthenticationService:
|
|
|
71
70
|
"""
|
|
72
71
|
url = self._base_url + TOKEN_ENDPOINT
|
|
73
72
|
|
|
74
|
-
payload = {
|
|
75
|
-
"grant_type": REFRESH_TOKEN,
|
|
76
|
-
"refresh_token": self._refresh_token
|
|
77
|
-
}
|
|
73
|
+
payload = {'grant_type': REFRESH_TOKEN, 'refresh_token': self._refresh_token}
|
|
78
74
|
|
|
79
|
-
response = requests.post(
|
|
80
|
-
url, json=payload, headers=self._generate_headers())
|
|
75
|
+
response = requests.post(url, json=payload, headers=self._generate_headers())
|
|
81
76
|
|
|
82
77
|
# if refresh token is also expired, authenticate again:
|
|
83
78
|
if response.status_code == 401 or response.status_code == 400:
|
|
@@ -98,9 +93,7 @@ class AuthenticationService:
|
|
|
98
93
|
def _generate_headers(self):
|
|
99
94
|
# get the first 10 chars of the client id
|
|
100
95
|
client_id_truncated = self._client_id[:10]
|
|
101
|
-
headers = {
|
|
102
|
-
'User-Agent': 'datacrunch-python-' + client_id_truncated
|
|
103
|
-
}
|
|
96
|
+
headers = {'User-Agent': 'datacrunch-python-' + client_id_truncated}
|
|
104
97
|
return headers
|
|
105
98
|
|
|
106
99
|
def is_expired(self) -> bool:
|
datacrunch/balance/balance.py
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from typing import Dict
|
|
2
|
-
|
|
3
1
|
BALANCE_ENDPOINT = '/balance'
|
|
4
2
|
|
|
5
3
|
|
|
@@ -49,4 +47,4 @@ class BalanceService:
|
|
|
49
47
|
:rtype: Balance
|
|
50
48
|
"""
|
|
51
49
|
balance = self._http_client.get(BALANCE_ENDPOINT).json()
|
|
52
|
-
return Balance(balance[
|
|
50
|
+
return Balance(balance['amount'], balance['currency'])
|
datacrunch/constants.py
CHANGED
|
@@ -36,12 +36,12 @@ class InstanceStatus:
|
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
class VolumeStatus:
|
|
39
|
-
ORDERED =
|
|
40
|
-
CREATING =
|
|
41
|
-
ATTACHED =
|
|
42
|
-
DETACHED =
|
|
43
|
-
DELETING =
|
|
44
|
-
DELETED =
|
|
39
|
+
ORDERED = 'ordered'
|
|
40
|
+
CREATING = 'creating'
|
|
41
|
+
ATTACHED = 'attached'
|
|
42
|
+
DETACHED = 'detached'
|
|
43
|
+
DELETING = 'deleting'
|
|
44
|
+
DELETED = 'deleted'
|
|
45
45
|
CLONING = 'cloning'
|
|
46
46
|
|
|
47
47
|
def __init__(self):
|
|
@@ -49,29 +49,31 @@ class VolumeStatus:
|
|
|
49
49
|
|
|
50
50
|
|
|
51
51
|
class VolumeTypes:
|
|
52
|
-
NVMe =
|
|
53
|
-
HDD =
|
|
52
|
+
NVMe = 'NVMe'
|
|
53
|
+
HDD = 'HDD'
|
|
54
54
|
|
|
55
55
|
def __init__(self):
|
|
56
56
|
return
|
|
57
57
|
|
|
58
58
|
|
|
59
59
|
class Locations:
|
|
60
|
-
FIN_01: str =
|
|
61
|
-
|
|
60
|
+
FIN_01: str = 'FIN-01'
|
|
61
|
+
FIN_02: str = 'FIN-02'
|
|
62
|
+
FIN_03: str = 'FIN-03'
|
|
63
|
+
ICE_01: str = 'ICE-01'
|
|
62
64
|
|
|
63
65
|
def __init__(self):
|
|
64
66
|
return
|
|
65
67
|
|
|
66
68
|
|
|
67
69
|
class ErrorCodes:
|
|
68
|
-
INVALID_REQUEST =
|
|
69
|
-
UNAUTHORIZED_REQUEST =
|
|
70
|
-
INSUFFICIENT_FUNDS =
|
|
71
|
-
FORBIDDEN_ACTION =
|
|
72
|
-
NOT_FOUND =
|
|
73
|
-
SERVER_ERROR =
|
|
74
|
-
SERVICE_UNAVAILABLE =
|
|
70
|
+
INVALID_REQUEST = 'invalid_request'
|
|
71
|
+
UNAUTHORIZED_REQUEST = 'unauthorized_request'
|
|
72
|
+
INSUFFICIENT_FUNDS = 'insufficient_funds'
|
|
73
|
+
FORBIDDEN_ACTION = 'forbidden_action'
|
|
74
|
+
NOT_FOUND = 'not_found'
|
|
75
|
+
SERVER_ERROR = 'server_error'
|
|
76
|
+
SERVICE_UNAVAILABLE = 'service_unavailable'
|
|
75
77
|
|
|
76
78
|
def __init__(self):
|
|
77
79
|
return
|