trainml 0.5.13__tar.gz → 0.5.15__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. {trainml-0.5.13 → trainml-0.5.15}/PKG-INFO +1 -1
  2. {trainml-0.5.13 → trainml-0.5.15}/pyproject.toml +1 -0
  3. trainml-0.5.15/tests/integration/cloudbender/conftest.py +28 -0
  4. {trainml-0.5.13 → trainml-0.5.15}/tests/integration/cloudbender/test_providers_integration.py +3 -8
  5. trainml-0.5.15/tests/integration/cloudbender/test_regions_integration.py +42 -0
  6. trainml-0.5.15/tests/integration/cloudbender/test_services_integration.py +87 -0
  7. {trainml-0.5.13 → trainml-0.5.15}/tests/integration/conftest.py +1 -1
  8. {trainml-0.5.13 → trainml-0.5.15}/tests/integration/projects/conftest.py +2 -1
  9. {trainml-0.5.13 → trainml-0.5.15}/tests/integration/projects/test_projects_credentials_integration.py +1 -0
  10. {trainml-0.5.13 → trainml-0.5.15}/tests/integration/projects/test_projects_data_connectors_integration.py +1 -0
  11. {trainml-0.5.13 → trainml-0.5.15}/tests/integration/projects/test_projects_datastores_integration.py +1 -0
  12. {trainml-0.5.13 → trainml-0.5.15}/tests/integration/projects/test_projects_integration.py +1 -0
  13. trainml-0.5.15/tests/integration/projects/test_projects_members_integration.py +50 -0
  14. {trainml-0.5.13 → trainml-0.5.15}/tests/integration/projects/test_projects_secrets_integration.py +1 -0
  15. {trainml-0.5.13 → trainml-0.5.15}/tests/integration/projects/test_projects_services_integration.py +1 -0
  16. {trainml-0.5.13 → trainml-0.5.15}/tests/integration/test_checkpoints_integration.py +1 -0
  17. {trainml-0.5.13 → trainml-0.5.15}/tests/integration/test_datasets_integration.py +1 -0
  18. {trainml-0.5.13 → trainml-0.5.15}/tests/integration/test_jobs_integration.py +13 -8
  19. {trainml-0.5.13 → trainml-0.5.15}/tests/integration/test_models_integration.py +1 -0
  20. {trainml-0.5.13 → trainml-0.5.15}/tests/integration/test_volumes_integration.py +1 -0
  21. trainml-0.5.15/tests/unit/projects/test_project_members_unit.py +107 -0
  22. {trainml-0.5.13 → trainml-0.5.15}/trainml/__init__.py +1 -1
  23. {trainml-0.5.13 → trainml-0.5.15}/trainml/auth.py +1 -1
  24. {trainml-0.5.13 → trainml-0.5.15}/trainml/cli/job/create.py +32 -2
  25. {trainml-0.5.13 → trainml-0.5.15}/trainml/cloudbender/services.py +19 -1
  26. {trainml-0.5.13 → trainml-0.5.15}/trainml/jobs.py +9 -0
  27. trainml-0.5.15/trainml/projects/members.py +98 -0
  28. {trainml-0.5.13 → trainml-0.5.15}/trainml/projects/projects.py +2 -0
  29. {trainml-0.5.13 → trainml-0.5.15}/trainml/projects/services.py +17 -0
  30. {trainml-0.5.13 → trainml-0.5.15}/trainml/trainml.py +37 -20
  31. {trainml-0.5.13 → trainml-0.5.15}/trainml.egg-info/PKG-INFO +1 -1
  32. {trainml-0.5.13 → trainml-0.5.15}/trainml.egg-info/SOURCES.txt +6 -0
  33. {trainml-0.5.13 → trainml-0.5.15}/LICENSE +0 -0
  34. {trainml-0.5.13 → trainml-0.5.15}/README.md +0 -0
  35. {trainml-0.5.13 → trainml-0.5.15}/examples/__init__.py +0 -0
  36. {trainml-0.5.13 → trainml-0.5.15}/examples/create_dataset_and_training_job.py +0 -0
  37. {trainml-0.5.13 → trainml-0.5.15}/examples/local_storage.py +0 -0
  38. {trainml-0.5.13 → trainml-0.5.15}/examples/training_inference_pipeline.py +0 -0
  39. {trainml-0.5.13 → trainml-0.5.15}/setup.cfg +0 -0
  40. {trainml-0.5.13 → trainml-0.5.15}/setup.py +0 -0
  41. {trainml-0.5.13 → trainml-0.5.15}/tests/integration/__init__.py +0 -0
  42. {trainml-0.5.13 → trainml-0.5.15}/tests/integration/cloudbender/__init__.py +0 -0
  43. {trainml-0.5.13 → trainml-0.5.15}/tests/integration/projects/__init__.py +0 -0
  44. {trainml-0.5.13 → trainml-0.5.15}/tests/integration/test_environments_integration.py +0 -0
  45. {trainml-0.5.13 → trainml-0.5.15}/tests/integration/test_gpu_types_integration.py +0 -0
  46. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/__init__.py +0 -0
  47. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/cli/__init__.py +0 -0
  48. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/cli/cloudbender/__init__.py +0 -0
  49. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/cli/cloudbender/test_cli_datastore_unit.py +0 -0
  50. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/cli/cloudbender/test_cli_device_unit.py +0 -0
  51. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/cli/cloudbender/test_cli_node_unit.py +0 -0
  52. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/cli/cloudbender/test_cli_provider_unit.py +0 -0
  53. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/cli/cloudbender/test_cli_region_unit.py +0 -0
  54. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/cli/cloudbender/test_cli_service_unit.py +0 -0
  55. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/cli/conftest.py +0 -0
  56. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/cli/projects/__init__.py +0 -0
  57. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/cli/projects/test_cli_project_credential_unit.py +0 -0
  58. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/cli/projects/test_cli_project_data_connector_unit.py +0 -0
  59. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/cli/projects/test_cli_project_datastore_unit.py +0 -0
  60. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/cli/projects/test_cli_project_secret_unit.py +0 -0
  61. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/cli/projects/test_cli_project_service_unit.py +0 -0
  62. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/cli/projects/test_cli_project_unit.py +0 -0
  63. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/cli/test_cli_checkpoint_unit.py +0 -0
  64. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/cli/test_cli_datasets_unit.py +0 -0
  65. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/cli/test_cli_environment_unit.py +0 -0
  66. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/cli/test_cli_gpu_unit.py +0 -0
  67. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/cli/test_cli_job_unit.py +0 -0
  68. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/cli/test_cli_model_unit.py +0 -0
  69. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/cli/test_cli_volume_unit.py +0 -0
  70. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/cloudbender/__init__.py +0 -0
  71. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/cloudbender/test_data_connectors_unit.py +0 -0
  72. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/cloudbender/test_datastores_unit.py +0 -0
  73. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/cloudbender/test_device_configs_unit.py +0 -0
  74. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/cloudbender/test_devices_unit.py +0 -0
  75. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/cloudbender/test_nodes_unit.py +0 -0
  76. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/cloudbender/test_providers_unit.py +0 -0
  77. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/cloudbender/test_regions_unit.py +0 -0
  78. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/cloudbender/test_services_unit.py +0 -0
  79. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/conftest.py +0 -0
  80. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/projects/__init__.py +0 -0
  81. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/projects/test_project_credentials_unit.py +0 -0
  82. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/projects/test_project_data_connectors_unit.py +0 -0
  83. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/projects/test_project_datastores_unit.py +0 -0
  84. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/projects/test_project_secrets_unit.py +0 -0
  85. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/projects/test_project_services_unit.py +0 -0
  86. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/projects/test_projects_unit.py +0 -0
  87. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/test_auth_unit.py +0 -0
  88. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/test_checkpoints_unit.py +0 -0
  89. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/test_connections_unit.py +0 -0
  90. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/test_datasets_unit.py +0 -0
  91. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/test_environments_unit.py +0 -0
  92. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/test_exceptions.py +0 -0
  93. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/test_gpu_types_unit.py +0 -0
  94. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/test_jobs_unit.py +0 -0
  95. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/test_models_unit.py +0 -0
  96. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/test_trainml_unit.py +0 -0
  97. {trainml-0.5.13 → trainml-0.5.15}/tests/unit/test_volumes_unit.py +0 -0
  98. {trainml-0.5.13 → trainml-0.5.15}/trainml/__main__.py +0 -0
  99. {trainml-0.5.13 → trainml-0.5.15}/trainml/checkpoints.py +0 -0
  100. {trainml-0.5.13 → trainml-0.5.15}/trainml/cli/__init__.py +0 -0
  101. {trainml-0.5.13 → trainml-0.5.15}/trainml/cli/checkpoint.py +0 -0
  102. {trainml-0.5.13 → trainml-0.5.15}/trainml/cli/cloudbender/__init__.py +0 -0
  103. {trainml-0.5.13 → trainml-0.5.15}/trainml/cli/cloudbender/data_connector.py +0 -0
  104. {trainml-0.5.13 → trainml-0.5.15}/trainml/cli/cloudbender/datastore.py +0 -0
  105. {trainml-0.5.13 → trainml-0.5.15}/trainml/cli/cloudbender/device.py +0 -0
  106. {trainml-0.5.13 → trainml-0.5.15}/trainml/cli/cloudbender/node.py +0 -0
  107. {trainml-0.5.13 → trainml-0.5.15}/trainml/cli/cloudbender/provider.py +0 -0
  108. {trainml-0.5.13 → trainml-0.5.15}/trainml/cli/cloudbender/region.py +0 -0
  109. {trainml-0.5.13 → trainml-0.5.15}/trainml/cli/cloudbender/service.py +0 -0
  110. {trainml-0.5.13 → trainml-0.5.15}/trainml/cli/connection.py +0 -0
  111. {trainml-0.5.13 → trainml-0.5.15}/trainml/cli/dataset.py +0 -0
  112. {trainml-0.5.13 → trainml-0.5.15}/trainml/cli/environment.py +0 -0
  113. {trainml-0.5.13 → trainml-0.5.15}/trainml/cli/gpu.py +0 -0
  114. {trainml-0.5.13 → trainml-0.5.15}/trainml/cli/job/__init__.py +0 -0
  115. {trainml-0.5.13 → trainml-0.5.15}/trainml/cli/model.py +0 -0
  116. {trainml-0.5.13 → trainml-0.5.15}/trainml/cli/project/__init__.py +0 -0
  117. {trainml-0.5.13 → trainml-0.5.15}/trainml/cli/project/credential.py +0 -0
  118. {trainml-0.5.13 → trainml-0.5.15}/trainml/cli/project/data_connector.py +0 -0
  119. {trainml-0.5.13 → trainml-0.5.15}/trainml/cli/project/datastore.py +0 -0
  120. {trainml-0.5.13 → trainml-0.5.15}/trainml/cli/project/secret.py +0 -0
  121. {trainml-0.5.13 → trainml-0.5.15}/trainml/cli/project/service.py +0 -0
  122. {trainml-0.5.13 → trainml-0.5.15}/trainml/cli/volume.py +0 -0
  123. {trainml-0.5.13 → trainml-0.5.15}/trainml/cloudbender/__init__.py +0 -0
  124. {trainml-0.5.13 → trainml-0.5.15}/trainml/cloudbender/cloudbender.py +0 -0
  125. {trainml-0.5.13 → trainml-0.5.15}/trainml/cloudbender/data_connectors.py +0 -0
  126. {trainml-0.5.13 → trainml-0.5.15}/trainml/cloudbender/datastores.py +0 -0
  127. {trainml-0.5.13 → trainml-0.5.15}/trainml/cloudbender/device_configs.py +0 -0
  128. {trainml-0.5.13 → trainml-0.5.15}/trainml/cloudbender/devices.py +0 -0
  129. {trainml-0.5.13 → trainml-0.5.15}/trainml/cloudbender/nodes.py +0 -0
  130. {trainml-0.5.13 → trainml-0.5.15}/trainml/cloudbender/providers.py +0 -0
  131. {trainml-0.5.13 → trainml-0.5.15}/trainml/cloudbender/regions.py +0 -0
  132. {trainml-0.5.13 → trainml-0.5.15}/trainml/connections.py +0 -0
  133. {trainml-0.5.13 → trainml-0.5.15}/trainml/datasets.py +0 -0
  134. {trainml-0.5.13 → trainml-0.5.15}/trainml/environments.py +0 -0
  135. {trainml-0.5.13 → trainml-0.5.15}/trainml/exceptions.py +0 -0
  136. {trainml-0.5.13 → trainml-0.5.15}/trainml/gpu_types.py +0 -0
  137. {trainml-0.5.13 → trainml-0.5.15}/trainml/models.py +0 -0
  138. {trainml-0.5.13 → trainml-0.5.15}/trainml/projects/__init__.py +0 -0
  139. {trainml-0.5.13 → trainml-0.5.15}/trainml/projects/credentials.py +0 -0
  140. {trainml-0.5.13 → trainml-0.5.15}/trainml/projects/data_connectors.py +0 -0
  141. {trainml-0.5.13 → trainml-0.5.15}/trainml/projects/datastores.py +0 -0
  142. {trainml-0.5.13 → trainml-0.5.15}/trainml/projects/secrets.py +0 -0
  143. {trainml-0.5.13 → trainml-0.5.15}/trainml/volumes.py +0 -0
  144. {trainml-0.5.13 → trainml-0.5.15}/trainml.egg-info/dependency_links.txt +0 -0
  145. {trainml-0.5.13 → trainml-0.5.15}/trainml.egg-info/entry_points.txt +0 -0
  146. {trainml-0.5.13 → trainml-0.5.15}/trainml.egg-info/requires.txt +0 -0
  147. {trainml-0.5.13 → trainml-0.5.15}/trainml.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: trainml
3
- Version: 0.5.13
3
+ Version: 0.5.15
4
4
  Summary: trainML client SDK and command line utilities
5
5
  Home-page: https://github.com/trainML/trainml-cli
6
6
  Author: trainML
@@ -1,4 +1,5 @@
1
1
  [tool.pytest.ini_options]
2
+ addopts = "--cov-report term-missing --cov=trainml -n 4 --dist=loadgroup"
2
3
  python_files = "test_*"
3
4
  python_classes = "*Tests"
4
5
  python_functions = "test_*"
@@ -0,0 +1,28 @@
1
+
2
+ from pytest import fixture, mark
3
+
4
+
5
+ pytestmark = [mark.integration,mark.cloudbender]
6
+
7
+ @fixture(scope="session")
8
+ @mark.create
9
+ @mark.asyncio
10
+ @mark.xdist_group("cloudbender_resources")
11
+ async def provider( trainml):
12
+ provider = await trainml.cloudbender.providers.enable(type="test")
13
+ await provider.wait_for("ready")
14
+ yield provider
15
+ await provider.remove()
16
+
17
+ @fixture(scope="session")
18
+ @mark.create
19
+ @mark.asyncio
20
+ @mark.xdist_group("cloudbender_resources")
21
+ async def region(trainml, provider):
22
+ region = await trainml.cloudbender.regions.create(provider_uuid=provider.id,name="test-region",
23
+ public=False,
24
+ storage=dict(mode="local"),)
25
+ await region.wait_for("healthy")
26
+ yield region
27
+ await region.remove()
28
+ await region.wait_for("archived")
@@ -8,14 +8,9 @@ pytestmark = [mark.sdk, mark.integration, mark.cloudbender, mark.providers]
8
8
 
9
9
  @mark.create
10
10
  @mark.asyncio
11
- class GetProvidersTests:
12
- @fixture(scope="class")
13
- async def provider(self, trainml):
14
- provider = await trainml.cloudbender.providers.enable(type="test")
15
- yield provider
16
- await provider.remove()
17
-
18
- async def test_get_providers(self, trainml):
11
+ @mark.xdist_group("cloudbender_resources")
12
+ class GetProviderTests:
13
+ async def test_get_providers(self, trainml, provider):
19
14
  providers = await trainml.cloudbender.providers.list()
20
15
  assert len(providers) > 0
21
16
 
@@ -0,0 +1,42 @@
1
+ import re
2
+ import sys
3
+ import asyncio
4
+ from pytest import mark, fixture
5
+
6
+ pytestmark = [mark.sdk, mark.integration, mark.cloudbender, mark.regions]
7
+
8
+
9
+ @mark.create
10
+ @mark.asyncio
11
+ @mark.xdist_group("cloudbender_resources")
12
+ class GetRegionTests:
13
+ async def test_get_regions(self, trainml, provider, region):
14
+ regions = await trainml.cloudbender.regions.list(provider_uuid=provider.id)
15
+ assert len(regions) > 0
16
+
17
+ async def test_get_region(self, trainml, provider, region):
18
+ response = await trainml.cloudbender.regions.get(provider.id, region.id)
19
+ assert response.id == region.id
20
+
21
+ async def test_region_properties(self, provider, region):
22
+ assert isinstance(region.id, str)
23
+ assert isinstance(region.provider_uuid, str)
24
+ assert isinstance(region.type, str)
25
+ assert region.type == "test"
26
+ assert region.provider_uuid == provider.id
27
+
28
+ async def test_region_str(self, region):
29
+ string = str(region)
30
+ regex = r"^{.*\"region_uuid\": \"" + region.id + r"\".*}$"
31
+ assert isinstance(string, str)
32
+ assert re.match(regex, string)
33
+
34
+ async def test_region_repr(self, region):
35
+ string = repr(region)
36
+ regex = (
37
+ r"^Region\( trainml , \*\*{.*'region_uuid': '"
38
+ + region.id
39
+ + r"'.*}\)$"
40
+ )
41
+ assert isinstance(string, str)
42
+ assert re.match(regex, string)
@@ -0,0 +1,87 @@
1
+ import re
2
+ import sys
3
+ import asyncio
4
+ from pytest import mark, fixture
5
+ from cryptography import x509
6
+ from cryptography.x509.oid import NameOID, ExtendedKeyUsageOID
7
+ from cryptography.hazmat.primitives.asymmetric import rsa
8
+ from cryptography.hazmat.primitives import serialization, hashes
9
+
10
+ pytestmark = [mark.sdk, mark.integration, mark.cloudbender, mark.regions]
11
+
12
+ def get_csr(service_id):
13
+ private_key = rsa.generate_private_key(
14
+ public_exponent=65537,
15
+ key_size=4096
16
+ )
17
+ csr = x509.CertificateSigningRequestBuilder().subject_name(x509.Name([
18
+ x509.NameAttribute(NameOID.COUNTRY_NAME, "US"),
19
+ x509.NameAttribute(NameOID.ORGANIZATION_NAME, "proxiML"),
20
+ x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, service_id),
21
+ x509.NameAttribute(NameOID.COMMON_NAME, "test-client"), # Client identity
22
+ ])).add_extension(
23
+ x509.ExtendedKeyUsage([
24
+ ExtendedKeyUsageOID.CLIENT_AUTH # Client authentication usage
25
+ ]),
26
+ critical=True
27
+ ).sign(private_key, hashes.SHA256())
28
+ return csr.public_bytes(serialization.Encoding.PEM).decode("utf-8")
29
+
30
+ @mark.create
31
+ @mark.asyncio
32
+ @mark.xdist_group("cloudbender_resources")
33
+ class GetServiceTests:
34
+ @fixture(scope="class")
35
+ async def service(self, trainml, region):
36
+ service = await trainml.cloudbender.services.create(
37
+ provider_uuid=region.provider_uuid,
38
+ region_uuid=region.id,
39
+ name="CLI Automated Service",
40
+ type="tcp",
41
+ port="8989",
42
+ public=False,
43
+ )
44
+ await service.wait_for("active")
45
+ yield service
46
+ await service.remove()
47
+ await service.wait_for("archived")
48
+
49
+ async def test_get_services(self, trainml, region,service):
50
+ services = await trainml.cloudbender.services.list(provider_uuid=region.provider_uuid, region_uuid=region.id)
51
+ assert len(services) > 0
52
+
53
+ async def test_get_service(self, trainml, provider, region, service):
54
+ response = await trainml.cloudbender.services.get(provider.id, region.id, service.id)
55
+ assert response.id == service.id
56
+
57
+ async def test_service_properties(self, region, service):
58
+ assert isinstance(service.id, str)
59
+ assert isinstance(service.provider_uuid, str)
60
+ assert isinstance(service.region_uuid, str)
61
+ assert isinstance(service.public, bool)
62
+ assert service.port == "8989"
63
+ assert service.provider_uuid == region.provider_uuid
64
+ assert service.region_uuid == region.id
65
+
66
+ async def test_service_str(self, service):
67
+ string = str(service)
68
+ regex = r"^{.*\"service_id\": \"" + service.id + r"\".*}$"
69
+ assert isinstance(string, str)
70
+ assert re.match(regex, string)
71
+
72
+ async def test_service_repr(self, service):
73
+ string = repr(service)
74
+ regex = (
75
+ r"^Service\( trainml , \*\*{.*'service_id': '"
76
+ + service.id
77
+ + r"'.*}\)$"
78
+ )
79
+ assert isinstance(string, str)
80
+ assert re.match(regex, string)
81
+
82
+ async def test_service_certificate(self, service):
83
+ service = await service.generate_certificate()
84
+ assert isinstance(service._service.get("auth_cert"), str)
85
+ csr = get_csr(service.id)
86
+ certificate = await service.sign_client_certificate(csr)
87
+ assert isinstance(certificate, str)
@@ -40,7 +40,7 @@ def env(request):
40
40
  yield ENVS[env]
41
41
 
42
42
 
43
- @fixture(scope="module")
43
+ @fixture(scope="session")
44
44
  def trainml(env):
45
45
  trainml = TrainML(**env)
46
46
  yield trainml
@@ -1,7 +1,8 @@
1
- from pytest import fixture
1
+ from pytest import fixture, mark
2
2
 
3
3
 
4
4
  @fixture(scope="module")
5
+ @mark.xdist_group("project_resources")
5
6
  async def project(trainml):
6
7
  project = await trainml.projects.create(
7
8
  name="New Project", copy_credentials=False, copy_secrets=False
@@ -8,6 +8,7 @@ pytestmark = [mark.sdk, mark.integration, mark.projects]
8
8
 
9
9
  @mark.create
10
10
  @mark.asyncio
11
+ @mark.xdist_group("project_resources")
11
12
  class ProjectCredentialsTests:
12
13
  @fixture(scope="class")
13
14
  async def project_credential(self, project):
@@ -8,6 +8,7 @@ pytestmark = [mark.sdk, mark.integration, mark.projects]
8
8
 
9
9
  @mark.create
10
10
  @mark.asyncio
11
+ @mark.xdist_group("project_resources")
11
12
  class ProjectDataConnectorsTests:
12
13
  @fixture(scope="class")
13
14
  async def project_data_connector(self, project):
@@ -8,6 +8,7 @@ pytestmark = [mark.sdk, mark.integration, mark.projects]
8
8
 
9
9
  @mark.create
10
10
  @mark.asyncio
11
+ @mark.xdist_group("project_resources")
11
12
  class ProjectDatastoresTests:
12
13
  @fixture(scope="class")
13
14
  async def project_datastore(self, project):
@@ -8,6 +8,7 @@ pytestmark = [mark.sdk, mark.integration, mark.projects]
8
8
 
9
9
  @mark.create
10
10
  @mark.asyncio
11
+ @mark.xdist_group("project_resources")
11
12
  class GetProjectsTests:
12
13
  async def test_get_projects(self, trainml):
13
14
  projects = await trainml.projects.list()
@@ -0,0 +1,50 @@
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
+ @mark.xdist_group("project_resources")
12
+ class ProjectMembersTests:
13
+ @fixture(scope="class")
14
+ async def project_member(self, project):
15
+ member = await project.members.add("test.account@proximl.ai","read","read","read","read","read")
16
+ yield member
17
+ await project.members.remove("test.account@proximl.ai")
18
+
19
+ async def test_list_project_members(self, project):
20
+ members = await project.members.list()
21
+ assert len(members) > 0
22
+
23
+ async def test_project_member_properties(self, project, project_member):
24
+ assert isinstance(project_member.id, str)
25
+ assert isinstance(project_member.email, str)
26
+ assert isinstance(project_member.project_uuid, str)
27
+ assert isinstance(project_member.owner, bool)
28
+ assert isinstance(project_member.job, str)
29
+ assert isinstance(project_member.dataset, str)
30
+ assert isinstance(project_member.model, str)
31
+ assert isinstance(project_member.checkpoint, str)
32
+ assert isinstance(project_member.volume, str)
33
+ assert project.id == project_member.project_uuid
34
+ assert project_member.id == "test.account@proximl.ai"
35
+
36
+ async def test_project_member_str(self, project_member):
37
+ string = str(project_member)
38
+ regex = r"^{.*\"email\": \"" + project_member.email + r"\".*}$"
39
+ assert isinstance(string, str)
40
+ assert re.match(regex, string)
41
+
42
+ async def test_project_member_repr(self, project_member):
43
+ string = repr(project_member)
44
+ regex = (
45
+ r"^ProjectMember\( trainml , \*\*{.*'email': '"
46
+ + project_member.email
47
+ + r"'.*}\)$"
48
+ )
49
+ assert isinstance(string, str)
50
+ assert re.match(regex, string)
@@ -8,6 +8,7 @@ pytestmark = [mark.sdk, mark.integration, mark.projects]
8
8
 
9
9
  @mark.create
10
10
  @mark.asyncio
11
+ @mark.xdist_group("project_resources")
11
12
  class ProjectSecretsTests:
12
13
  @fixture(scope="class")
13
14
  async def project_secret(self, project):
@@ -8,6 +8,7 @@ pytestmark = [mark.sdk, mark.integration, mark.projects]
8
8
 
9
9
  @mark.create
10
10
  @mark.asyncio
11
+ @mark.xdist_group("project_resources")
11
12
  class ProjectServicesTests:
12
13
  @fixture(scope="class")
13
14
  async def project_service(self, project):
@@ -8,6 +8,7 @@ pytestmark = [mark.sdk, mark.integration, mark.checkpoints]
8
8
 
9
9
  @mark.create
10
10
  @mark.asyncio
11
+ @mark.xdist_group("checkpoints")
11
12
  class GetCheckpointTests:
12
13
  @fixture(scope="class")
13
14
  async def checkpoint(self, trainml):
@@ -8,6 +8,7 @@ pytestmark = [mark.sdk, mark.integration, mark.datasets]
8
8
 
9
9
  @mark.create
10
10
  @mark.asyncio
11
+ @mark.xdist_group("datasets")
11
12
  class GetDatasetTests:
12
13
  @fixture(scope="class")
13
14
  async def dataset(self, trainml):
@@ -20,6 +20,7 @@ def extract_domain_suffix(hostname):
20
20
 
21
21
 
22
22
  @fixture(scope="class")
23
+ @mark.xdist_group("job_lifecycle")
23
24
  async def job(trainml):
24
25
  job = await trainml.jobs.create(
25
26
  name="CLI Automated Tests - Job Lifecycle",
@@ -38,6 +39,7 @@ async def job(trainml):
38
39
 
39
40
  @mark.create
40
41
  @mark.asyncio
42
+ @mark.xdist_group("job_lifecycle")
41
43
  class JobLifeCycleTests:
42
44
  async def test_wait_for_running(self, job):
43
45
  assert job.status != "running"
@@ -413,6 +415,7 @@ class JobAPIWorkerValidationTests:
413
415
 
414
416
  @mark.create
415
417
  @mark.asyncio
418
+ @mark.xdist_group("job_io")
416
419
  class JobIOTests:
417
420
  async def test_job_local_output(self, trainml, capsys):
418
421
  temp_dir = tempfile.TemporaryDirectory()
@@ -423,11 +426,11 @@ class JobIOTests:
423
426
  disk_size=10,
424
427
  workers=["python $ML_MODEL_PATH/tensorflow/main.py"],
425
428
  environment=dict(
426
- type="DEEPLEARNING_PY310",
429
+ type="DEEPLEARNING_PY312",
427
430
  env=[
428
431
  dict(
429
432
  key="CHECKPOINT_FILE",
430
- value="model.ckpt-0050",
433
+ value="model.ckpt-0050.weights.h5",
431
434
  )
432
435
  ],
433
436
  ),
@@ -469,7 +472,7 @@ class JobIOTests:
469
472
  sys.stderr.write(captured.err)
470
473
  assert "Epoch 1/2" in captured.out
471
474
  assert "Epoch 2/2" in captured.out
472
- assert "adding: model.ckpt-0001.data-00000-of-00001" in captured.out
475
+ assert "adding: model.ckpt-0001" in captured.out
473
476
  assert "Send complete" in captured.out
474
477
 
475
478
  async def test_job_model_input_and_output(self, trainml, capsys):
@@ -527,6 +530,7 @@ class JobIOTests:
527
530
 
528
531
  @mark.create
529
532
  @mark.asyncio
533
+ @mark.xdist_group("job_types")
530
534
  class JobTypeTests:
531
535
  async def test_endpoint(self, trainml):
532
536
 
@@ -558,7 +562,7 @@ class JobTypeTests:
558
562
  assert job.url
559
563
  assert extract_domain_suffix(urlparse(job.url).hostname) == "proximl.cloud"
560
564
  tries = 0
561
- await asyncio.sleep(30)
565
+ await asyncio.sleep(180) ## downloading weights can be slow
562
566
  async with aiohttp.ClientSession() as session:
563
567
  retry = True
564
568
  while retry:
@@ -640,7 +644,7 @@ class JobTypeTests:
640
644
  assert "Epoch 2/2" in captured.out
641
645
  assert "Uploading s3://trainml-example/output/resnet_cifar10" in captured.out
642
646
  assert (
643
- "upload: ./model.ckpt-0002.data-00000-of-00001 to s3://trainml-example/output/resnet_cifar10/model.ckpt-0002.data-00000-of-00001"
647
+ "upload: ./model.ckpt-0002.weights.h5 to s3://trainml-example/output/resnet_cifar10/model.ckpt-0002.weights.h5"
644
648
  in captured.out
645
649
  )
646
650
  assert "Upload complete" in captured.out
@@ -648,6 +652,7 @@ class JobTypeTests:
648
652
 
649
653
  @mark.create
650
654
  @mark.asyncio
655
+ @mark.xdist_group("job_features")
651
656
  class JobFeatureTests:
652
657
  async def test_cpu_instance(self, trainml, capsys):
653
658
  job = await trainml.jobs.create(
@@ -713,9 +718,9 @@ class JobFeatureTests:
713
718
  sys.stderr.write(captured.err)
714
719
  upload_contents = os.listdir(temp_dir.name)
715
720
  temp_dir.cleanup()
716
- assert len(upload_contents) > 4
721
+ assert len(upload_contents) >= 3
717
722
  assert any(
718
- "model.ckpt-0002.data-00000-of-00001" in content
723
+ "model.ckpt-0002" in content
719
724
  for content in upload_contents
720
725
  )
721
726
 
@@ -724,5 +729,5 @@ class JobFeatureTests:
724
729
  sys.stderr.write(captured.err)
725
730
  assert "Epoch 1/2" in captured.out
726
731
  assert "Epoch 2/2" in captured.out
727
- assert "Number of regular files transferred: 7" in captured.out
732
+ assert "Number of regular files transferred: 4" in captured.out
728
733
  assert "Send complete" in captured.out
@@ -8,6 +8,7 @@ pytestmark = [mark.sdk, mark.integration, mark.models]
8
8
 
9
9
  @mark.create
10
10
  @mark.asyncio
11
+ @mark.xdist_group("models")
11
12
  class GetModelTests:
12
13
  @fixture(scope="class")
13
14
  async def model(self, trainml):
@@ -8,6 +8,7 @@ pytestmark = [mark.sdk, mark.integration, mark.volumes]
8
8
 
9
9
  @mark.create
10
10
  @mark.asyncio
11
+ @mark.xdist_group("volumes")
11
12
  class GetVolumeTests:
12
13
  @fixture(scope="class")
13
14
  async def volume(self, trainml):
@@ -0,0 +1,107 @@
1
+ import re
2
+ import json
3
+ import logging
4
+ from unittest.mock import AsyncMock, patch
5
+ from pytest import mark, fixture, raises
6
+ from aiohttp import WSMessage, WSMsgType
7
+
8
+ import trainml.projects.members as specimen
9
+ from trainml.exceptions import (
10
+ ApiError,
11
+ SpecificationError,
12
+ TrainMLException,
13
+ )
14
+
15
+ pytestmark = [mark.sdk, mark.unit, mark.projects]
16
+
17
+
18
+ @fixture
19
+ def project_members(mock_trainml):
20
+ yield specimen.ProjectMembers(mock_trainml, project_id="1")
21
+
22
+
23
+ @fixture
24
+ def project_member(mock_trainml):
25
+ yield specimen.ProjectMember(
26
+ mock_trainml,
27
+ id="owner@gmail.com",
28
+ email="owner@gmail.com",
29
+ project_uuid="proj-id-1",
30
+ owner= True,
31
+ job= "all",
32
+ dataset= "all",
33
+ model= "all",
34
+ checkpoint="all",
35
+ volume= "all"
36
+ )
37
+
38
+
39
+ class ProjectMembersTests:
40
+ @mark.asyncio
41
+ async def test_project_members_list(self, project_members, mock_trainml):
42
+ api_response = [
43
+ {
44
+ "project_uuid": "proj-id-1",
45
+ "email": "owner@gmail.com",
46
+ "createdAt": "2024-09-04T00:42:39.529Z",
47
+ "updatedAt": "2024-09-04T00:42:39.529Z",
48
+ "owner": True,
49
+ "job": "all",
50
+ "dataset": "all",
51
+ "model": "all",
52
+ "checkpoint": "all",
53
+ "volume": "all"
54
+ },
55
+ {
56
+ "project_uuid": "proj-id-1",
57
+ "email": "non-owner@gmail.com",
58
+ "createdAt": "2024-09-04T00:42:39.529Z",
59
+ "updatedAt": "2024-09-04T00:42:39.529Z",
60
+ "owner": False,
61
+ "job": "all",
62
+ "dataset": "all",
63
+ "model": "all",
64
+ "checkpoint": "read",
65
+ "volume": "read"
66
+ },
67
+ ]
68
+ mock_trainml._query = AsyncMock(return_value=api_response)
69
+ resp = await project_members.list()
70
+ mock_trainml._query.assert_called_once_with(
71
+ "/project/1/access", "GET", dict()
72
+ )
73
+ assert len(resp) == 2
74
+
75
+
76
+ class ProjectMemberTests:
77
+ def test_project_member_properties(self, project_member):
78
+ assert isinstance(project_member.id, str)
79
+ assert isinstance(project_member.email, str)
80
+ assert isinstance(project_member.project_uuid, str)
81
+ assert isinstance(project_member.owner, bool)
82
+ assert isinstance(project_member.job, str)
83
+ assert isinstance(project_member.dataset, str)
84
+ assert isinstance(project_member.model, str)
85
+ assert isinstance(project_member.checkpoint, str)
86
+ assert isinstance(project_member.volume, str)
87
+
88
+ def test_project_member_str(self, project_member):
89
+ string = str(project_member)
90
+ regex = r"^{.*\"id\": \"" + project_member.id + r"\".*}$"
91
+ assert isinstance(string, str)
92
+ assert re.match(regex, string)
93
+
94
+ def test_project_member_repr(self, project_member):
95
+ string = repr(project_member)
96
+ regex = (
97
+ r"^ProjectMember\( trainml , \*\*{.*'id': '"
98
+ + project_member.id
99
+ + r"'.*}\)$"
100
+ )
101
+ assert isinstance(string, str)
102
+ assert re.match(regex, string)
103
+
104
+ def test_project_member_bool(self, project_member, mock_trainml):
105
+ empty_project_member = specimen.ProjectMember(mock_trainml)
106
+ assert bool(project_member)
107
+ assert not bool(empty_project_member)
@@ -13,5 +13,5 @@ logging.basicConfig(
13
13
  logger = logging.getLogger(__name__)
14
14
 
15
15
 
16
- __version__ = "0.5.13"
16
+ __version__ = "0.5.15"
17
17
  __all__ = "TrainML"
@@ -508,7 +508,7 @@ class AWSSRP(object):
508
508
 
509
509
 
510
510
  class Auth(object):
511
- def __init__(self, config_dir, domain_suffix="trainml.ai", **kwargs):
511
+ def __init__(self, config_dir, domain_suffix="proximl.ai", **kwargs):
512
512
  try:
513
513
  with open(f"{config_dir}/environment.json", "r") as file:
514
514
  env_str = file.read().replace("\n", "")