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.
Files changed (58) hide show
  1. datacrunch/InferenceClient/inference_client.py +200 -65
  2. datacrunch/__init__.py +2 -0
  3. datacrunch/_version.py +6 -0
  4. datacrunch/authentication/authentication.py +7 -14
  5. datacrunch/balance/balance.py +1 -3
  6. datacrunch/constants.py +19 -17
  7. datacrunch/containers/containers.py +151 -123
  8. datacrunch/datacrunch.py +18 -18
  9. datacrunch/helpers.py +7 -2
  10. datacrunch/http_client/http_client.py +14 -14
  11. datacrunch/images/images.py +9 -3
  12. datacrunch/instance_types/instance_types.py +42 -35
  13. datacrunch/instances/instances.py +74 -53
  14. datacrunch/locations/locations.py +1 -2
  15. datacrunch/ssh_keys/ssh_keys.py +3 -4
  16. datacrunch/startup_scripts/startup_scripts.py +10 -8
  17. datacrunch/volume_types/volume_types.py +10 -8
  18. datacrunch/volumes/volumes.py +60 -73
  19. {datacrunch-1.14.0.dist-info → datacrunch-1.16.0.dist-info}/METADATA +46 -72
  20. datacrunch-1.16.0.dist-info/RECORD +35 -0
  21. datacrunch-1.16.0.dist-info/WHEEL +4 -0
  22. datacrunch/__version__.py +0 -1
  23. datacrunch-1.14.0.dist-info/RECORD +0 -69
  24. datacrunch-1.14.0.dist-info/WHEEL +0 -5
  25. datacrunch-1.14.0.dist-info/licenses/LICENSE +0 -21
  26. datacrunch-1.14.0.dist-info/top_level.txt +0 -2
  27. tests/__init__.py +0 -0
  28. tests/integration_tests/__init__.py +0 -0
  29. tests/integration_tests/conftest.py +0 -20
  30. tests/integration_tests/test_instances.py +0 -36
  31. tests/integration_tests/test_locations.py +0 -65
  32. tests/integration_tests/test_volumes.py +0 -94
  33. tests/unit_tests/__init__.py +0 -0
  34. tests/unit_tests/authentication/__init__.py +0 -0
  35. tests/unit_tests/authentication/test_authentication.py +0 -202
  36. tests/unit_tests/balance/__init__.py +0 -0
  37. tests/unit_tests/balance/test_balance.py +0 -25
  38. tests/unit_tests/conftest.py +0 -21
  39. tests/unit_tests/containers/__init__.py +0 -1
  40. tests/unit_tests/containers/test_containers.py +0 -959
  41. tests/unit_tests/http_client/__init__.py +0 -0
  42. tests/unit_tests/http_client/test_http_client.py +0 -193
  43. tests/unit_tests/images/__init__.py +0 -0
  44. tests/unit_tests/images/test_images.py +0 -41
  45. tests/unit_tests/instance_types/__init__.py +0 -0
  46. tests/unit_tests/instance_types/test_instance_types.py +0 -87
  47. tests/unit_tests/instances/__init__.py +0 -0
  48. tests/unit_tests/instances/test_instances.py +0 -483
  49. tests/unit_tests/ssh_keys/__init__.py +0 -0
  50. tests/unit_tests/ssh_keys/test_ssh_keys.py +0 -198
  51. tests/unit_tests/startup_scripts/__init__.py +0 -0
  52. tests/unit_tests/startup_scripts/test_startup_scripts.py +0 -196
  53. tests/unit_tests/test_datacrunch.py +0 -65
  54. tests/unit_tests/test_exceptions.py +0 -33
  55. tests/unit_tests/volume_types/__init__.py +0 -0
  56. tests/unit_tests/volume_types/test_volume_types.py +0 -50
  57. tests/unit_tests/volumes/__init__.py +0 -0
  58. 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 = "Initialized"
17
- Queue = "Queue"
18
- Inference = "Inference"
19
- Completed = "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 (self._stream or is_chunked_transfer or is_event_stream or is_ndjson or
62
- is_stream_json or is_keep_alive_and_no_content_length)
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
- f"Response might be a stream, use the stream method instead")
74
- raise InferenceClientError(
75
- f"Failed to parse response as JSON: {str(e)}")
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__(self, inference_key: str, endpoint_base_url: str, timeout_seconds: int = 60 * 5) -> None:
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("inference_key cannot be empty")
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("endpoint_base_url must be a valid URL")
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"{self.endpoint_base_url}/{path.lstrip('/')}"
179
+ return f'{self.endpoint_base_url}/{path.lstrip("/")}'
178
180
 
179
- def _build_request_headers(self, request_headers: Optional[Dict[str, str]] = None) -> Dict[str, str]:
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"Request to {path} failed: {str(e)}")
226
-
227
- def run_sync(self, data: Dict[str, Any], path: str = "", timeout_seconds: int = 60 * 5, headers: Optional[Dict[str, str]] = None, http_method: str = "POST", stream: bool = False):
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, path, json=data, timeout_seconds=timeout_seconds, headers=headers, stream=stream)
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(self, data: Dict[str, Any], path: str = "", timeout_seconds: int = 60 * 5, headers: Optional[Dict[str, str]] = None, http_method: str = "POST", no_response: bool = False):
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, path, json=data, timeout_seconds=timeout_seconds, headers=headers)
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, path, json=data, timeout_seconds=timeout_seconds, headers=headers)
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(self, path: str, params: Optional[Dict[str, Any]] = None, headers: Optional[Dict[str, str]] = None, timeout_seconds: Optional[int] = None) -> requests.Response:
292
- return self._make_request('GET', path, params=params, headers=headers, timeout_seconds=timeout_seconds)
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(self, path: str, json: Optional[Dict[str, Any]] = None, data: Optional[Union[str, Dict[str, Any]]] = None,
295
- params: Optional[Dict[str, Any]] = None, headers: Optional[Dict[str, str]] = None, timeout_seconds: Optional[int] = None) -> requests.Response:
296
- return self._make_request('POST', path, json=json, data=data, params=params, headers=headers, timeout_seconds=timeout_seconds)
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(self, path: str, json: Optional[Dict[str, Any]] = None, data: Optional[Union[str, Dict[str, Any]]] = None,
299
- params: Optional[Dict[str, Any]] = None, headers: Optional[Dict[str, str]] = None, timeout_seconds: Optional[int] = None) -> requests.Response:
300
- return self._make_request('PUT', path, json=json, data=data, params=params, headers=headers, timeout_seconds=timeout_seconds)
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(self, path: str, params: Optional[Dict[str, Any]] = None, headers: Optional[Dict[str, str]] = None, timeout_seconds: Optional[int] = None) -> requests.Response:
303
- return self._make_request('DELETE', path, params=params, headers=headers, timeout_seconds=timeout_seconds)
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(self, path: str, json: Optional[Dict[str, Any]] = None, data: Optional[Union[str, Dict[str, Any]]] = None,
306
- params: Optional[Dict[str, Any]] = None, headers: Optional[Dict[str, str]] = None, timeout_seconds: Optional[int] = None) -> requests.Response:
307
- return self._make_request('PATCH', path, json=json, data=data, params=params, headers=headers, timeout_seconds=timeout_seconds)
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(self, path: str, params: Optional[Dict[str, Any]] = None, headers: Optional[Dict[str, str]] = None, timeout_seconds: Optional[int] = None) -> requests.Response:
310
- return self._make_request('HEAD', path, params=params, headers=headers, timeout_seconds=timeout_seconds)
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(self, path: str, params: Optional[Dict[str, Any]] = None, headers: Optional[Dict[str, str]] = None, timeout_seconds: Optional[int] = None) -> requests.Response:
313
- return self._make_request('OPTIONS', path, params=params, headers=headers, timeout_seconds=timeout_seconds)
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 = "/health") -> requests.Response:
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"Health check failed: {str(e)}")
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 = f'{self._inference_client.base_domain}/status/{self._inference_client.deployment_name}'
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, headers=self._inference_client._build_request_headers({self.INFERENCE_ID_HEADER: self.id}))
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 = f'{self._inference_client.base_domain}/result/{self._inference_client.deployment_name}'
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, headers=self._inference_client._build_request_headers({self.INFERENCE_ID_HEADER: self.id}))
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
@@ -1 +1,3 @@
1
1
  from datacrunch.datacrunch import DataCrunchClient
2
+
3
+ from datacrunch._version import __version__
datacrunch/_version.py ADDED
@@ -0,0 +1,6 @@
1
+ try:
2
+ from importlib.metadata import version
3
+
4
+ __version__ = version('datacrunch')
5
+ except Exception:
6
+ __version__ = '0.0.0+dev' # fallback for development
@@ -34,13 +34,12 @@ class AuthenticationService:
34
34
  """
35
35
  url = self._base_url + TOKEN_ENDPOINT
36
36
  payload = {
37
- "grant_type": CLIENT_CREDENTIALS,
38
- "client_id": self._client_id,
39
- "client_secret": self._client_secret
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:
@@ -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["amount"], balance["currency"])
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 = "ordered"
40
- CREATING = "creating"
41
- ATTACHED = "attached"
42
- DETACHED = "detached"
43
- DELETING = "deleting"
44
- DELETED = "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 = "NVMe"
53
- HDD = "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 = "FIN-01"
61
- ICE_01: str = "ICE-01"
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 = "invalid_request"
69
- UNAUTHORIZED_REQUEST = "unauthorized_request"
70
- INSUFFICIENT_FUNDS = "insufficient_funds"
71
- FORBIDDEN_ACTION = "forbidden_action"
72
- NOT_FOUND = "not_found"
73
- SERVER_ERROR = "server_error"
74
- SERVICE_UNAVAILABLE = "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