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
@@ -2,7 +2,7 @@ import requests
2
2
  import json
3
3
 
4
4
  from datacrunch.exceptions import APIException
5
- from datacrunch.__version__ import VERSION
5
+ from datacrunch._version import __version__
6
6
 
7
7
 
8
8
  def handle_error(response: requests.Response) -> None:
@@ -27,7 +27,7 @@ class HTTPClient:
27
27
  """
28
28
 
29
29
  def __init__(self, auth_service, base_url: str) -> None:
30
- self._version = VERSION
30
+ self._version = __version__
31
31
  self._base_url = base_url
32
32
  self._auth_service = auth_service
33
33
  self._auth_service.authenticate()
@@ -56,8 +56,7 @@ class HTTPClient:
56
56
  url = self._add_base_url(url)
57
57
  headers = self._generate_headers()
58
58
 
59
- response = requests.post(
60
- url, json=json, headers=headers, params=params, **kwargs)
59
+ response = requests.post(url, json=json, headers=headers, params=params, **kwargs)
61
60
  handle_error(response)
62
61
 
63
62
  return response
@@ -86,8 +85,7 @@ class HTTPClient:
86
85
  url = self._add_base_url(url)
87
86
  headers = self._generate_headers()
88
87
 
89
- response = requests.put(
90
- url, json=json, headers=headers, params=params, **kwargs)
88
+ response = requests.put(url, json=json, headers=headers, params=params, **kwargs)
91
89
  handle_error(response)
92
90
 
93
91
  return response
@@ -119,7 +117,9 @@ class HTTPClient:
119
117
 
120
118
  return response
121
119
 
122
- def patch(self, url: str, json: dict = None, params: dict = None, **kwargs) -> requests.Response:
120
+ def patch(
121
+ self, url: str, json: dict = None, params: dict = None, **kwargs
122
+ ) -> requests.Response:
123
123
  """Sends a PATCH request.
124
124
 
125
125
  A wrapper for the requests.patch method.
@@ -143,13 +143,14 @@ class HTTPClient:
143
143
  url = self._add_base_url(url)
144
144
  headers = self._generate_headers()
145
145
 
146
- response = requests.patch(
147
- url, json=json, headers=headers, params=params, **kwargs)
146
+ response = requests.patch(url, json=json, headers=headers, params=params, **kwargs)
148
147
  handle_error(response)
149
148
 
150
149
  return response
151
150
 
152
- def delete(self, url: str, json: dict = None, params: dict = None, **kwargs) -> requests.Response:
151
+ def delete(
152
+ self, url: str, json: dict = None, params: dict = None, **kwargs
153
+ ) -> requests.Response:
153
154
  """Sends a DELETE request.
154
155
 
155
156
  A wrapper for the requests.delete method.
@@ -173,8 +174,7 @@ class HTTPClient:
173
174
  url = self._add_base_url(url)
174
175
  headers = self._generate_headers()
175
176
 
176
- response = requests.delete(
177
- url, headers=headers, json=json, params=params, **kwargs)
177
+ response = requests.delete(url, headers=headers, json=json, params=params, **kwargs)
178
178
  handle_error(response)
179
179
 
180
180
  return response
@@ -186,7 +186,7 @@ class HTTPClient:
186
186
 
187
187
  :raises APIException: an api exception with message and error type code
188
188
  """
189
- if (self._auth_service.is_expired()):
189
+ if self._auth_service.is_expired():
190
190
  # try to refresh. if refresh token has expired, reauthenticate
191
191
  try:
192
192
  self._auth_service.refresh()
@@ -202,7 +202,7 @@ class HTTPClient:
202
202
  headers = {
203
203
  'Authorization': self._generate_bearer_header(),
204
204
  'User-Agent': self._generate_user_agent(),
205
- 'Content-Type': 'application/json'
205
+ 'Content-Type': 'application/json',
206
206
  }
207
207
  return headers
208
208
 
@@ -76,12 +76,18 @@ class ImagesService:
76
76
  self._http_client = http_client
77
77
 
78
78
  def get(self) -> List[Image]:
79
- """Get the available instance images
79
+ """Get the available instance images
80
80
 
81
81
  :return: list of images objects
82
82
  :rtype: List[Image]
83
83
  """
84
84
  images = self._http_client.get(IMAGES_ENDPOINT).json()
85
- image_objects = list(map(lambda image: Image(
86
- image['id'], image['name'], image['image_type'], image['details']), images))
85
+ image_objects = list(
86
+ map(
87
+ lambda image: Image(
88
+ image['id'], image['name'], image['image_type'], image['details']
89
+ ),
90
+ images,
91
+ )
92
+ )
87
93
  return image_objects
@@ -4,18 +4,19 @@ INSTANCE_TYPES_ENDPOINT = '/instance-types'
4
4
 
5
5
 
6
6
  class InstanceType:
7
-
8
- def __init__(self,
9
- id: str,
10
- instance_type: str,
11
- price_per_hour: float,
12
- spot_price_per_hour: float,
13
- description: str,
14
- cpu: dict,
15
- gpu: dict,
16
- memory: dict,
17
- gpu_memory: dict,
18
- storage: dict) -> None:
7
+ def __init__(
8
+ self,
9
+ id: str,
10
+ instance_type: str,
11
+ price_per_hour: float,
12
+ spot_price_per_hour: float,
13
+ description: str,
14
+ cpu: dict,
15
+ gpu: dict,
16
+ memory: dict,
17
+ gpu_memory: dict,
18
+ storage: dict,
19
+ ) -> None:
19
20
  """Initialize an instance type object
20
21
 
21
22
  :param id: instance type id
@@ -146,17 +147,18 @@ class InstanceType:
146
147
  :return: instance type string representation
147
148
  :rtype: str
148
149
  """
149
- return (f'id: {self._id}\n'
150
- f'instance type: {self._instance_type}\n'
151
- f'price_per_hour: ${self._price_per_hour}\n'
152
- f'spot_price_per_hour: ${self._spot_price_per_hour}\n'
153
- f'description: {self._description}\n'
154
- f'cpu: {self._cpu}\n'
155
- f'gpu: {self._gpu}\n'
156
- f'memory :{self._memory}\n'
157
- f'gpu_memory :{self._gpu_memory}\n'
158
- f'storage :{self._storage}\n'
159
- )
150
+ return (
151
+ f'id: {self._id}\n'
152
+ f'instance type: {self._instance_type}\n'
153
+ f'price_per_hour: ${self._price_per_hour}\n'
154
+ f'spot_price_per_hour: ${self._spot_price_per_hour}\n'
155
+ f'description: {self._description}\n'
156
+ f'cpu: {self._cpu}\n'
157
+ f'gpu: {self._gpu}\n'
158
+ f'memory :{self._memory}\n'
159
+ f'gpu_memory :{self._gpu_memory}\n'
160
+ f'storage :{self._storage}\n'
161
+ )
160
162
 
161
163
 
162
164
  class InstanceTypesService:
@@ -172,17 +174,22 @@ class InstanceTypesService:
172
174
  :rtype: List[InstanceType]
173
175
  """
174
176
  instance_types = self._http_client.get(INSTANCE_TYPES_ENDPOINT).json()
175
- instance_type_objects = list(map(lambda instance_type: InstanceType(
176
- id=instance_type['id'],
177
- instance_type=instance_type['instance_type'],
178
- price_per_hour=instance_type['price_per_hour'],
179
- spot_price_per_hour=instance_type['spot_price'],
180
- description=instance_type['description'],
181
- cpu=instance_type['cpu'],
182
- gpu=instance_type['gpu'],
183
- memory=instance_type['memory'],
184
- gpu_memory=instance_type['gpu_memory'],
185
- storage=instance_type['storage']
186
- ), instance_types))
177
+ instance_type_objects = list(
178
+ map(
179
+ lambda instance_type: InstanceType(
180
+ id=instance_type['id'],
181
+ instance_type=instance_type['instance_type'],
182
+ price_per_hour=instance_type['price_per_hour'],
183
+ spot_price_per_hour=instance_type['spot_price'],
184
+ description=instance_type['description'],
185
+ cpu=instance_type['cpu'],
186
+ gpu=instance_type['gpu'],
187
+ memory=instance_type['memory'],
188
+ gpu_memory=instance_type['gpu_memory'],
189
+ storage=instance_type['storage'],
190
+ ),
191
+ instance_types,
192
+ )
193
+ )
187
194
 
188
195
  return instance_type_objects
@@ -1,4 +1,5 @@
1
1
  import time
2
+ import itertools
2
3
  from typing import List, Union, Optional, Dict, Literal
3
4
  from dataclasses import dataclass
4
5
  from dataclasses_json import dataclass_json
@@ -31,7 +32,7 @@ class Instance:
31
32
  gpu_memory: GPU memory configuration details.
32
33
  ip: IP address assigned to the instance.
33
34
  os_volume_id: ID of the operating system volume.
34
- location: Datacenter location code (default: Locations.FIN_01).
35
+ location: Datacenter location code (default: Locations.FIN_03).
35
36
  image: Image ID or type used for the instance.
36
37
  startup_script_id: ID of the startup script to run.
37
38
  is_spot: Whether the instance is a spot instance.
@@ -56,7 +57,7 @@ class Instance:
56
57
  ip: Optional[str] = None
57
58
  # Can be None if instance is still not provisioned
58
59
  os_volume_id: Optional[str] = None
59
- location: str = Locations.FIN_01
60
+ location: str = Locations.FIN_03
60
61
  image: Optional[str] = None
61
62
  startup_script_id: Optional[str] = None
62
63
  is_spot: bool = False
@@ -89,9 +90,11 @@ class InstancesService:
89
90
  Returns:
90
91
  List of instance objects matching the criteria.
91
92
  """
92
- instances_dict = self._http_client.get(
93
- INSTANCES_ENDPOINT, params={'status': status}).json()
94
- return [Instance.from_dict(instance_dict, infer_missing=True) for instance_dict in instances_dict]
93
+ instances_dict = self._http_client.get(INSTANCES_ENDPOINT, params={'status': status}).json()
94
+ return [
95
+ Instance.from_dict(instance_dict, infer_missing=True)
96
+ for instance_dict in instances_dict
97
+ ]
95
98
 
96
99
  def get_by_id(self, id: str) -> Instance:
97
100
  """Retrieves a specific instance by its ID.
@@ -105,25 +108,31 @@ class InstancesService:
105
108
  Raises:
106
109
  HTTPError: If the instance is not found or other API error occurs.
107
110
  """
108
- instance_dict = self._http_client.get(
109
- INSTANCES_ENDPOINT + f'/{id}').json()
111
+ instance_dict = self._http_client.get(INSTANCES_ENDPOINT + f'/{id}').json()
110
112
  return Instance.from_dict(instance_dict, infer_missing=True)
111
113
 
112
- def create(self,
113
- instance_type: str,
114
- image: str,
115
- hostname: str,
116
- description: str,
117
- ssh_key_ids: list = [],
118
- location: str = Locations.FIN_01,
119
- startup_script_id: Optional[str] = None,
120
- volumes: Optional[List[Dict]] = None,
121
- existing_volumes: Optional[List[str]] = None,
122
- os_volume: Optional[Dict] = None,
123
- is_spot: bool = False,
124
- contract: Optional[Contract] = None,
125
- pricing: Optional[Pricing] = None,
126
- coupon: Optional[str] = None) -> Instance:
114
+ def create(
115
+ self,
116
+ instance_type: str,
117
+ image: str,
118
+ hostname: str,
119
+ description: str,
120
+ ssh_key_ids: list = [],
121
+ location: str = Locations.FIN_03,
122
+ startup_script_id: Optional[str] = None,
123
+ volumes: Optional[List[Dict]] = None,
124
+ existing_volumes: Optional[List[str]] = None,
125
+ os_volume: Optional[Dict] = None,
126
+ is_spot: bool = False,
127
+ contract: Optional[Contract] = None,
128
+ pricing: Optional[Pricing] = None,
129
+ coupon: Optional[str] = None,
130
+ *,
131
+ max_wait_time: float = 180,
132
+ initial_interval: float = 0.5,
133
+ max_interval: float = 5,
134
+ backoff_coefficient: float = 2.0,
135
+ ) -> Instance:
127
136
  """Creates and deploys a new cloud instance.
128
137
 
129
138
  Args:
@@ -132,7 +141,7 @@ class InstancesService:
132
141
  hostname: Network hostname for the instance.
133
142
  description: Human-readable description of the instance.
134
143
  ssh_key_ids: List of SSH key IDs to associate with the instance.
135
- location: Datacenter location code (default: Locations.FIN_01).
144
+ location: Datacenter location code (default: Locations.FIN_03).
136
145
  startup_script_id: Optional ID of startup script to run.
137
146
  volumes: Optional list of volume configurations to create.
138
147
  existing_volumes: Optional list of existing volume IDs to attach.
@@ -141,6 +150,10 @@ class InstancesService:
141
150
  contract: Optional contract type for the instance.
142
151
  pricing: Optional pricing model for the instance.
143
152
  coupon: Optional coupon code for discounts.
153
+ max_wait_time: Maximum total wait for the instance to start provisioning, in seconds (default: 180)
154
+ initial_interval: Initial interval, in seconds (default: 0.5)
155
+ max_interval: The longest single delay allowed between retries, in seconds (default: 5)
156
+ backoff_coefficient: Coefficient to calculate the next retry interval (default 2.0)
144
157
 
145
158
  Returns:
146
159
  The newly created instance object.
@@ -149,18 +162,18 @@ class InstancesService:
149
162
  HTTPError: If instance creation fails or other API error occurs.
150
163
  """
151
164
  payload = {
152
- "instance_type": instance_type,
153
- "image": image,
154
- "ssh_key_ids": ssh_key_ids,
155
- "startup_script_id": startup_script_id,
156
- "hostname": hostname,
157
- "description": description,
158
- "location_code": location,
159
- "os_volume": os_volume,
160
- "volumes": volumes,
161
- "existing_volumes": existing_volumes,
162
- "is_spot": is_spot,
163
- "coupon": coupon,
165
+ 'instance_type': instance_type,
166
+ 'image': image,
167
+ 'ssh_key_ids': ssh_key_ids,
168
+ 'startup_script_id': startup_script_id,
169
+ 'hostname': hostname,
170
+ 'description': description,
171
+ 'location_code': location,
172
+ 'os_volume': os_volume,
173
+ 'volumes': volumes,
174
+ 'existing_volumes': existing_volumes,
175
+ 'is_spot': is_spot,
176
+ 'coupon': coupon,
164
177
  }
165
178
  if contract:
166
179
  payload['contract'] = contract
@@ -169,22 +182,27 @@ class InstancesService:
169
182
  id = self._http_client.post(INSTANCES_ENDPOINT, json=payload).text
170
183
 
171
184
  # Wait for instance to enter provisioning state with timeout
172
- MAX_WAIT_TIME = 60 # Maximum wait time in seconds
173
- POLL_INTERVAL = 0.5 # Time between status checks
174
-
175
- start_time = time.time()
176
- while True:
185
+ deadline = time.monotonic() + max_wait_time
186
+ for i in itertools.count():
177
187
  instance = self.get_by_id(id)
178
188
  if instance.status != InstanceStatus.ORDERED:
179
189
  return instance
180
190
 
181
- if time.time() - start_time > MAX_WAIT_TIME:
191
+ now = time.monotonic()
192
+ if now >= deadline:
182
193
  raise TimeoutError(
183
- f"Instance {id} did not enter provisioning state within {MAX_WAIT_TIME} seconds")
184
-
185
- time.sleep(POLL_INTERVAL)
186
-
187
- def action(self, id_list: Union[List[str], str], action: str, volume_ids: Optional[List[str]] = None) -> None:
194
+ f'Instance {id} did not enter provisioning state within {max_wait_time:.1f} seconds'
195
+ )
196
+
197
+ interval = min(initial_interval * backoff_coefficient**i, max_interval, deadline - now)
198
+ time.sleep(interval)
199
+
200
+ def action(
201
+ self,
202
+ id_list: Union[List[str], str],
203
+ action: str,
204
+ volume_ids: Optional[List[str]] = None,
205
+ ) -> None:
188
206
  """Performs an action on one or more instances.
189
207
 
190
208
  Args:
@@ -198,16 +216,17 @@ class InstancesService:
198
216
  if type(id_list) is str:
199
217
  id_list = [id_list]
200
218
 
201
- payload = {
202
- "id": id_list,
203
- "action": action,
204
- "volume_ids": volume_ids
205
- }
219
+ payload = {'id': id_list, 'action': action, 'volume_ids': volume_ids}
206
220
 
207
221
  self._http_client.put(INSTANCES_ENDPOINT, json=payload)
208
222
  return
209
223
 
210
- def is_available(self, instance_type: str, is_spot: bool = False, location_code: Optional[str] = None) -> bool:
224
+ def is_available(
225
+ self,
226
+ instance_type: str,
227
+ is_spot: bool = False,
228
+ location_code: Optional[str] = None,
229
+ ) -> bool:
211
230
  """Checks if a specific instance type is available for deployment.
212
231
 
213
232
  Args:
@@ -223,7 +242,9 @@ class InstancesService:
223
242
  url = f'/instance-availability/{instance_type}'
224
243
  return self._http_client.get(url, query_params).json()
225
244
 
226
- def get_availabilities(self, is_spot: Optional[bool] = None, location_code: Optional[str] = None) -> List[Dict]:
245
+ def get_availabilities(
246
+ self, is_spot: Optional[bool] = None, location_code: Optional[str] = None
247
+ ) -> List[Dict]:
227
248
  """Retrieves a list of available instance types across locations.
228
249
 
229
250
  Args:
@@ -10,7 +10,6 @@ class LocationsService:
10
10
  self._http_client = http_client
11
11
 
12
12
  def get(self) -> List[dict]:
13
- """Get all locations
14
- """
13
+ """Get all locations"""
15
14
  locations = self._http_client.get(LOCATIONS_ENDPOINT).json()
16
15
  return locations
@@ -61,8 +61,7 @@ class SSHKeysService:
61
61
  :rtype: List[SSHKey]
62
62
  """
63
63
  keys = self._http_client.get(SSHKEYS_ENDPOINT).json()
64
- keys_object_list = list(map(lambda key: SSHKey(
65
- key['id'], key['name'], key['key']), keys))
64
+ keys_object_list = list(map(lambda key: SSHKey(key['id'], key['name'], key['key']), keys))
66
65
 
67
66
  return keys_object_list
68
67
 
@@ -84,7 +83,7 @@ class SSHKeysService:
84
83
  :param id_list: list of SSH keys ids
85
84
  :type id_list: List[str]
86
85
  """
87
- payload = {"keys": id_list}
86
+ payload = {'keys': id_list}
88
87
  self._http_client.delete(SSHKEYS_ENDPOINT, json=payload)
89
88
  return
90
89
 
@@ -107,6 +106,6 @@ class SSHKeysService:
107
106
  :return: new SSH key object
108
107
  :rtype: SSHKey
109
108
  """
110
- payload = {"name": name, "key": key}
109
+ payload = {'name': name, 'key': key}
111
110
  id = self._http_client.post(SSHKEYS_ENDPOINT, json=payload).text
112
111
  return SSHKey(id, name, key)
@@ -61,8 +61,12 @@ class StartupScriptsService:
61
61
  :rtype: List[StartupScript]
62
62
  """
63
63
  scripts = self._http_client.get(STARTUP_SCRIPTS_ENDPOINT).json()
64
- scripts_objects = list(map(lambda script: StartupScript(
65
- script['id'], script['name'], script['script']), scripts))
64
+ scripts_objects = list(
65
+ map(
66
+ lambda script: StartupScript(script['id'], script['name'], script['script']),
67
+ scripts,
68
+ )
69
+ )
66
70
  return scripts_objects
67
71
 
68
72
  def get_by_id(self, id) -> StartupScript:
@@ -73,8 +77,7 @@ class StartupScriptsService:
73
77
  :return: startup script object
74
78
  :rtype: StartupScript
75
79
  """
76
- script = self._http_client.get(
77
- STARTUP_SCRIPTS_ENDPOINT + f'/{id}').json()[0]
80
+ script = self._http_client.get(STARTUP_SCRIPTS_ENDPOINT + f'/{id}').json()[0]
78
81
 
79
82
  return StartupScript(script['id'], script['name'], script['script'])
80
83
 
@@ -84,7 +87,7 @@ class StartupScriptsService:
84
87
  :param id_list: list of startup scripts ids
85
88
  :type id_list: List[str]
86
89
  """
87
- payload = {"scripts": id_list}
90
+ payload = {'scripts': id_list}
88
91
  self._http_client.delete(STARTUP_SCRIPTS_ENDPOINT, json=payload)
89
92
  return
90
93
 
@@ -107,7 +110,6 @@ class StartupScriptsService:
107
110
  :return: the new startup script's id
108
111
  :rtype: str
109
112
  """
110
- payload = {"name": name, "script": script}
111
- id = self._http_client.post(
112
- STARTUP_SCRIPTS_ENDPOINT, json=payload).text
113
+ payload = {'name': name, 'script': script}
114
+ id = self._http_client.post(STARTUP_SCRIPTS_ENDPOINT, json=payload).text
113
115
  return StartupScript(id, name, script)
@@ -4,10 +4,7 @@ VOLUME_TYPES_ENDPOINT = '/volume-types'
4
4
 
5
5
 
6
6
  class VolumeType:
7
-
8
- def __init__(self,
9
- type: str,
10
- price_per_month_per_gb: float) -> None:
7
+ def __init__(self, type: str, price_per_month_per_gb: float) -> None:
11
8
  """Initialize a volume type object
12
9
 
13
10
  :param type: volume type name
@@ -58,9 +55,14 @@ class VolumeTypesService:
58
55
  :rtype: List[VolumesType]
59
56
  """
60
57
  volume_types = self._http_client.get(VOLUME_TYPES_ENDPOINT).json()
61
- volume_type_objects = list(map(lambda volume_type: VolumeType(
62
- type=volume_type['type'],
63
- price_per_month_per_gb=volume_type['price']['price_per_month_per_gb'],
64
- ), volume_types))
58
+ volume_type_objects = list(
59
+ map(
60
+ lambda volume_type: VolumeType(
61
+ type=volume_type['type'],
62
+ price_per_month_per_gb=volume_type['price']['price_per_month_per_gb'],
63
+ ),
64
+ volume_types,
65
+ )
66
+ )
65
67
 
66
68
  return volume_type_objects