trainml 0.5.9__py3-none-any.whl → 0.5.11__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 (53) hide show
  1. tests/integration/projects/conftest.py +3 -1
  2. tests/integration/projects/test_projects_data_connectors_integration.py +44 -0
  3. tests/integration/projects/test_projects_datastores_integration.py +42 -0
  4. tests/integration/projects/test_projects_services_integration.py +44 -0
  5. tests/integration/test_checkpoints_integration.py +1 -2
  6. tests/integration/test_models_integration.py +0 -1
  7. tests/unit/cli/projects/__init__.py +0 -0
  8. tests/unit/cli/projects/test_cli_project_data_connector_unit.py +28 -0
  9. tests/unit/cli/projects/test_cli_project_datastore_unit.py +26 -0
  10. tests/unit/cli/projects/test_cli_project_key_unit.py +26 -0
  11. tests/unit/cli/projects/test_cli_project_secret_unit.py +26 -0
  12. tests/unit/cli/projects/test_cli_project_service_unit.py +26 -0
  13. tests/unit/cli/projects/test_cli_project_unit.py +19 -0
  14. tests/unit/cloudbender/test_datastores_unit.py +1 -5
  15. tests/unit/conftest.py +77 -4
  16. tests/unit/test_checkpoints_unit.py +15 -23
  17. tests/unit/test_datasets_unit.py +15 -20
  18. tests/unit/test_models_unit.py +13 -16
  19. tests/unit/test_volumes_unit.py +3 -0
  20. trainml/__init__.py +1 -1
  21. trainml/checkpoints.py +14 -3
  22. trainml/cli/cloudbender/datastore.py +2 -7
  23. trainml/cli/project/__init__.py +3 -72
  24. trainml/cli/project/data_connector.py +61 -0
  25. trainml/cli/project/datastore.py +61 -0
  26. trainml/cli/project/service.py +61 -0
  27. trainml/cloudbender/data_connectors.py +8 -0
  28. trainml/cloudbender/datastores.py +9 -19
  29. trainml/cloudbender/nodes.py +44 -1
  30. trainml/cloudbender/providers.py +53 -0
  31. trainml/cloudbender/regions.py +48 -0
  32. trainml/datasets.py +14 -3
  33. trainml/exceptions.py +51 -0
  34. trainml/jobs.py +2 -13
  35. trainml/models.py +14 -3
  36. trainml/volumes.py +15 -3
  37. {trainml-0.5.9.dist-info → trainml-0.5.11.dist-info}/METADATA +1 -1
  38. {trainml-0.5.9.dist-info → trainml-0.5.11.dist-info}/RECORD +42 -40
  39. tests/integration/test_projects_integration.py +0 -44
  40. tests/unit/cli/cloudbender/test_cli_reservation_unit.py +0 -34
  41. tests/unit/cli/test_cli_project_unit.py +0 -42
  42. tests/unit/cloudbender/test_reservations_unit.py +0 -173
  43. tests/unit/test_auth.py +0 -30
  44. tests/unit/test_projects_unit.py +0 -320
  45. tests/unit/test_trainml.py +0 -54
  46. trainml/cli/cloudbender/reservation.py +0 -159
  47. trainml/cli/project.py +0 -149
  48. trainml/cloudbender/reservations.py +0 -126
  49. trainml/projects.py +0 -228
  50. {trainml-0.5.9.dist-info → trainml-0.5.11.dist-info}/LICENSE +0 -0
  51. {trainml-0.5.9.dist-info → trainml-0.5.11.dist-info}/WHEEL +0 -0
  52. {trainml-0.5.9.dist-info → trainml-0.5.11.dist-info}/entry_points.txt +0 -0
  53. {trainml-0.5.9.dist-info → trainml-0.5.11.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,16 @@
1
1
  import json
2
2
  import logging
3
+ import asyncio
4
+ import math
3
5
  from datetime import datetime
4
6
 
7
+ from trainml.exceptions import (
8
+ ApiError,
9
+ SpecificationError,
10
+ TrainMLException,
11
+ ProviderError,
12
+ )
13
+
5
14
 
6
15
  class Providers(object):
7
16
  def __init__(self, trainml):
@@ -36,6 +45,7 @@ class Provider:
36
45
  self._provider = kwargs
37
46
  self._id = self._provider.get("provider_uuid")
38
47
  self._type = self._provider.get("type")
48
+ self._status = self._provider.get("status")
39
49
  self._credits = self._provider.get("credits")
40
50
 
41
51
  @property
@@ -46,6 +56,10 @@ class Provider:
46
56
  def type(self) -> str:
47
57
  return self._type
48
58
 
59
+ @property
60
+ def status(self) -> str:
61
+ return self._status
62
+
49
63
  @property
50
64
  def credits(self) -> float:
51
65
  return self._credits
@@ -69,3 +83,42 @@ class Provider:
69
83
  )
70
84
  self.__init__(self.trainml, **resp)
71
85
  return self
86
+
87
+ async def wait_for(self, status, timeout=300):
88
+ if self.status == status:
89
+ return
90
+ valid_statuses = ["ready", "archived"]
91
+ if not status in valid_statuses:
92
+ raise SpecificationError(
93
+ "status",
94
+ f"Invalid wait_for status {status}. Valid statuses are: {valid_statuses}",
95
+ )
96
+ MAX_TIMEOUT = 24 * 60 * 60
97
+ if timeout > MAX_TIMEOUT:
98
+ raise SpecificationError(
99
+ "timeout",
100
+ f"timeout must be less than {MAX_TIMEOUT} seconds.",
101
+ )
102
+
103
+ POLL_INTERVAL_MIN = 5
104
+ POLL_INTERVAL_MAX = 60
105
+ POLL_INTERVAL = max(min(timeout / 60, POLL_INTERVAL_MAX), POLL_INTERVAL_MIN)
106
+ retry_count = math.ceil(timeout / POLL_INTERVAL)
107
+ count = 0
108
+ while count < retry_count:
109
+ await asyncio.sleep(POLL_INTERVAL)
110
+ try:
111
+ await self.refresh()
112
+ except ApiError as e:
113
+ if status == "archived" and e.status == 404:
114
+ return
115
+ raise e
116
+ if self.status in ["errored", "failed"]:
117
+ raise ProviderError(self.status, self)
118
+ if self.status == status:
119
+ return self
120
+ else:
121
+ count += 1
122
+ logging.debug(f"self: {self}, retry count {count}")
123
+
124
+ raise TrainMLException(f"Timeout waiting for {status}")
@@ -1,5 +1,14 @@
1
1
  import json
2
2
  import logging
3
+ import asyncio
4
+ import math
5
+
6
+ from trainml.exceptions import (
7
+ ApiError,
8
+ SpecificationError,
9
+ TrainMLException,
10
+ RegionError,
11
+ )
3
12
 
4
13
 
5
14
  class Regions(object):
@@ -111,3 +120,42 @@ class Region:
111
120
  None,
112
121
  dict(project_uuid=project_uuid, checkpoint_uuid=checkpoint_uuid),
113
122
  )
123
+
124
+ async def wait_for(self, status, timeout=300):
125
+ if self.status == status:
126
+ return
127
+ valid_statuses = ["healthy", "offline", "archived"]
128
+ if not status in valid_statuses:
129
+ raise SpecificationError(
130
+ "status",
131
+ f"Invalid wait_for status {status}. Valid statuses are: {valid_statuses}",
132
+ )
133
+ MAX_TIMEOUT = 24 * 60 * 60
134
+ if timeout > MAX_TIMEOUT:
135
+ raise SpecificationError(
136
+ "timeout",
137
+ f"timeout must be less than {MAX_TIMEOUT} seconds.",
138
+ )
139
+
140
+ POLL_INTERVAL_MIN = 5
141
+ POLL_INTERVAL_MAX = 60
142
+ POLL_INTERVAL = max(min(timeout / 60, POLL_INTERVAL_MAX), POLL_INTERVAL_MIN)
143
+ retry_count = math.ceil(timeout / POLL_INTERVAL)
144
+ count = 0
145
+ while count < retry_count:
146
+ await asyncio.sleep(POLL_INTERVAL)
147
+ try:
148
+ await self.refresh()
149
+ except ApiError as e:
150
+ if status == "archived" and e.status == 404:
151
+ return
152
+ raise e
153
+ if self.status in ["errored", "failed"]:
154
+ raise RegionError(self.status, self)
155
+ if self.status == status:
156
+ return self
157
+ else:
158
+ count += 1
159
+ logging.debug(f"self: {self}, retry count {count}")
160
+
161
+ raise TrainMLException(f"Timeout waiting for {status}")
trainml/datasets.py CHANGED
@@ -31,13 +31,24 @@ class Datasets(object):
31
31
  datasets = [Dataset(self.trainml, **dataset) for dataset in resp]
32
32
  return datasets
33
33
 
34
- async def create(self, name, source_type, source_uri, **kwargs):
34
+ async def create(
35
+ self,
36
+ name,
37
+ source_type,
38
+ source_uri,
39
+ type="evefs",
40
+ project_uuid=None,
41
+ **kwargs,
42
+ ):
43
+ if not project_uuid:
44
+ project_uuid = self.trainml.active_project
35
45
  data = dict(
36
46
  name=name,
37
47
  source_type=source_type,
38
48
  source_uri=source_uri,
39
- source_options=kwargs.get("source_options"),
40
- project_uuid=kwargs.get("project_uuid") or self.trainml.active_project,
49
+ project_uuid=project_uuid,
50
+ type=type,
51
+ **kwargs,
41
52
  )
42
53
  payload = {k: v for k, v in data.items() if v is not None}
43
54
  logging.info(f"Creating Dataset {name}")
trainml/exceptions.py CHANGED
@@ -147,3 +147,54 @@ class SpecificationError(TrainMLException):
147
147
 
148
148
  def __str__(self):
149
149
  return "SpecificationError({self.attribute}, {self.message})".format(self=self)
150
+
151
+
152
+ class NodeError(TrainMLException):
153
+ def __init__(self, status, data, *args):
154
+ super().__init__(data, *args)
155
+ self._status = status
156
+ self._message = data
157
+
158
+ @property
159
+ def status(self) -> str:
160
+ return self._status
161
+
162
+ def __repr__(self):
163
+ return "NodeError({self.status}, {self.message})".format(self=self)
164
+
165
+ def __str__(self):
166
+ return "NodeError({self.status}, {self.message})".format(self=self)
167
+
168
+
169
+ class ProviderError(TrainMLException):
170
+ def __init__(self, status, data, *args):
171
+ super().__init__(data, *args)
172
+ self._status = status
173
+ self._message = data
174
+
175
+ @property
176
+ def status(self) -> str:
177
+ return self._status
178
+
179
+ def __repr__(self):
180
+ return "ProviderError({self.status}, {self.message})".format(self=self)
181
+
182
+ def __str__(self):
183
+ return "ProviderError({self.status}, {self.message})".format(self=self)
184
+
185
+
186
+ class RegionError(TrainMLException):
187
+ def __init__(self, status, data, *args):
188
+ super().__init__(data, *args)
189
+ self._status = status
190
+ self._message = data
191
+
192
+ @property
193
+ def status(self) -> str:
194
+ return self._status
195
+
196
+ def __repr__(self):
197
+ return "RegionError({self.status}, {self.message})".format(self=self)
198
+
199
+ def __str__(self):
200
+ return "RegionError({self.status}, {self.message})".format(self=self)
trainml/jobs.py CHANGED
@@ -476,7 +476,6 @@ class Job:
476
476
  return
477
477
  valid_statuses = [
478
478
  "waiting for data/model download",
479
- "waiting for GPUs",
480
479
  "waiting for resources",
481
480
  "running",
482
481
  "stopped",
@@ -488,11 +487,6 @@ class Job:
488
487
  "status",
489
488
  f"Invalid wait_for status {status}. Valid statuses are: {valid_statuses}",
490
489
  )
491
- if status == "waiting for GPUs":
492
- warnings.warn(
493
- "'waiting for GPUs' status is deprecated, use 'waiting for resources' instead.",
494
- DeprecationWarning,
495
- )
496
490
  if (self.type == "training") and status == "stopped":
497
491
  warnings.warn(
498
492
  "'stopped' status is deprecated for training jobs, use 'finished' instead.",
@@ -523,12 +517,8 @@ class Job:
523
517
  self.status == status
524
518
  or (
525
519
  status
526
- in [
527
- "waiting for GPUs",
528
- "waiting for resources",
529
- ] ## this status could be very short and the polling could miss it
530
- and self.status
531
- not in ["new", "waiting for GPUs", "waiting for resources"]
520
+ == "waiting for resources" ## this status could be very short and the polling could miss it
521
+ and self.status not in ["new", "waiting for resources"]
532
522
  )
533
523
  or (
534
524
  status
@@ -536,7 +526,6 @@ class Job:
536
526
  and self.status
537
527
  not in [
538
528
  "new",
539
- "waiting for GPUs",
540
529
  "waiting for resources",
541
530
  "waiting for data/model download",
542
531
  ]
trainml/models.py CHANGED
@@ -26,13 +26,24 @@ class Models(object):
26
26
  models = [Model(self.trainml, **model) for model in resp]
27
27
  return models
28
28
 
29
- async def create(self, name, source_type, source_uri, **kwargs):
29
+ async def create(
30
+ self,
31
+ name,
32
+ source_type,
33
+ source_uri,
34
+ type="evefs",
35
+ project_uuid=None,
36
+ **kwargs,
37
+ ):
38
+ if not project_uuid:
39
+ project_uuid = self.trainml.active_project
30
40
  data = dict(
31
41
  name=name,
32
42
  source_type=source_type,
33
43
  source_uri=source_uri,
34
- source_options=kwargs.get("source_options"),
35
- project_uuid=kwargs.get("project_uuid") or self.trainml.active_project,
44
+ project_uuid=project_uuid,
45
+ type=type,
46
+ **kwargs,
36
47
  )
37
48
  payload = {k: v for k, v in data.items() if v is not None}
38
49
  logging.info(f"Creating Model {name}")
trainml/volumes.py CHANGED
@@ -26,14 +26,26 @@ class Volumes(object):
26
26
  volumes = [Volume(self.trainml, **volume) for volume in resp]
27
27
  return volumes
28
28
 
29
- async def create(self, name, source_type, source_uri, capacity, **kwargs):
29
+ async def create(
30
+ self,
31
+ name,
32
+ source_type,
33
+ source_uri,
34
+ capacity,
35
+ type="evefs",
36
+ project_uuid=None,
37
+ **kwargs,
38
+ ):
39
+ if not project_uuid:
40
+ project_uuid = self.trainml.active_project
30
41
  data = dict(
31
42
  name=name,
32
43
  source_type=source_type,
33
44
  source_uri=source_uri,
34
45
  capacity=capacity,
35
- source_options=kwargs.get("source_options"),
36
- project_uuid=kwargs.get("project_uuid") or self.trainml.active_project,
46
+ project_uuid=project_uuid,
47
+ type=type,
48
+ **kwargs,
37
49
  )
38
50
  payload = {k: v for k, v in data.items() if v is not None}
39
51
  logging.info(f"Creating Volume {name}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: trainml
3
- Version: 0.5.9
3
+ Version: 0.5.11
4
4
  Summary: trainML client SDK and command line utilities
5
5
  Home-page: https://github.com/trainML/trainml-cli
6
6
  Author: trainML
@@ -4,37 +4,36 @@ examples/local_storage.py,sha256=w8iAeqr5CLOCOkNrqGzEDtybjDGGY7SQUqeE0ibMUrM,174
4
4
  examples/training_inference_pipeline.py,sha256=SNr4RFT9y69F9G9tMD8ONUbJmXRFrq1yxynq-FbfEf8,2334
5
5
  tests/integration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
6
  tests/integration/conftest.py,sha256=VWWTfofsFcBOdSCXQxNYbMcDEEaErDi2wFFMn--LE4Y,1134
7
- tests/integration/test_checkpoints_integration.py,sha256=mLha1BhVZ916OJIDOKF6vah3kxJXQu7pbAsHq7lsNCE,3230
7
+ tests/integration/test_checkpoints_integration.py,sha256=Giy5Z6WyselfXzskDGR7Z0ks2KK2U7WGQBDPfyqQ6dk,3206
8
8
  tests/integration/test_datasets_integration.py,sha256=zdHOevduuMUWvVxaHBslpmH8AdvPdqEJ95MdqCC5_rw,3499
9
9
  tests/integration/test_environments_integration.py,sha256=0IckhJvQhd8j4Ouiu0hMq2b7iA1dbZpZYmknyfWjsFM,1403
10
10
  tests/integration/test_gpu_types_integration.py,sha256=V2OncokZWWVq_l5FSmKEDM4EsWrmpB-zKiVPt-we0aY,1256
11
11
  tests/integration/test_jobs_integration.py,sha256=N2peEQGYHteGMd0J7NK4gJaaTyps5jjuiKq2ENnD8SY,25117
12
- tests/integration/test_models_integration.py,sha256=UPRAz0lcpzGihsnDUARoafbd5sZ6OM8TIeh8HNN6Bg0,2902
13
- tests/integration/test_projects_integration.py,sha256=BX-LqLfzawTQUhtx--5dw7QqR8kl_CJvwSCyNXDUQTw,1446
12
+ tests/integration/test_models_integration.py,sha256=u6KVnX-F00TqwiU-gEoZJP1oKfAblqnyRptOc9vNGJ4,2878
14
13
  tests/integration/test_volumes_integration.py,sha256=gOmZpwwFxqeOAVmfKWSTmuyshx8nb2zu_0xv1RUEepM,3270
15
14
  tests/integration/cloudbender/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
15
  tests/integration/cloudbender/test_providers_integration.py,sha256=oV8ydFsosDZ_Z1Dkg2IN-ZhWuIl5e_HkHAORMsOsAJc,1473
17
16
  tests/integration/projects/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
- tests/integration/projects/conftest.py,sha256=EwH4QLDqouYTm0eofHFTRRh7D-lPjLgYYRyx91Zmv9Q,208
17
+ tests/integration/projects/conftest.py,sha256=IaCvq3G_2uBm-DAfqTUF2K_km06af711bzmp-OMwZBs,242
18
+ tests/integration/projects/test_projects_data_connectors_integration.py,sha256=R90v7HVN4i9k-AOEl17AvnbWIwnD9Fl1OK9IGOV847o,1630
19
+ tests/integration/projects/test_projects_datastores_integration.py,sha256=S9aSvryVjrcoEDezr33x4T5aPjEhUwL9O1-1eyzhQ-s,1469
19
20
  tests/integration/projects/test_projects_integration.py,sha256=HP4jPAyTqGt7pwk42bxQsqGp0olgai6sI-S1XXcXoJs,1241
20
21
  tests/integration/projects/test_projects_keys_integration.py,sha256=O-a9OwBxIM2l0vi__GMlWP8ncNswxGBJvEhgo8PoalQ,1423
21
22
  tests/integration/projects/test_projects_secrets_integration.py,sha256=XMMov60dVyklZH0FzcHgVKOu5zpNH4z7GriOp-wawgI,1478
23
+ tests/integration/projects/test_projects_services_integration.py,sha256=lBnrqFpjsL3zfaJJmFh2rCWfNmmX-TjyOPCR3KobDAc,1530
22
24
  tests/unit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
- tests/unit/conftest.py,sha256=xctqH6_Sk4TeSe5ewW8w1ILMHIiUrGDTFXbwoF7sKn4,33174
24
- tests/unit/test_auth.py,sha256=nfhlOCR7rUsn_MaD8QQtBc2v0k8pIxqbzGgRAZK1WGc,858
25
+ tests/unit/conftest.py,sha256=IIdY88sGlvFuH2ZZHysWi3QCZ1-oAZ1yG6npKMRjCzI,35521
25
26
  tests/unit/test_auth_unit.py,sha256=nfhlOCR7rUsn_MaD8QQtBc2v0k8pIxqbzGgRAZK1WGc,858
26
- tests/unit/test_checkpoints_unit.py,sha256=4Add2DXZCuriSZ0atvOXc8fsEGMaEfPhYmT8Q3UgP5E,16008
27
+ tests/unit/test_checkpoints_unit.py,sha256=7bgpEnjkfETgbEHmYApyD_htEWAUVZdposyYVClU3tQ,15965
27
28
  tests/unit/test_connections_unit.py,sha256=FzN2ddQxNpjxzNGUsXhjTk0HnD24wSPelPTL4o_r-Ho,5507
28
- tests/unit/test_datasets_unit.py,sha256=lVNoBZu4RIiJK26gbUPOUAra_k0YS2GcnjJDnT7UV6Y,15879
29
+ tests/unit/test_datasets_unit.py,sha256=YL_wU-NiXvPFOK_jM8l9q5cZGztsMkgQorggYaLt6Lc,15910
29
30
  tests/unit/test_environments_unit.py,sha256=1QFGf1xwM0yKCyVHT_Xi0DX8g0Aelr0mcqAImEXJfQU,1882
30
31
  tests/unit/test_exceptions.py,sha256=3tAok6kAU1QRjN7qTNVYuSGWDg7IEoK__OXFLyzLr7k,906
31
32
  tests/unit/test_gpu_types_unit.py,sha256=c9ie6YSYT5onBnlmHvHWON9WgQiJ1eO2C-4Tk-UPQHg,2054
32
33
  tests/unit/test_jobs_unit.py,sha256=bZxN9HUfHCyQCjZCZGn6WFIhu8S5FU1z5ZG9sgH2XEg,26835
33
- tests/unit/test_models_unit.py,sha256=uezWF7FUHGmCSQBtpyyKhBttTnCTRjxU22NsHdJLYYg,15064
34
- tests/unit/test_projects_unit.py,sha256=uqMs3v4mNevUSh5QgP54_R88ctqOXdD73t0AgjTWXbg,10743
35
- tests/unit/test_trainml.py,sha256=8vAKvFD1xYsx_VY4HFVa0b1MUlMoNApY6TO8r7vI-UQ,1701
34
+ tests/unit/test_models_unit.py,sha256=Wg2QsoKqtSFSVf-1tt4sA6OcwMTkSHYPcUBQQOmZCgY,15091
36
35
  tests/unit/test_trainml_unit.py,sha256=8vAKvFD1xYsx_VY4HFVa0b1MUlMoNApY6TO8r7vI-UQ,1701
37
- tests/unit/test_volumes_unit.py,sha256=KHVmdbQIiX8tEE09U-XsH-vl6wfYGVoRzR_UQJlhOVE,15305
36
+ tests/unit/test_volumes_unit.py,sha256=DF3kFtkIfuZjwacBRpdmMGlPvV7vxaxUAhlstWj2V4w,15382
38
37
  tests/unit/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
38
  tests/unit/cli/conftest.py,sha256=w6p_2URicywJKUCtY79tSD_mx8cwJtxHbK_Lu3grOYs,236
40
39
  tests/unit/cli/test_cli_checkpoint_unit.py,sha256=6gO6PWWxNJiL520mhTFkSUJT7p5-dptLwUgDjPeHNrA,702
@@ -43,7 +42,6 @@ tests/unit/cli/test_cli_environment_unit.py,sha256=7FFKFPVa6yqJqujTXD_tsSUDruW5K
43
42
  tests/unit/cli/test_cli_gpu_unit.py,sha256=FIq3tQIDmeD-pvxkhJKMykfnlWcVxho2vRkoCMXgQxE,650
44
43
  tests/unit/cli/test_cli_job_unit.py,sha256=xUqkLFDIyI1ExiVVgr-218gQSlFSYCm4RRX_eyipJhY,611
45
44
  tests/unit/cli/test_cli_model_unit.py,sha256=fE-CRVg8gbtDlwrKBkf-hc9x7EhFlYeE3jlum1E27EA,629
46
- tests/unit/cli/test_cli_project_unit.py,sha256=pi0N-XVH5wsJQ6idFcRsIofWSAdj8KtK1gwf1J3l2JY,1688
47
45
  tests/unit/cli/test_cli_volume_unit.py,sha256=oggGL2eLiaExP6rSdFmQevxLp6nw5o7SKUEqMKBmy_A,644
48
46
  tests/unit/cli/cloudbender/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
49
47
  tests/unit/cli/cloudbender/test_cli_datastore_unit.py,sha256=DQWDjqg4viBZRONi00nVzqF9rJ5qKOKRub9pKbTmMWU,1381
@@ -51,17 +49,22 @@ tests/unit/cli/cloudbender/test_cli_device_unit.py,sha256=2BSMyXQ8fOzNKh_-pa_tx7
51
49
  tests/unit/cli/cloudbender/test_cli_node_unit.py,sha256=KbK7axJ1L4y4sN7KQRpOVIqphnNpi0aFW4-HlYLtwnI,1316
52
50
  tests/unit/cli/cloudbender/test_cli_provider_unit.py,sha256=Rm-tRNPbTTB7ZzkkIpLfDp_pEYfqihjB0ZYk_EPQUfs,781
53
51
  tests/unit/cli/cloudbender/test_cli_region_unit.py,sha256=iH5AbrzZ-R2EJ-Bd2HFN7FN2lTpkr3-pCLR59ZVvdQU,1262
54
- tests/unit/cli/cloudbender/test_cli_reservation_unit.py,sha256=4LDOJDXygMuho2cdI2K59eq4oyiry9hNaG0avEr0_tw,1311
55
52
  tests/unit/cli/cloudbender/test_cli_service_unit.py,sha256=4LDOJDXygMuho2cdI2K59eq4oyiry9hNaG0avEr0_tw,1311
53
+ tests/unit/cli/projects/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
54
+ tests/unit/cli/projects/test_cli_project_data_connector_unit.py,sha256=_NgnxV8zTIycpHKe7dyL6EXq774puFHQjvgCKsiEMO0,993
55
+ tests/unit/cli/projects/test_cli_project_datastore_unit.py,sha256=E_kLoIZ5U92LQaPcYAKDyUqpjM1Xe6PVPvofU1Yn6Yk,936
56
+ tests/unit/cli/projects/test_cli_project_key_unit.py,sha256=Ma5HjsZt2RaHKgmMdGY59EIc2WdAgbWKWn2LvlYeDqQ,894
57
+ tests/unit/cli/projects/test_cli_project_secret_unit.py,sha256=QBkq1gVqkjGUni2xCihL994bvseNcA-dyUWaJVU9GjI,915
58
+ tests/unit/cli/projects/test_cli_project_service_unit.py,sha256=TRNOwK3RUCtktYdCGzfhQE3fE_mn8LweCN5E4gRn1h4,922
59
+ tests/unit/cli/projects/test_cli_project_unit.py,sha256=0IynsMwFsXBZn-u0gX2iir7AjPsNraHe68PuAe-Lkaw,627
56
60
  tests/unit/cloudbender/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
57
61
  tests/unit/cloudbender/test_data_connectors_unit.py,sha256=qy97mNAcy_xEkh8obBobHt16B3e1N3QcBSyfV9xJwPI,5783
58
- tests/unit/cloudbender/test_datastores_unit.py,sha256=54mPokxhrRjlkBfqpmeA_q-PLml-HUNNit91aQVTpCg,5398
62
+ tests/unit/cloudbender/test_datastores_unit.py,sha256=5XUczUrOQ2Zgfb2IbJeEtYxbBWRltEVJ1KIC_UeQjEY,5281
59
63
  tests/unit/cloudbender/test_device_configs_unit.py,sha256=lzyCuF7MRoQrtJVTQFL27lqPnRwQFv25htPgJqDuQI8,5714
60
64
  tests/unit/cloudbender/test_devices_unit.py,sha256=QBWnlOe1tw_XNA_i-yDHkmpGvtK36f2u1HhoXquoVaE,9103
61
65
  tests/unit/cloudbender/test_nodes_unit.py,sha256=BDpfJXCBNNpLt5rhJMk2BVXDQ_4QSmxoVrO_YPs6xBU,6231
62
66
  tests/unit/cloudbender/test_providers_unit.py,sha256=OgxifgC1IqLH8DNMKXy1Ne9_7a75ea6kHEOfRSRoQuQ,4373
63
67
  tests/unit/cloudbender/test_regions_unit.py,sha256=BbJICLIQmlotpA1UmLD0KTW_H9g2UW0J8ZYzQk1_Xjc,6299
64
- tests/unit/cloudbender/test_reservations_unit.py,sha256=nWEZ_p9EF2C49nbgL7Dt4NG2Irmyt94ZqJJQDyNfGFI,5624
65
68
  tests/unit/cloudbender/test_services_unit.py,sha256=fYJx-W89HD-EYkO32_v33X40VxipUfWQCy13FZO2fcA,5220
66
69
  tests/unit/projects/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
67
70
  tests/unit/projects/test_project_data_connectors_unit.py,sha256=uQPFx6rwYMWZz1IJkns_LdM5gccyHSIaKb3k8zmfSkY,3385
@@ -70,20 +73,19 @@ tests/unit/projects/test_project_keys_unit.py,sha256=2-w_VwmWxWHoLWBCoBAfg5Wozye
70
73
  tests/unit/projects/test_project_secrets_unit.py,sha256=StVlY-ZR3CKxXAt9NL8hTkXUEdgGESH0m6foEPXKE-4,3276
71
74
  tests/unit/projects/test_project_services_unit.py,sha256=ZK5nz78RAmmaCrAwYBd34t87dh6etU-7snLB_xukZHM,3331
72
75
  tests/unit/projects/test_projects_unit.py,sha256=4Jczapaj5pZOYKmRYN8MEygzyNwULCaOyYSyiNUBsq8,3823
73
- trainml/__init__.py,sha256=vIVVLaYTrWOAjLNK5btdaBLFAHwY3kI3ivbASxT_BJQ,432
76
+ trainml/__init__.py,sha256=vzWg68mF2ggaSj8F_z_riREX-rzGbJcPhGIDE1jG3yE,433
74
77
  trainml/__main__.py,sha256=JgErYkiskih8Y6oRwowALtR-rwQhAAdqOYWjQraRIPI,59
75
78
  trainml/auth.py,sha256=gruZv27nhttrCbhcVQTH9kZkF2uMm1E06SwA_2pQAHQ,26565
76
- trainml/checkpoints.py,sha256=TF_dZ-umcGiGmEF8cl0bhqChl1Tnp2QsDd-MqhiIcQs,8667
79
+ trainml/checkpoints.py,sha256=Hg3_Si7ohzsv4_8JIjoj9pMRpHNZRWKC5R2gtgmp01s,8790
77
80
  trainml/connections.py,sha256=h-S1NZbOkaXpIlpRStA6q-3eXc_OMlFWOLzF8R9SVG8,20029
78
- trainml/datasets.py,sha256=aIXsn-6ItzSwunT_xt7874XGznCPBMkXk49BDix-nqE,8477
81
+ trainml/datasets.py,sha256=dxTZbK-cnr8eoq48KAx2TZNK1ndcFveTVBtaNK2QyJ0,8600
79
82
  trainml/environments.py,sha256=OH4o08zXZ7IJ2CiA1rPnys2Fl45r8qvQHfM2mCBRAIc,1507
80
- trainml/exceptions.py,sha256=MG1FkcjRacv3HoPuBS1IWLCUk0wGHEQ6DaOzXNymsNI,4094
83
+ trainml/exceptions.py,sha256=Qdof2fKRvbMiwarX1VSw1XJXXJjY71H4U3v05nE7-7g,5468
81
84
  trainml/gpu_types.py,sha256=mm-dwfYc02192bmYPIJmzesndyBcoOdkKYBaYZXOUwU,1901
82
- trainml/jobs.py,sha256=28U0kyqczGhzP-tuRGAk6lNTeOzYdElZ7VpiHgz81rg,18056
83
- trainml/models.py,sha256=UqWoO6pVrSwxnRb1BTQH8czTLkkdTiC_cOsIjz9P8Pk,8161
84
- trainml/projects.py,sha256=ZVsVJtUPTYsQAkCLimjYF3DAwFwQV4ucLila-N-jSJo,6585
85
+ trainml/jobs.py,sha256=5wHiEmHfri1btgIn6zbFp9kIO6EU5FCOVkNMP0-WuYM,17632
86
+ trainml/models.py,sha256=SpVNt9oZMu70m_3VjO9j_QYexw_z-yRqMhIuJXPs4Vg,8284
85
87
  trainml/trainml.py,sha256=EBnqQ3Q291xrPKYuN6xKm5yt0mJQOJ3b7GAlR-fl8NI,10864
86
- trainml/volumes.py,sha256=1EAPOuYhs8WsOZDb0k0qhpqn2WJnoFxDTQ5tv9ZABUw,8312
88
+ trainml/volumes.py,sha256=7T0ZN_Xq3qk4yzw7YYxC0rxH3KqlqADd6HOZCkAfCR4,8443
87
89
  trainml/cli/__init__.py,sha256=Gvj6oGSEtgpb40ACtiVeMD93GM-uy15MG6VlX6rwdwA,4346
88
90
  trainml/cli/checkpoint.py,sha256=8Rh4bmFwJ4DKlIjHK-FLTeRynABqKCgIUGRtbQhAsX4,7170
89
91
  trainml/cli/connection.py,sha256=ELV6bPL30dzttFNxDU7Fb74R8oPL_E70k7TcJEzbwtQ,1700
@@ -91,32 +93,32 @@ trainml/cli/dataset.py,sha256=Pc00M6t7hGoRzCxznmmkijsWhG4PhIfG7UkrwtwykTY,6871
91
93
  trainml/cli/environment.py,sha256=dfm_T8OlCM5M8dLyOQBapJl3eFuVIku2P4JO6V0cYVQ,1019
92
94
  trainml/cli/gpu.py,sha256=CMcQyl2qbUgc2bc-gvUVT6X7bq2-sgiCHl3hyZ6kFWM,883
93
95
  trainml/cli/model.py,sha256=hR23E6ttRXcLk-RofkPK6wUXMO7OU6sT6jTEHTmUg9Q,6111
94
- trainml/cli/project.py,sha256=f772bHs68AVRY60l7dbVKgeDmDC3u2bZjqrz7zm7xvQ,3314
95
96
  trainml/cli/volume.py,sha256=kDUss93N78DT-YlLjC6I3jEq5nBWfRNNR5M4tY_F_Zg,6246
96
97
  trainml/cli/cloudbender/__init__.py,sha256=tKkL8TzD9nEeRtf1OEYM4XZJWb0-rGMPTmLIdA5G_SY,592
97
98
  trainml/cli/cloudbender/data_connector.py,sha256=q0Hqeh5w00Zkmh61fzO3pNR6EmQfoLQ2DbgJ5ZBx1UM,3606
98
- trainml/cli/cloudbender/datastore.py,sha256=gJ-comfAq65uiPoONQ35UIDLNVN7QKMf3l_2EcTN6zY,3478
99
+ trainml/cli/cloudbender/datastore.py,sha256=j67VUoYZOFWP0F7WK8V7fft8HlT4uqauAu0WH-9Dkes,3323
99
100
  trainml/cli/cloudbender/device.py,sha256=KGZCFwwvS4tWsWuudrhlvquu_IFtV7LCUAOmCajicic,3453
100
101
  trainml/cli/cloudbender/node.py,sha256=iN_WaPCxOhtgDtnSsIFAEMGADG4MKiLjWoez6YSYwZI,3843
101
102
  trainml/cli/cloudbender/provider.py,sha256=oFjZWKfFQjNY7OtDu7nUdfv-RTmQc_Huuug963D3BdA,1726
102
103
  trainml/cli/cloudbender/region.py,sha256=X6-FYOb-pGpOEazn-NbsYSwa9ergB7FGATFkTe4a8Pk,2892
103
- trainml/cli/cloudbender/reservation.py,sha256=z2oMYwp-w_Keo1DepKUtuRnwiGz2VscVHDYWEFap1gs,3569
104
104
  trainml/cli/cloudbender/service.py,sha256=Wh6ycEuECiKL7qpFhc4IyO1rR5lvLtIHk3S475_R6pk,3147
105
105
  trainml/cli/job/__init__.py,sha256=ljY-ELeXhXQ7txASbJEKGBom7OXfNyy7sWILz3nxRAE,6545
106
106
  trainml/cli/job/create.py,sha256=pfOCqs5Vfk4PAI5KZpXHJ1vp3DDe4ccvYzieh0oFexY,34288
107
- trainml/cli/project/__init__.py,sha256=LSRKRO88xD7lYO8epTi0DloWzH4EYdRovajlIWjJPkY,3402
107
+ trainml/cli/project/__init__.py,sha256=XOBLQMQqlkzMUNoUejXXIP7bY5ebSWKlUn5XGkQVbPw,1904
108
+ trainml/cli/project/data_connector.py,sha256=KLDhpNJfwcJkcmyuJRgcVH70Jf73619O9ddYP8vhMvk,1411
109
+ trainml/cli/project/datastore.py,sha256=IwF0LqsSFn7DrKPRdxQs6kturk9MCI52A1aoWeb7ClA,1336
108
110
  trainml/cli/project/key.py,sha256=kQlCs_N-5c27hOyGkmT22_J47x8U6CZaSardsaPYGbw,3203
109
111
  trainml/cli/project/secret.py,sha256=LdEd6qJwfRs3JIjoOaNxkDcDuCdYB9GPKHW_vcOBal8,1621
112
+ trainml/cli/project/service.py,sha256=FzcyGI9MN-UvnjdKW7GmXLTXLUIXtuTFAISR_FmCmJo,1310
110
113
  trainml/cloudbender/__init__.py,sha256=iE29obtC0_9f0IhRvHQcG5aY58fVhVYipTakpjAhdss,64
111
114
  trainml/cloudbender/cloudbender.py,sha256=ekJZHSQ1F4HF8y0sAJ3MDB_hiC8QxPv9-O7U24z_RR4,717
112
- trainml/cloudbender/data_connectors.py,sha256=Qr-p9nukBeIaCg2v2plgZTBiBznAIBqDejzWpqHx310,3297
113
- trainml/cloudbender/datastores.py,sha256=biVGifedc3r1DcuxsfCQh-f1Tw4HcJMMJfdgHxPfkKM,3506
115
+ trainml/cloudbender/data_connectors.py,sha256=e0KAkKtcOcxW-UFHHUdF6deAYtP1SFv2PH9VyvizHek,3420
116
+ trainml/cloudbender/datastores.py,sha256=HGD0SLd0KYxkwl_r9NpnkcfKmQJ0q86BaxWwwyQT9W0,3255
114
117
  trainml/cloudbender/device_configs.py,sha256=DJWiGFaOE4C4xLE1BLDAiEjeL4T00R3FA_pb1xnSOr4,3399
115
118
  trainml/cloudbender/devices.py,sha256=QORNmKdLJoqGZmeWXRnivC1JmNBIw-ebvf4bsoem3r8,5660
116
- trainml/cloudbender/nodes.py,sha256=7HV2VLmxiUcJ-Kc6AAXS3M8C_XO-HKmaVgJpPdVnBQk,4332
117
- trainml/cloudbender/providers.py,sha256=-gkdiTu6Ah2znUuyyc3ZuRALagW8s1-OgqVjtlvc1AU,2036
118
- trainml/cloudbender/regions.py,sha256=Aqc_MeLVAeEv21e-lR5u8x1eintqUhZT2DBiQG3AcEE,3570
119
- trainml/cloudbender/reservations.py,sha256=rOrGXWIUHON4ad2aufEcvK4Yv_Mv3dDoScUtLJE8LWw,3586
119
+ trainml/cloudbender/nodes.py,sha256=U2sTIL2fuBVsNFPsJrvB2JbBcuLULF1-AwJ4dp5ChmM,5924
120
+ trainml/cloudbender/providers.py,sha256=22ymUl1KLC8UlyoWMsIrOKhUDOwnhklhHQ3EZ_V6eWA,3738
121
+ trainml/cloudbender/regions.py,sha256=nfSY9fIWp_AaRE_1Y0qwXX6WVSyPKxpji-zUfM3BNUo,5157
120
122
  trainml/cloudbender/services.py,sha256=KC3VcyljvnazUUG-Tzwm6Ab6d0--yuccXjOaMgYB5uA,5126
121
123
  trainml/projects/__init__.py,sha256=6NKCcHtQMeGB1IyU-djANphfnDX6MEkrXUM5Fyq9fWg,75
122
124
  trainml/projects/data_connectors.py,sha256=Y8lJNF9K5JZlwfKTtN9gh7eSRAivgiPWe_lx05pGM2U,1662
@@ -125,9 +127,9 @@ trainml/projects/keys.py,sha256=qcQAdysTbfVW19tzzxZDvBBWjU9DG0dkViLmG_S0SyM,2157
125
127
  trainml/projects/projects.py,sha256=mxMGmBppoFbwebMitczZjeNEODrzt0AuNm1LGNegyPo,2755
126
128
  trainml/projects/secrets.py,sha256=5qNPWITH8qtlXKkPWnFOzH0HPU7Z0K1OeW-9p7haOzM,2157
127
129
  trainml/projects/services.py,sha256=ZP-6CDXktIe6p89Hshgs91HLWXQPNWeU7VFtoKUPHXo,1679
128
- trainml-0.5.9.dist-info/LICENSE,sha256=s0lpBxhSSUEpMavwde-Vb6K_K7xDCTTvSpNznVqVGR0,1069
129
- trainml-0.5.9.dist-info/METADATA,sha256=ktJVkRMTp-cwdLXC5FIuhMXcAsWfHOXc8PaIBA1vdiE,7345
130
- trainml-0.5.9.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
131
- trainml-0.5.9.dist-info/entry_points.txt,sha256=OzBDm2wXby1bSGF02jTVxzRFZLejnbFiLHXhKdW3Bds,63
132
- trainml-0.5.9.dist-info/top_level.txt,sha256=Y1kLFRWKUW7RG8BX7cvejHF_yW8wBOaRYF1JQHENY4w,23
133
- trainml-0.5.9.dist-info/RECORD,,
130
+ trainml-0.5.11.dist-info/LICENSE,sha256=s0lpBxhSSUEpMavwde-Vb6K_K7xDCTTvSpNznVqVGR0,1069
131
+ trainml-0.5.11.dist-info/METADATA,sha256=z5-C7kUq2eL3_WS_uLwEIILAIBF0FAAErmoI1EYKaiw,7346
132
+ trainml-0.5.11.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
133
+ trainml-0.5.11.dist-info/entry_points.txt,sha256=OzBDm2wXby1bSGF02jTVxzRFZLejnbFiLHXhKdW3Bds,63
134
+ trainml-0.5.11.dist-info/top_level.txt,sha256=Y1kLFRWKUW7RG8BX7cvejHF_yW8wBOaRYF1JQHENY4w,23
135
+ trainml-0.5.11.dist-info/RECORD,,
@@ -1,44 +0,0 @@
1
- import re
2
- import sys
3
- import asyncio
4
- from pytest import mark, fixture
5
-
6
- pytestmark = [mark.sdk, mark.integration, mark.projects]
7
-
8
-
9
- @mark.create
10
- @mark.asyncio
11
- class GetProjectsTests:
12
- @fixture(scope="class")
13
- async def project(self, trainml):
14
- project = await trainml.projects.create(name="New Project", copy_keys=False)
15
- yield project
16
- await project.remove()
17
-
18
- async def test_get_projects(self, trainml):
19
- projects = await trainml.projects.list()
20
- assert len(projects) > 0
21
-
22
- async def test_get_project(self, trainml, project):
23
- response = await trainml.projects.get(project.id)
24
- assert response.id == project.id
25
-
26
- async def test_project_properties(self, project):
27
- assert isinstance(project.id, str)
28
- assert isinstance(project.name, str)
29
- assert isinstance(project.owner_name, str)
30
- assert isinstance(project.is_owner, bool)
31
- assert project.name == "New Project"
32
- assert project.is_owner
33
-
34
- async def test_project_str(self, project):
35
- string = str(project)
36
- regex = r"^{.*\"id\": \"" + project.id + r"\".*}$"
37
- assert isinstance(string, str)
38
- assert re.match(regex, string)
39
-
40
- async def test_project_repr(self, project):
41
- string = repr(project)
42
- regex = r"^Project\( trainml , \*\*{.*'id': '" + project.id + r"'.*}\)$"
43
- assert isinstance(string, str)
44
- assert re.match(regex, string)
@@ -1,34 +0,0 @@
1
- import re
2
- import json
3
- import click
4
- from unittest.mock import AsyncMock, patch
5
- from pytest import mark, fixture, raises
6
-
7
- pytestmark = [mark.cli, mark.unit, mark.cloudbender, mark.services]
8
-
9
- from trainml.cli.cloudbender import service as specimen
10
- from trainml.cloudbender.services import Service
11
-
12
-
13
- def test_list(runner, mock_services):
14
- with patch("trainml.cli.TrainML", new=AsyncMock) as mock_trainml:
15
- mock_trainml.cloudbender = AsyncMock()
16
- mock_trainml.cloudbender.services = AsyncMock()
17
- mock_trainml.cloudbender.services.list = AsyncMock(return_value=mock_services)
18
- result = runner.invoke(
19
- specimen,
20
- args=["list", "--provider=prov-id-1", "--region=reg-id-1"],
21
- )
22
- assert result.exit_code == 0
23
- mock_trainml.cloudbender.services.list.assert_called_once_with(
24
- provider_uuid="prov-id-1", region_uuid="reg-id-1"
25
- )
26
-
27
-
28
- def test_list_no_provider(runner, mock_services):
29
- with patch("trainml.cli.TrainML", new=AsyncMock) as mock_trainml:
30
- mock_trainml.cloudbender = AsyncMock()
31
- mock_trainml.cloudbender.services = AsyncMock()
32
- mock_trainml.cloudbender.services.list = AsyncMock(return_value=mock_services)
33
- result = runner.invoke(specimen, ["list"])
34
- assert result.exit_code != 0
@@ -1,42 +0,0 @@
1
- import re
2
- import json
3
- import click
4
- from unittest.mock import AsyncMock, patch, create_autospec
5
- from pytest import mark, fixture, raises
6
-
7
- pytestmark = [mark.cli, mark.unit, mark.projects]
8
-
9
- from trainml.cli import project as specimen
10
- from trainml.projects import Project
11
-
12
-
13
- def test_list(runner, mock_projects):
14
- with patch("trainml.cli.TrainML", new=AsyncMock) as mock_trainml:
15
- mock_trainml.projects = AsyncMock()
16
- mock_trainml.projects.list = AsyncMock(return_value=mock_projects)
17
- result = runner.invoke(specimen, ["list"])
18
- print(result)
19
- assert result.exit_code == 0
20
- mock_trainml.projects.list.assert_called_once()
21
-
22
-
23
- def test_list_datastores(runner, mock_project_datastores):
24
- with patch("trainml.cli.TrainML", new=AsyncMock) as mock_trainml:
25
- mock_project = create_autospec(Project)
26
- mock_project.list_datastores = AsyncMock(return_value=mock_project_datastores)
27
- mock_trainml.projects.get = AsyncMock(return_value=mock_project)
28
- result = runner.invoke(specimen, ["list-datastores"])
29
- print(result)
30
- assert result.exit_code == 0
31
- mock_project.list_datastores.assert_called_once()
32
-
33
-
34
- def test_list_services(runner, mock_project_services):
35
- with patch("trainml.cli.TrainML", new=AsyncMock) as mock_trainml:
36
- mock_project = create_autospec(Project)
37
- mock_project.list_services = AsyncMock(return_value=mock_project_services)
38
- mock_trainml.projects.get = AsyncMock(return_value=mock_project)
39
- result = runner.invoke(specimen, ["list-services"])
40
- print(result)
41
- assert result.exit_code == 0
42
- mock_project.list_services.assert_called_once()