anyscale 0.24.88__py3-none-any.whl → 0.24.91__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 (115) hide show
  1. anyscale/__init__.py +46 -0
  2. anyscale/_private/anyscale_client/anyscale_client.py +148 -28
  3. anyscale/_private/anyscale_client/common.py +74 -1
  4. anyscale/_private/anyscale_client/fake_anyscale_client.py +165 -1
  5. anyscale/_private/docgen/README.md +1 -1
  6. anyscale/_private/docgen/__main__.py +62 -19
  7. anyscale/_private/docgen/api.md +0 -20
  8. anyscale/_private/docgen/generator.py +3 -2
  9. anyscale/_private/docgen/models.md +1 -46
  10. anyscale/_private/workload/workload_config.py +1 -1
  11. anyscale/aggregated_instance_usage/__init__.py +1 -1
  12. anyscale/aggregated_instance_usage/commands.py +2 -4
  13. anyscale/aggregated_instance_usage/models.py +8 -8
  14. anyscale/client/README.md +15 -22
  15. anyscale/client/openapi_client/__init__.py +10 -14
  16. anyscale/client/openapi_client/api/default_api.py +634 -957
  17. anyscale/client/openapi_client/models/__init__.py +10 -14
  18. anyscale/client/openapi_client/models/{session_event_types.py → cloud_deployment_config.py} +35 -24
  19. anyscale/client/openapi_client/models/{platformfinetuningjob_response.py → clouddeploymentconfig_response.py} +11 -11
  20. anyscale/client/openapi_client/models/{company_size.py → cluster_size.py} +10 -10
  21. anyscale/client/openapi_client/models/cluster_status_details.py +2 -1
  22. anyscale/client/openapi_client/models/create_experimental_workspace.py +29 -1
  23. anyscale/client/openapi_client/models/{resubmit_ft_job_request.py → describe_machine_pool_request.py} +21 -20
  24. anyscale/client/openapi_client/models/describe_machine_pool_response.py +123 -0
  25. anyscale/client/openapi_client/models/{fine_tuning_job_status.py → describemachinepoolresponse_response.py} +34 -16
  26. anyscale/client/openapi_client/models/machine_allocation_state.py +3 -1
  27. anyscale/client/openapi_client/models/machine_state_info.py +326 -0
  28. anyscale/client/openapi_client/models/organization_marketing_questions.py +80 -54
  29. anyscale/client/openapi_client/models/request_state_info.py +210 -0
  30. anyscale/client/openapi_client/models/{sessionevent_list_response.py → scheduler_info.py} +43 -38
  31. anyscale/client/openapi_client/models/usage_by_cluster.py +28 -1
  32. anyscale/client/openapi_client/models/usage_by_user.py +30 -3
  33. anyscale/client/openapi_client/models/workload_info.py +210 -0
  34. anyscale/cloud/__init__.py +83 -0
  35. anyscale/cloud/_private/cloud_sdk.py +25 -0
  36. anyscale/cloud/commands.py +45 -0
  37. anyscale/cloud/models.py +91 -0
  38. anyscale/cluster_compute.py +1 -1
  39. anyscale/commands/aggregated_instance_usage_commands.py +4 -4
  40. anyscale/commands/cloud_commands.py +38 -2
  41. anyscale/commands/command_examples.py +61 -0
  42. anyscale/commands/job_commands.py +15 -3
  43. anyscale/commands/machine_pool_commands.py +113 -1
  44. anyscale/commands/organization_invitation_commands.py +98 -0
  45. anyscale/commands/project_commands.py +52 -2
  46. anyscale/commands/resource_quota_commands.py +98 -11
  47. anyscale/commands/service_commands.py +1 -1
  48. anyscale/commands/session_commands_hidden.py +5 -1
  49. anyscale/commands/user_commands.py +1 -1
  50. anyscale/commands/util.py +1 -1
  51. anyscale/commands/workspace_commands.py +1 -1
  52. anyscale/connect.py +1 -1
  53. anyscale/connect_utils/project.py +7 -4
  54. anyscale/controllers/cloud_controller.py +6 -6
  55. anyscale/controllers/cloud_functional_verification_controller.py +1 -1
  56. anyscale/controllers/cluster_controller.py +2 -2
  57. anyscale/controllers/compute_config_controller.py +1 -1
  58. anyscale/controllers/experimental_integrations_controller.py +1 -1
  59. anyscale/controllers/job_controller.py +8 -3
  60. anyscale/controllers/list_controller.py +2 -2
  61. anyscale/controllers/machine_pool_controller.py +12 -1
  62. anyscale/controllers/project_controller.py +4 -3
  63. anyscale/controllers/schedule_controller.py +1 -1
  64. anyscale/controllers/service_controller.py +1 -1
  65. anyscale/controllers/workspace_controller.py +1 -1
  66. anyscale/models/job_model.py +1 -1
  67. anyscale/organization_invitation/__init__.py +61 -0
  68. anyscale/organization_invitation/_private/organization_invitation_sdk.py +24 -0
  69. anyscale/organization_invitation/commands.py +84 -0
  70. anyscale/organization_invitation/models.py +45 -0
  71. anyscale/project/__init__.py +35 -0
  72. anyscale/project/_private/project_sdk.py +27 -0
  73. anyscale/project/commands.py +56 -0
  74. anyscale/project/models.py +91 -0
  75. anyscale/{project.py → project_utils.py} +3 -4
  76. anyscale/resource_quota/__init__.py +99 -0
  77. anyscale/resource_quota/_private/resource_quota_sdk.py +111 -0
  78. anyscale/resource_quota/commands.py +150 -0
  79. anyscale/resource_quota/models.py +303 -0
  80. anyscale/scripts.py +4 -0
  81. anyscale/sdk/anyscale_client/__init__.py +0 -5
  82. anyscale/sdk/anyscale_client/api/default_api.py +0 -150
  83. anyscale/sdk/anyscale_client/models/__init__.py +0 -5
  84. anyscale/sdk/anyscale_client/models/cluster_status_details.py +2 -1
  85. anyscale/sdk/anyscale_client/sdk.py +1 -1
  86. anyscale/user/__init__.py +1 -1
  87. anyscale/user/commands.py +1 -1
  88. anyscale/user/models.py +25 -15
  89. anyscale/util.py +15 -0
  90. anyscale/utils/cloud_utils.py +1 -1
  91. anyscale/version.py +1 -1
  92. anyscale/workspace_utils.py +1 -1
  93. {anyscale-0.24.88.dist-info → anyscale-0.24.91.dist-info}/METADATA +1 -5
  94. {anyscale-0.24.88.dist-info → anyscale-0.24.91.dist-info}/RECORD +100 -94
  95. anyscale/client/openapi_client/models/create_fine_tuning_hyperparameters.py +0 -156
  96. anyscale/client/openapi_client/models/create_fine_tuning_job_product_request.py +0 -353
  97. anyscale/client/openapi_client/models/finish_ft_job_request.py +0 -204
  98. anyscale/client/openapi_client/models/log_level_types.py +0 -100
  99. anyscale/client/openapi_client/models/platform_fine_tuning_job.py +0 -577
  100. anyscale/client/openapi_client/models/platformfinetuningjob_list_response.py +0 -147
  101. anyscale/client/openapi_client/models/session_event.py +0 -267
  102. anyscale/client/openapi_client/models/session_event_cause.py +0 -150
  103. anyscale/controllers/resource_quota_controller.py +0 -183
  104. anyscale/sdk/anyscale_client/models/log_level_types.py +0 -100
  105. anyscale/sdk/anyscale_client/models/session_event.py +0 -267
  106. anyscale/sdk/anyscale_client/models/session_event_cause.py +0 -150
  107. anyscale/sdk/anyscale_client/models/session_event_types.py +0 -111
  108. anyscale/sdk/anyscale_client/models/sessionevent_list_response.py +0 -147
  109. anyscale/utils/imports/azure.py +0 -14
  110. /anyscale/{cloud.py → cloud_utils.py} +0 -0
  111. {anyscale-0.24.88.dist-info → anyscale-0.24.91.dist-info}/LICENSE +0 -0
  112. {anyscale-0.24.88.dist-info → anyscale-0.24.91.dist-info}/NOTICE +0 -0
  113. {anyscale-0.24.88.dist-info → anyscale-0.24.91.dist-info}/WHEEL +0 -0
  114. {anyscale-0.24.88.dist-info → anyscale-0.24.91.dist-info}/entry_points.txt +0 -0
  115. {anyscale-0.24.88.dist-info → anyscale-0.24.91.dist-info}/top_level.txt +0 -0
@@ -29,7 +29,7 @@ from anyscale.client.openapi_client.models.decorated_production_job import (
29
29
  from anyscale.client.openapi_client.models.ha_job_states import HaJobStates
30
30
  from anyscale.controllers.base_controller import BaseController
31
31
  from anyscale.models.job_model import JobConfig
32
- from anyscale.project import infer_project_id
32
+ from anyscale.project_utils import infer_project_id
33
33
  from anyscale.sdk.anyscale_client.models.job import Job
34
34
  from anyscale.sdk.anyscale_client.models.jobs_query import JobsQuery
35
35
  from anyscale.sdk.anyscale_client.models.jobs_sort_field import JobsSortField
@@ -65,6 +65,8 @@ _PENDING_STATES = {
65
65
  HaJobStates.RESTARTING,
66
66
  }
67
67
 
68
+ DEFAULT_PAGE_LIMIT = 500
69
+
68
70
 
69
71
  class MiniJobRun(BaseModel):
70
72
  last_job_run_id: str
@@ -292,6 +294,7 @@ class JobController(BaseController):
292
294
  project_id: Optional[str],
293
295
  include_archived: bool,
294
296
  max_items: int,
297
+ states: List[str],
295
298
  ) -> None:
296
299
  """
297
300
  This function will list jobs.
@@ -342,7 +345,8 @@ class JobController(BaseController):
342
345
  creator_id=creator_id,
343
346
  type_filter="BATCH_JOB",
344
347
  archive_status="ALL" if include_archived else "NOT_ARCHIVED",
345
- count=10,
348
+ count=DEFAULT_PAGE_LIMIT,
349
+ state_filter=states,
346
350
  )
347
351
  jobs_list.extend(resp.results)
348
352
  paging_token = resp.metadata.next_paging_token
@@ -354,8 +358,9 @@ class JobController(BaseController):
354
358
  creator_id=creator_id,
355
359
  type_filter="BATCH_JOB",
356
360
  archive_status="ALL" if include_archived else "NOT_ARCHIVED",
357
- count=10,
361
+ count=DEFAULT_PAGE_LIMIT,
358
362
  paging_token=paging_token,
363
+ state_filter=states,
359
364
  )
360
365
  jobs_list.extend(resp.results)
361
366
  paging_token = resp.metadata.next_paging_token
@@ -9,10 +9,10 @@ from typing import Any, Dict, List, Optional, Tuple
9
9
  import tabulate
10
10
 
11
11
  from anyscale.cli_logger import BlockLogger
12
- from anyscale.cloud import get_cloud_json_from_id
12
+ from anyscale.cloud_utils import get_cloud_json_from_id
13
13
  from anyscale.controllers.base_controller import BaseController
14
14
  from anyscale.formatters import clouds_formatter
15
- from anyscale.project import (
15
+ from anyscale.project_utils import (
16
16
  get_project_id,
17
17
  load_project_or_throw,
18
18
  )
@@ -10,11 +10,13 @@ from anyscale.client.openapi_client.models import (
10
10
  CreateMachinePoolRequest,
11
11
  CreateMachinePoolResponse,
12
12
  DeleteMachinePoolRequest,
13
+ DescribeMachinePoolRequest,
14
+ DescribeMachinePoolResponse,
13
15
  DetachMachinePoolFromCloudRequest,
14
16
  ListMachinePoolsResponse,
15
17
  UpdateMachinePoolRequest,
16
18
  )
17
- from anyscale.cloud import get_cloud_id_and_name
19
+ from anyscale.cloud_utils import get_cloud_id_and_name
18
20
  from anyscale.controllers.base_controller import BaseController
19
21
 
20
22
 
@@ -65,6 +67,15 @@ class MachinePoolController(BaseController):
65
67
  UpdateMachinePoolRequest(machine_pool_name=machine_pool_name, spec=spec,)
66
68
  )
67
69
 
70
+ def describe_machine_pool(
71
+ self, machine_pool_name: str,
72
+ ) -> DescribeMachinePoolResponse:
73
+ return self.api_client.describe_machine_pool_api_v2_machine_pools_describe_post(
74
+ describe_machine_pool_request=DescribeMachinePoolRequest(
75
+ machine_pool_name=machine_pool_name,
76
+ )
77
+ ).result
78
+
68
79
  def list_machine_pools(self,) -> ListMachinePoolsResponse:
69
80
  response = self.api_client.list_machine_pools_api_v2_machine_pools_get().result
70
81
  return response
@@ -8,9 +8,10 @@ import tabulate
8
8
 
9
9
  import anyscale
10
10
  from anyscale.cli_logger import BlockLogger
11
- from anyscale.cloud import get_cloud_id_and_name
11
+ from anyscale.cloud_utils import get_cloud_id_and_name
12
12
  from anyscale.controllers.base_controller import BaseController
13
- from anyscale.project import ( # pylint:disable=private-import
13
+ import anyscale.project_utils
14
+ from anyscale.project_utils import ( # pylint:disable=private-import
14
15
  attach_to_project_with_id,
15
16
  create_new_proj_def,
16
17
  get_proj_id_from_name,
@@ -102,7 +103,7 @@ class ProjectController(BaseController):
102
103
  "and compute configs."
103
104
  )
104
105
  self.log.warning(message)
105
- project_id_path = anyscale.project.ANYSCALE_PROJECT_FILE
106
+ project_id_path = anyscale.project_utils.ANYSCALE_PROJECT_FILE
106
107
 
107
108
  if project_id:
108
109
  # Exactly one of project_id or name must be provided
@@ -22,7 +22,7 @@ from anyscale.client.openapi_client.models.schedule_config import (
22
22
  )
23
23
  from anyscale.controllers.base_controller import BaseController
24
24
  from anyscale.models.job_model import JobConfig
25
- from anyscale.project import infer_project_id
25
+ from anyscale.project_utils import infer_project_id
26
26
  from anyscale.tables import SchedulesTable
27
27
  from anyscale.util import (
28
28
  AnyscaleEndpointFormatter,
@@ -19,7 +19,7 @@ from anyscale.client.openapi_client.models.decorated_production_service_v2_api_m
19
19
  )
20
20
  from anyscale.controllers.base_controller import BaseController
21
21
  from anyscale.models.service_model import ServiceConfig
22
- from anyscale.project import infer_project_id
22
+ from anyscale.project_utils import infer_project_id
23
23
  from anyscale.sdk.anyscale_client.models import (
24
24
  ApplyServiceModel,
25
25
  RollbackServiceModel,
@@ -15,7 +15,7 @@ from anyscale.client.openapi_client.models.experimental_workspace import (
15
15
  )
16
16
  from anyscale.controllers.base_controller import BaseController
17
17
  from anyscale.feature_flags import FLAG_DEFAULT_WORKING_DIR_FOR_PROJ
18
- from anyscale.project import get_default_project
18
+ from anyscale.project_utils import get_default_project
19
19
  from anyscale.util import get_endpoint
20
20
  from anyscale.utils.workspace_utils import (
21
21
  extract_workspace_parameters,
@@ -23,7 +23,7 @@ from anyscale.cluster_env import (
23
23
  get_default_cluster_env_build,
24
24
  validate_successful_build,
25
25
  )
26
- from anyscale.project import (
26
+ from anyscale.project_utils import (
27
27
  get_parent_cloud_id_and_name_of_project,
28
28
  get_proj_id_from_name,
29
29
  )
@@ -0,0 +1,61 @@
1
+ from typing import List, Optional
2
+
3
+ from anyscale._private.anyscale_client import AnyscaleClientInterface
4
+ from anyscale._private.sdk import sdk_docs
5
+ from anyscale._private.sdk.base_sdk import Timer
6
+ from anyscale.cli_logger import BlockLogger
7
+ from anyscale.organization_invitation._private.organization_invitation_sdk import (
8
+ PrivateOrganizationInvitationSDK,
9
+ )
10
+ from anyscale.organization_invitation.commands import (
11
+ _CREATE_ARG_DOCSTRINGS,
12
+ _CREATE_EXAMPLE,
13
+ _DELETE_ARG_DOCSTRINGS,
14
+ _DELETE_EXAMPLE,
15
+ _LIST_ARG_DOCSTRINGS,
16
+ _LIST_EXAMPLE,
17
+ create,
18
+ delete,
19
+ list,
20
+ )
21
+
22
+
23
+ class OrganizationInvitationSDK:
24
+ def __init__(
25
+ self,
26
+ *,
27
+ client: Optional[AnyscaleClientInterface] = None,
28
+ logger: Optional[BlockLogger] = None,
29
+ timer: Optional[Timer] = None,
30
+ ):
31
+ self._private_sdk = PrivateOrganizationInvitationSDK(
32
+ client=client, logger=logger, timer=timer
33
+ )
34
+
35
+ @sdk_docs(
36
+ doc_py_example=_CREATE_EXAMPLE, arg_docstrings=_CREATE_ARG_DOCSTRINGS,
37
+ )
38
+ def create( # noqa: F811
39
+ self, emails: List[str],
40
+ ):
41
+ """Creates organization invitations for the provided emails
42
+ """
43
+ return self._private_sdk.create(emails=emails)
44
+
45
+ @sdk_docs(
46
+ doc_py_example=_LIST_EXAMPLE, arg_docstrings=_LIST_ARG_DOCSTRINGS,
47
+ )
48
+ def list(self): # noqa: F811
49
+ """Lists organization invitations
50
+ """
51
+ return self._private_sdk.list()
52
+
53
+ @sdk_docs(
54
+ doc_py_example=_DELETE_EXAMPLE, arg_docstrings=_DELETE_ARG_DOCSTRINGS,
55
+ )
56
+ def delete( # noqa: F811
57
+ self, email: str,
58
+ ):
59
+ """Deletes an organization invitation
60
+ """
61
+ return self._private_sdk.delete(email=email)
@@ -0,0 +1,24 @@
1
+ from typing import List, Tuple
2
+
3
+ from anyscale._private.sdk.base_sdk import BaseSDK
4
+ from anyscale.organization_invitation.models import OrganizationInvitation
5
+
6
+
7
+ class PrivateOrganizationInvitationSDK(BaseSDK):
8
+ def create(self, emails: List[str]) -> Tuple[List[str], List[str]]:
9
+ return self.client.create_organization_invitations(emails=emails)
10
+
11
+ def list(self) -> List[OrganizationInvitation]:
12
+ invitations = self.client.list_organization_invitations()
13
+ return [
14
+ OrganizationInvitation(
15
+ id=invitation.id,
16
+ email=invitation.email,
17
+ created_at=invitation.created_at,
18
+ expires_at=invitation.expires_at,
19
+ )
20
+ for invitation in invitations
21
+ ]
22
+
23
+ def delete(self, email: str) -> str:
24
+ return self.client.delete_organization_invitation(email=email).email
@@ -0,0 +1,84 @@
1
+ from typing import Dict, List, Tuple
2
+
3
+ from anyscale._private.sdk import sdk_command
4
+ from anyscale.organization_invitation._private.organization_invitation_sdk import (
5
+ PrivateOrganizationInvitationSDK,
6
+ )
7
+ from anyscale.organization_invitation.models import OrganizationInvitation
8
+
9
+
10
+ _ORGANIZATION_INVITATION_SDK_SINGLETON_KEY = "organization_invitation_sdk"
11
+
12
+ _CREATE_EXAMPLE = """
13
+ import anyscale
14
+
15
+ anyscale.organization_invitation.create(emails=["test1@anyscale.com","test2@anyscale.com"])
16
+ """
17
+
18
+ _CREATE_ARG_DOCSTRINGS = {
19
+ "emails": "The emails to send the organization invitations to."
20
+ }
21
+
22
+ _LIST_EXAMPLE = """
23
+ import anyscale
24
+
25
+ anyscale.organization_invitation.list()
26
+ """
27
+
28
+ _LIST_ARG_DOCSTRINGS: Dict[str, str] = {}
29
+
30
+ _DELETE_EXAMPLE = """
31
+ import anyscale
32
+
33
+ anyscale.organization_invitation.delete(email="test@anyscale.com")
34
+ """
35
+
36
+ _DELETE_ARG_DOCSTRINGS = {
37
+ "email": "The email of the organization invitation to delete."
38
+ }
39
+
40
+
41
+ @sdk_command(
42
+ _ORGANIZATION_INVITATION_SDK_SINGLETON_KEY,
43
+ PrivateOrganizationInvitationSDK,
44
+ doc_py_example=_CREATE_EXAMPLE,
45
+ arg_docstrings=_CREATE_ARG_DOCSTRINGS,
46
+ )
47
+ def create(
48
+ emails: List[str], *, _sdk: PrivateOrganizationInvitationSDK
49
+ ) -> Tuple[List[str], List[str]]:
50
+ """Creates organization invitations for the provided emails.
51
+
52
+ Returns a tuple of successful emails and error messages.
53
+ """
54
+ return _sdk.create(emails=emails)
55
+
56
+
57
+ @sdk_command(
58
+ _ORGANIZATION_INVITATION_SDK_SINGLETON_KEY,
59
+ PrivateOrganizationInvitationSDK,
60
+ doc_py_example=_LIST_EXAMPLE,
61
+ arg_docstrings=_LIST_ARG_DOCSTRINGS,
62
+ )
63
+ def list( # noqa: A001
64
+ *, _sdk: PrivateOrganizationInvitationSDK
65
+ ) -> List[OrganizationInvitation]:
66
+ """Lists organization invitations.
67
+
68
+ Returns a list of organization invitations.
69
+ """
70
+ return _sdk.list()
71
+
72
+
73
+ @sdk_command(
74
+ _ORGANIZATION_INVITATION_SDK_SINGLETON_KEY,
75
+ PrivateOrganizationInvitationSDK,
76
+ doc_py_example=_DELETE_EXAMPLE,
77
+ arg_docstrings=_DELETE_ARG_DOCSTRINGS,
78
+ )
79
+ def delete(email: str, *, _sdk: PrivateOrganizationInvitationSDK) -> str:
80
+ """Deletes an organization invitation.
81
+
82
+ Returns the email of the deleted organization invitation.
83
+ """
84
+ return _sdk.delete(email)
@@ -0,0 +1,45 @@
1
+ from dataclasses import dataclass, field
2
+ from datetime import datetime
3
+
4
+ from anyscale._private.models import ModelBase
5
+
6
+
7
+ @dataclass(frozen=True)
8
+ class OrganizationInvitation(ModelBase):
9
+ """Organization invitation model.
10
+ """
11
+
12
+ __doc_py_example__ = """\
13
+ import anyscale
14
+ from anyscale.organization_invitation.models import OrganizationInvitation
15
+
16
+ organization_invitations: List[OrganizationInvitation] = anyscale.organization_invitation.list()
17
+ """
18
+
19
+ id: str = field(metadata={"docstring": "ID of the organization invitation."},)
20
+
21
+ def _validate_id(self, id: str): # noqa: A002
22
+ if not isinstance(id, str):
23
+ raise TypeError("id must be a string.")
24
+
25
+ email: str = field(metadata={"docstring": "Email of the organization invitation."})
26
+
27
+ def _validate_email(self, email: str):
28
+ if not isinstance(email, str):
29
+ raise TypeError("email must be a string.")
30
+
31
+ created_at: datetime = field(
32
+ metadata={"docstring": "Creation time of the organization invitation."},
33
+ )
34
+
35
+ def _validate_created_at(self, created_at: datetime):
36
+ if not isinstance(created_at, datetime):
37
+ raise TypeError("created_at must be a datetime.")
38
+
39
+ expires_at: datetime = field(
40
+ metadata={"docstring": "Expiration time of the organization invitation."},
41
+ )
42
+
43
+ def _validate_expires_at(self, expires_at: datetime):
44
+ if not isinstance(expires_at, datetime):
45
+ raise TypeError("expires_at must be a datetime.")
@@ -0,0 +1,35 @@
1
+ from typing import List, Optional
2
+
3
+ from anyscale._private.anyscale_client import AnyscaleClientInterface
4
+ from anyscale._private.sdk import sdk_docs
5
+ from anyscale._private.sdk.base_sdk import Timer
6
+ from anyscale.cli_logger import BlockLogger
7
+ from anyscale.project._private.project_sdk import PrivateProjectSDK
8
+ from anyscale.project.commands import (
9
+ _ADD_COLLABORATORS_DOCSTRINGS,
10
+ _ADD_COLLABORATORS_EXAMPLE,
11
+ add_collaborators,
12
+ )
13
+ from anyscale.project.models import CreateProjectCollaborator
14
+
15
+
16
+ class ProjectSDK:
17
+ def __init__(
18
+ self,
19
+ *,
20
+ client: Optional[AnyscaleClientInterface] = None,
21
+ logger: Optional[BlockLogger] = None,
22
+ timer: Optional[Timer] = None,
23
+ ):
24
+ self._private_sdk = PrivateProjectSDK(client=client, logger=logger, timer=timer)
25
+
26
+ @sdk_docs(
27
+ doc_py_example=_ADD_COLLABORATORS_EXAMPLE,
28
+ arg_docstrings=_ADD_COLLABORATORS_DOCSTRINGS,
29
+ )
30
+ def add_collaborators( # noqa: F811
31
+ self, cloud: str, project: str, collaborators: List[CreateProjectCollaborator],
32
+ ) -> str:
33
+ """Batch add collaborators to a project.
34
+ """
35
+ return self._private_sdk.add_collaborators(cloud, project, collaborators)
@@ -0,0 +1,27 @@
1
+ from typing import List
2
+
3
+ from anyscale._private.sdk.base_sdk import BaseSDK
4
+ from anyscale.client.openapi_client import (
5
+ CreateUserProjectCollaborator,
6
+ CreateUserProjectCollaboratorValue,
7
+ )
8
+ from anyscale.project.models import CreateProjectCollaborator
9
+
10
+
11
+ class PrivateProjectSDK(BaseSDK):
12
+ def add_collaborators(
13
+ self, cloud: str, project: str, collaborators: List[CreateProjectCollaborator]
14
+ ) -> str:
15
+ cloud_id = self.client.get_cloud_id(cloud_name=cloud, compute_config_id=None)
16
+ project_id = self.client.get_project_id(parent_cloud_id=cloud_id, name=project)
17
+
18
+ return self.client.add_project_collaborators(
19
+ project_id=project_id,
20
+ collaborators=[
21
+ CreateUserProjectCollaborator(
22
+ value=CreateUserProjectCollaboratorValue(email=collaborator.email),
23
+ permission_level=collaborator.permission_level.lower(),
24
+ )
25
+ for collaborator in collaborators
26
+ ],
27
+ )
@@ -0,0 +1,56 @@
1
+ from typing import List
2
+
3
+ from anyscale._private.sdk import sdk_command
4
+ from anyscale.project._private.project_sdk import PrivateProjectSDK
5
+ from anyscale.project.models import CreateProjectCollaborator
6
+
7
+
8
+ _PROJECT_SDK_SINGLETON_KEY = "project_sdk"
9
+
10
+ _ADD_COLLABORATORS_EXAMPLE = """
11
+ import anyscale
12
+ from anyscale.project.models import CreateProjectCollaborator, ProjectPermissionLevel
13
+
14
+ anyscale.project.add_collaborators(
15
+ cloud="cloud_name",
16
+ project="project_name",
17
+ collaborators=[
18
+ CreateProjectCollaborator(
19
+ email="test1@anyscale.com",
20
+ permission_level=ProjectPermissionLevel.OWNER,
21
+ ),
22
+ CreateProjectCollaborator(
23
+ email="test2@anyscale.com",
24
+ permission_level=ProjectPermissionLevel.WRITE,
25
+ ),
26
+ CreateProjectCollaborator(
27
+ email="test3@anyscale.com",
28
+ permission_level=ProjectPermissionLevel.READONLY,
29
+ ),
30
+ ],
31
+ )
32
+ """
33
+
34
+ _ADD_COLLABORATORS_DOCSTRINGS = {
35
+ "cloud": "The cloud that the project belongs to.",
36
+ "project": "The project to add users to.",
37
+ "collaborators": "The list of collaborators to add to the project.",
38
+ }
39
+
40
+
41
+ @sdk_command(
42
+ _PROJECT_SDK_SINGLETON_KEY,
43
+ PrivateProjectSDK,
44
+ doc_py_example=_ADD_COLLABORATORS_EXAMPLE,
45
+ arg_docstrings=_ADD_COLLABORATORS_DOCSTRINGS,
46
+ )
47
+ def add_collaborators(
48
+ cloud: str,
49
+ project: str,
50
+ collaborators: List[CreateProjectCollaborator],
51
+ *,
52
+ _sdk: PrivateProjectSDK
53
+ ) -> str:
54
+ """Batch add collaborators to a project.
55
+ """
56
+ return _sdk.add_collaborators(cloud, project, collaborators)
@@ -0,0 +1,91 @@
1
+ from dataclasses import dataclass, field
2
+ from typing import Any, Dict, List
3
+
4
+ from anyscale._private.models import ModelBase, ModelEnum
5
+
6
+
7
+ class ProjectPermissionLevel(ModelEnum):
8
+ OWNER = "OWNER"
9
+ WRITE = "WRITE"
10
+ READONLY = "READONLY"
11
+
12
+ __docstrings__ = {
13
+ OWNER: "Owner permission level for the project",
14
+ WRITE: "Write permission level for the project",
15
+ READONLY: "Readonly permission level for the project",
16
+ }
17
+
18
+
19
+ @dataclass(frozen=True)
20
+ class CreateProjectCollaborator(ModelBase):
21
+ """User to be added as a collaborator to a project.
22
+ """
23
+
24
+ __doc_py_example__ = """\
25
+ import anyscale
26
+ from anyscale.project.models import ProjectPermissionLevel, CreateProjectCollaborator
27
+ create_project_collaborator = CreateProjectCollaborator(
28
+ # Email of the user to be added as a collaborator
29
+ email="test@anyscale.com",
30
+ # Permission level for the user to the project (ProjectPermissionLevel.OWNER, ProjectPermissionLevel.WRITE, ProjectPermissionLevel.READONLY)
31
+ permission_level=ProjectPermissionLevel.READONLY,
32
+ )
33
+ """
34
+
35
+ def _validate_email(self, email: str):
36
+ if not isinstance(email, str):
37
+ raise TypeError("Email must be a string.")
38
+
39
+ email: str = field(
40
+ metadata={"docstring": "Email of the user to be added as a collaborator."},
41
+ )
42
+
43
+ def _validate_permission_level(
44
+ self, permission_level: ProjectPermissionLevel
45
+ ) -> ProjectPermissionLevel:
46
+ if isinstance(permission_level, str):
47
+ return ProjectPermissionLevel.validate(permission_level)
48
+ elif isinstance(permission_level, ProjectPermissionLevel):
49
+ return permission_level
50
+ else:
51
+ raise TypeError(
52
+ f"'permission_level' must be a 'ProjectPermissionLevel' (it is {type(permission_level)})."
53
+ )
54
+
55
+ permission_level: ProjectPermissionLevel = field( # type: ignore
56
+ default=ProjectPermissionLevel.READONLY, # type: ignore
57
+ metadata={
58
+ "docstring": "Permission level the added user should have for the project" # type: ignore
59
+ f"(one of: {','.join([str(m.value) for m in ProjectPermissionLevel])}", # type: ignore
60
+ },
61
+ )
62
+
63
+
64
+ @dataclass(frozen=True)
65
+ class CreateProjectCollaborators(ModelBase):
66
+ """List of users to be added as collaborators to a project.
67
+ """
68
+
69
+ __doc_py_example__ = """\
70
+ import anyscale
71
+ from anyscale.project.models import ProjectPermissionLevel, CreateProjectCollaborator, CreateProjectCollaborators
72
+ create_project_collaborator = CreateProjectCollaborator(
73
+ # Email of the user to be added as a collaborator
74
+ email="test@anyscale.com",
75
+ # Permission level for the user to the project (ProjectPermissionLevel.OWNER, ProjectPermissionLevel.WRITE, ProjectPermissionLevel.READONLY)
76
+ permission_level=ProjectPermissionLevel.READONLY,
77
+ )
78
+ create_project_collaborators = CreateProjectCollaborators(
79
+ collaborators=[create_project_collaborator]
80
+ )
81
+ """
82
+
83
+ collaborators: List[Dict[str, Any]] = field(
84
+ metadata={
85
+ "docstring": "List of users to be added as collaborators to a project."
86
+ },
87
+ )
88
+
89
+ def _validate_collaborators(self, collaborators: List[Dict[str, Any]]):
90
+ if not isinstance(collaborators, list):
91
+ raise TypeError("Collaborators must be a list.")
@@ -5,12 +5,11 @@ import click
5
5
  from click import ClickException
6
6
  import yaml
7
7
 
8
- import anyscale
9
8
  from anyscale.authenticate import get_auth_api_client
10
9
  from anyscale.cli_logger import BlockLogger
11
10
  from anyscale.client.openapi_client import Project
12
11
  from anyscale.client.openapi_client.api.default_api import DefaultApi
13
- from anyscale.cloud import get_cloud_id_and_name
12
+ from anyscale.cloud_utils import get_cloud_id_and_name
14
13
  from anyscale.cluster_compute import (
15
14
  get_cluster_compute_from_name,
16
15
  get_selected_cloud_id_or_default,
@@ -301,13 +300,13 @@ def create_new_proj_def(
301
300
  name = slugify(name)
302
301
  log.info(f"Normalized project name to {name}")
303
302
 
304
- project_definition = anyscale.project.ProjectDefinition(os.getcwd())
303
+ project_definition = ProjectDefinition(os.getcwd())
305
304
  project_definition.config["name"] = name
306
305
  return name, project_definition
307
306
 
308
307
 
309
308
  def _do_attach(project_id: str, is_create_project: bool) -> None:
310
- with open(anyscale.project.ANYSCALE_PROJECT_FILE, "w") as f:
309
+ with open(ANYSCALE_PROJECT_FILE, "w") as f:
311
310
  yaml.dump(
312
311
  {"project_id": project_id}, f,
313
312
  )