anyscale 0.26.52__py3-none-any.whl → 0.26.54__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 (64) hide show
  1. anyscale/_private/anyscale_client/anyscale_client.py +26 -26
  2. anyscale/_private/anyscale_client/common.py +5 -5
  3. anyscale/_private/anyscale_client/fake_anyscale_client.py +6 -6
  4. anyscale/_private/docgen/__main__.py +8 -8
  5. anyscale/_private/docgen/generator.py +48 -10
  6. anyscale/_private/docgen/models.md +2 -2
  7. anyscale/_private/sdk/__init__.py +124 -1
  8. anyscale/_private/workload/workload_config.py +4 -6
  9. anyscale/_private/workload/workload_sdk.py +9 -11
  10. anyscale/client/README.md +14 -13
  11. anyscale/client/openapi_client/__init__.py +4 -4
  12. anyscale/client/openapi_client/api/default_api.py +395 -325
  13. anyscale/client/openapi_client/models/__init__.py +4 -4
  14. anyscale/client/openapi_client/models/aws_config.py +2 -2
  15. anyscale/client/openapi_client/models/baseimagesenum.py +76 -1
  16. anyscale/client/openapi_client/models/cloud_data_bucket_file_type.py +2 -1
  17. anyscale/client/openapi_client/models/cloud_data_bucket_presigned_url_request.py +31 -3
  18. anyscale/client/openapi_client/models/cloud_deployment.py +37 -36
  19. anyscale/client/openapi_client/models/create_resource_notification.py +31 -3
  20. anyscale/client/openapi_client/models/{decorated_cloud_deployment.py → decorated_cloud_resource.py} +124 -96
  21. anyscale/client/openapi_client/models/{clouddeployment_list_response.py → decoratedcloudresource_list_response.py} +15 -15
  22. anyscale/client/openapi_client/models/{clouddeployment_response.py → decoratedcloudresource_response.py} +11 -11
  23. anyscale/client/openapi_client/models/file_storage.py +4 -4
  24. anyscale/client/openapi_client/models/gcp_config.py +2 -2
  25. anyscale/client/openapi_client/models/ha_job_error_types.py +9 -2
  26. anyscale/client/openapi_client/models/object_storage.py +2 -2
  27. anyscale/client/openapi_client/models/{decoratedclouddeployment_response.py → presigned_url_response.py} +24 -22
  28. anyscale/client/openapi_client/models/production_job_event.py +31 -3
  29. anyscale/client/openapi_client/models/resource_alert_event_type.py +2 -1
  30. anyscale/client/openapi_client/models/resource_notification.py +29 -1
  31. anyscale/client/openapi_client/models/supportedbaseimagesenum.py +76 -1
  32. anyscale/client/openapi_client/models/workload_info.py +31 -3
  33. anyscale/client/openapi_client/models/workload_state_info.py +29 -1
  34. anyscale/cloud/models.py +39 -42
  35. anyscale/commands/cloud_commands.py +25 -23
  36. anyscale/commands/command_examples.py +10 -10
  37. anyscale/commands/exec_commands.py +12 -1
  38. anyscale/commands/list_commands.py +42 -12
  39. anyscale/commands/project_commands.py +23 -10
  40. anyscale/commands/schedule_commands.py +22 -11
  41. anyscale/commands/service_commands.py +11 -6
  42. anyscale/commands/util.py +94 -1
  43. anyscale/commands/workspace_commands.py +92 -38
  44. anyscale/compute_config/__init__.py +1 -1
  45. anyscale/compute_config/_private/compute_config_sdk.py +8 -11
  46. anyscale/compute_config/commands.py +3 -3
  47. anyscale/compute_config/models.py +30 -30
  48. anyscale/controllers/cloud_controller.py +306 -300
  49. anyscale/controllers/kubernetes_verifier.py +1 -1
  50. anyscale/job/_private/job_sdk.py +12 -12
  51. anyscale/job/models.py +1 -1
  52. anyscale/sdk/anyscale_client/models/baseimagesenum.py +76 -1
  53. anyscale/sdk/anyscale_client/models/supportedbaseimagesenum.py +76 -1
  54. anyscale/shared_anyscale_utils/latest_ray_version.py +1 -1
  55. anyscale/version.py +1 -1
  56. anyscale/workspace/commands.py +114 -23
  57. anyscale/workspace/models.py +3 -5
  58. {anyscale-0.26.52.dist-info → anyscale-0.26.54.dist-info}/METADATA +1 -1
  59. {anyscale-0.26.52.dist-info → anyscale-0.26.54.dist-info}/RECORD +64 -64
  60. {anyscale-0.26.52.dist-info → anyscale-0.26.54.dist-info}/WHEEL +0 -0
  61. {anyscale-0.26.52.dist-info → anyscale-0.26.54.dist-info}/entry_points.txt +0 -0
  62. {anyscale-0.26.52.dist-info → anyscale-0.26.54.dist-info}/licenses/LICENSE +0 -0
  63. {anyscale-0.26.52.dist-info → anyscale-0.26.54.dist-info}/licenses/NOTICE +0 -0
  64. {anyscale-0.26.52.dist-info → anyscale-0.26.54.dist-info}/top_level.txt +0 -0
@@ -244,8 +244,8 @@ def list_cloud(name: Optional[str], cloud_id: Optional[str], max_items: int,) ->
244
244
  )
245
245
 
246
246
 
247
- @cloud_cli.group("deployment", help="Manage the configuration for a cloud deployment.")
248
- def cloud_deployment_group() -> None:
247
+ @cloud_cli.group("resource", help="Manage the configuration for a cloud resource.")
248
+ def cloud_resource_group() -> None:
249
249
  pass
250
250
 
251
251
 
@@ -254,68 +254,68 @@ def cloud_config_group() -> None:
254
254
  pass
255
255
 
256
256
 
257
- @cloud_deployment_group.command(
257
+ @cloud_resource_group.command(
258
258
  name="create",
259
- help="Create a new cloud deployment in an existing cloud.",
259
+ help="Create a new cloud resource in an existing cloud.",
260
260
  cls=AnyscaleCommand,
261
- example=command_examples.CLOUD_DEPLOYMENT_CREATE_EXAMPLE,
261
+ example=command_examples.CLOUD_RESOURCE_CREATE_EXAMPLE,
262
262
  is_alpha=True,
263
263
  )
264
264
  @click.option(
265
265
  "--cloud",
266
- help="The name of the cloud to create the new deployment in.",
266
+ help="The name of the cloud to add the new resource to.",
267
267
  type=str,
268
268
  required=True,
269
269
  )
270
270
  @click.option(
271
271
  "--file",
272
272
  "-f",
273
- help="Path to a YAML file defining the cloud deployment. Schema: https://docs.anyscale.com/reference/cloud/#clouddeployment.",
273
+ help="Path to a YAML file defining the cloud resource. Schema: https://docs.anyscale.com/reference/cloud/#cloudresource.",
274
274
  required=True,
275
275
  )
276
276
  @click.option(
277
277
  "--skip-verification",
278
278
  is_flag=True,
279
279
  default=False,
280
- help="Skip cloud deployment verification.",
280
+ help="Skip cloud resource verification.",
281
281
  )
282
282
  @click.option(
283
283
  "--yes", "-y", is_flag=True, default=False, help="Skip asking for confirmation."
284
284
  )
285
- def cloud_deployment_create(
285
+ def cloud_resource_create(
286
286
  cloud: str, file: str, skip_verification: bool, yes: bool,
287
287
  ) -> None:
288
288
  try:
289
- CloudController().create_cloud_deployment(cloud, file, skip_verification, yes)
289
+ CloudController().create_cloud_resource(cloud, file, skip_verification, yes)
290
290
  except click.ClickException as e:
291
291
  print(e)
292
292
 
293
293
 
294
- @cloud_deployment_group.command(
294
+ @cloud_resource_group.command(
295
295
  name="delete",
296
- help="Remove a cloud deployment from an existing cloud.",
296
+ help="Remove a cloud resource from an existing cloud.",
297
297
  cls=AnyscaleCommand,
298
- example=command_examples.CLOUD_DEPLOYMENT_DELETE_EXAMPLE,
298
+ example=command_examples.CLOUD_RESOURCE_DELETE_EXAMPLE,
299
299
  is_alpha=True,
300
300
  )
301
301
  @click.option(
302
302
  "--cloud",
303
- help="The name of the cloud to remove the deployment from.",
303
+ help="The name of the cloud to remove the resource from.",
304
304
  type=str,
305
305
  required=True,
306
306
  )
307
307
  @click.option(
308
- "--deployment",
309
- help="The name of the deployment to remove.",
308
+ "--resource",
309
+ help="The name of the cloud resource to remove.",
310
310
  type=str,
311
311
  required=True,
312
312
  )
313
313
  @click.option(
314
314
  "--yes", "-y", is_flag=True, default=False, help="Skip asking for confirmation."
315
315
  )
316
- def cloud_deployment_delete(cloud: str, deployment: str, yes: bool,) -> None:
316
+ def cloud_resource_delete(cloud: str, resource: str, yes: bool,) -> None:
317
317
  try:
318
- CloudController().remove_cloud_deployment(cloud, deployment, yes)
318
+ CloudController().remove_cloud_resource(cloud, resource, yes)
319
319
  except click.ClickException as e:
320
320
  print(e)
321
321
 
@@ -361,7 +361,7 @@ def cloud_deployment_delete(cloud: str, deployment: str, yes: bool,) -> None:
361
361
  @click.option(
362
362
  "--resources-file",
363
363
  "-f",
364
- help="EXPERIMENTAL: Path to a YAML file defining a list of cloud resources. Schema: https://docs.anyscale.com/reference/cloud/#clouddeployment.",
364
+ help="EXPERIMENTAL: Path to a YAML file defining a list of cloud resources. Schema: https://docs.anyscale.com/reference/cloud/#cloudresource.",
365
365
  required=False,
366
366
  )
367
367
  @click.option(
@@ -387,7 +387,7 @@ def cloud_update( # noqa: PLR0913
387
387
  "were both provided. Please only provide one of these two arguments."
388
388
  )
389
389
  if resources_file:
390
- CloudController().update_cloud_deployments(
390
+ CloudController().update_cloud_resources(
391
391
  cloud_name=cloud_name or name,
392
392
  cloud_id=cloud_id,
393
393
  resources_file=resources_file,
@@ -1337,14 +1337,16 @@ def get_cloud(
1337
1337
  log.error("Cloud not found.")
1338
1338
  return
1339
1339
 
1340
- # Include all cloud deployments for the cloud.
1341
- cloud_deployments = CloudController().get_cloud_deployments(cloud_id=cloud.id)
1340
+ # Include all cloud resources for the cloud.
1341
+ cloud_resources = CloudController().get_formatted_cloud_resources(
1342
+ cloud_id=cloud.id
1343
+ )
1342
1344
  result = {
1343
1345
  "name": cloud.name,
1344
1346
  "id": cloud.id,
1345
1347
  "created_at": cloud.created_at,
1346
1348
  "is_default": cloud.is_default,
1347
- "deployments": cloud_deployments.get("deployments", []),
1349
+ "resources": cloud_resources,
1348
1350
  }
1349
1351
 
1350
1352
  if output:
@@ -550,12 +550,12 @@ collaborators:
550
550
  """
551
551
 
552
552
 
553
- CLOUD_DEPLOYMENT_CREATE_EXAMPLE = """\
554
- $ anyscale cloud deployment create --cloud my-cloud --file new-cloud-deployment.yaml
555
- Successfully created cloud deployment my-new-deployment in cloud my-cloud.
553
+ CLOUD_RESOURCE_CREATE_EXAMPLE = """\
554
+ $ anyscale cloud resource create --cloud my-cloud --file new-cloud-resource.yaml
555
+ Successfully created cloud resource my-new-resource in cloud my-cloud.
556
556
 
557
- $ cat new-cloud-deployment.yaml
558
- name: my-new-deployment
557
+ $ cat new-cloud-resource.yaml
558
+ name: my-new-resource
559
559
  provider: AWS
560
560
  compute_stack: VM
561
561
  region: us-west-2
@@ -575,11 +575,11 @@ aws_config:
575
575
  memorydb_cluster_name: my-memorydb-cluster
576
576
  """
577
577
 
578
- CLOUD_DEPLOYMENT_DELETE_EXAMPLE = """\
579
- $ anyscale cloud deployment delete --cloud my-cloud --deployment my-deployment
578
+ CLOUD_RESOURCE_DELETE_EXAMPLE = """\
579
+ $ anyscale cloud resource delete --cloud my-cloud --resource my-resource
580
580
  Output
581
- Please confirm that you would like to remove deployment my-deployment from cloud my-cloud. [y/N]: y
582
- (anyscale +3.5s) Successfully removed deployment my-deployment from cloud my-cloud!
581
+ Please confirm that you would like to remove resource my-resource from cloud my-cloud. [y/N]: y
582
+ (anyscale +3.5s) Successfully removed resource my-resource from cloud my-cloud!
583
583
  """
584
584
 
585
585
  CLOUD_GET_CLOUD_EXAMPLE = """\
@@ -589,7 +589,7 @@ name: my-cloud
589
589
  created_at: 2022-10-18 05:12:13.335803+00:00
590
590
  is_default: true
591
591
  deployments:
592
- - cloud_deployment_id: cldrsrc_123
592
+ - cloud_resource_id: cldrsrc_123
593
593
  name: vm-aws-us-west-2
594
594
  provider: AWS
595
595
  compute_stack: VM
@@ -1,14 +1,25 @@
1
1
  import click
2
2
 
3
+ from anyscale.commands.util import DeprecatedAnyscaleCommand
4
+
3
5
 
4
6
  @click.command(
5
7
  name="exec",
6
8
  hidden=True,
7
9
  help="[DEPRECATED] Execute shell commands in interactive cluster.",
8
10
  context_settings={"ignore_unknown_options": True, "allow_extra_args": True,},
11
+ cls=DeprecatedAnyscaleCommand,
12
+ removal_date="2025-10-01",
13
+ deprecation_message="`anyscale exec` has been deprecated and no longer works on Anyscale",
14
+ alternative="use `anyscale job submit` to run your script as a job in a cluster",
9
15
  )
10
16
  def anyscale_exec() -> None:
17
+ """Execute shell commands in interactive cluster.
18
+
19
+ DEPRECATED: This command will be removed on 2025-10-01.
20
+ Use 'anyscale job submit' to run your script as a job in a cluster.
21
+ """
11
22
  raise click.ClickException(
12
- "Warning: `anyscale exec` has been deprecated and no longer works on Anyscale V2. "
23
+ "`anyscale exec` has been deprecated and no longer works on Anyscale. "
13
24
  "Please use `anyscale job submit` to run your script as a job in a cluster."
14
25
  )
@@ -8,6 +8,7 @@ from typing import Any, Optional
8
8
  import click
9
9
 
10
10
  from anyscale.cli_logger import BlockLogger
11
+ from anyscale.commands.util import DeprecatedAnyscaleCommand
11
12
  from anyscale.controllers.list_controller import ListController
12
13
 
13
14
 
@@ -25,26 +26,40 @@ def list_cli() -> None:
25
26
  name="clouds",
26
27
  help="[DEPRECATED] List the clouds currently available in your account.",
27
28
  hidden=True,
29
+ cls=DeprecatedAnyscaleCommand,
30
+ removal_date="2025-10-01",
31
+ deprecation_message="`anyscale list clouds` has been deprecated",
32
+ alternative="use `anyscale cloud list` instead",
28
33
  )
29
34
  @click.option("--json", "show_json", help="Return the results in json", is_flag=True)
30
35
  def list_clouds(show_json: bool) -> None:
31
- log.warning(
32
- "`anyscale list clouds` has been deprecated. Please use `anyscale cloud list` instead."
33
- )
36
+ """List the clouds currently available in your account.
37
+
38
+ DEPRECATED: This command will be removed on 2025-10-01.
39
+ Use 'anyscale cloud list' instead.
40
+ """
34
41
  list_controller = ListController()
35
42
  output = list_controller.list_clouds(json_format=show_json)
36
43
  print(output)
37
44
 
38
45
 
39
46
  @list_cli.command(
40
- name="projects", help="[DEPRECATED] List all accessible projects.", hidden=True
47
+ name="projects",
48
+ help="[DEPRECATED] List all accessible projects.",
49
+ hidden=True,
50
+ cls=DeprecatedAnyscaleCommand,
51
+ removal_date="2025-10-01",
52
+ deprecation_message="`anyscale list projects` has been deprecated",
53
+ alternative="use `anyscale project list` instead",
41
54
  )
42
55
  @click.option("--json", "show_json", help="Return the results in json", is_flag=True)
43
56
  @click.pass_context
44
57
  def project_list(ctx: Any, show_json: bool) -> None: # noqa: ARG001
45
- log.warning(
46
- "`anyscale list projects` has been deprecated. Please use `anyscale project list` instead."
47
- )
58
+ """List all accessible projects.
59
+
60
+ DEPRECATED: This command will be removed on 2025-10-01.
61
+ Use 'anyscale project list' instead.
62
+ """
48
63
  list_controller = ListController()
49
64
  output = list_controller.list_projects(json_format=show_json)
50
65
  print(output)
@@ -54,6 +69,10 @@ def project_list(ctx: Any, show_json: bool) -> None: # noqa: ARG001
54
69
  name="sessions",
55
70
  help="[DEPRECATED] List all clusters within the current project.",
56
71
  hidden=True,
72
+ cls=DeprecatedAnyscaleCommand,
73
+ removal_date="2025-10-01",
74
+ deprecation_message="`anyscale list sessions` has been deprecated",
75
+ alternative="use `anyscale cluster list` instead",
57
76
  )
58
77
  @click.option(
59
78
  "--name",
@@ -65,9 +84,11 @@ def project_list(ctx: Any, show_json: bool) -> None: # noqa: ARG001
65
84
  @click.option("--all", help="List all clusters, including inactive ones.", is_flag=True)
66
85
  @click.option("--json", "show_json", help="Return the results in json", is_flag=True)
67
86
  def session_list(name: Optional[str], all: bool, show_json: bool) -> None: # noqa: A002
68
- log.warning(
69
- "`anyscale list sessions` has been deprecated. Please use `anyscale cluster list` instead."
70
- )
87
+ """List all clusters within the current project.
88
+
89
+ DEPRECATED: This command will be removed on 2025-10-01.
90
+ Use 'anyscale cluster list' instead.
91
+ """
71
92
  list_controller = ListController()
72
93
  output = list_controller.list_sessions(
73
94
  name=name, show_all=all, json_format=show_json
@@ -79,7 +100,16 @@ def session_list(name: Optional[str], all: bool, show_json: bool) -> None: # no
79
100
  name="ips",
80
101
  help="[DEPRECATED] List IP addresses of head and worker nodes.",
81
102
  context_settings={"ignore_unknown_options": True, "allow_extra_args": True,},
103
+ cls=DeprecatedAnyscaleCommand,
104
+ removal_date="2025-10-01",
105
+ deprecation_message="Listing IPs is not supported on Anyscale",
106
+ alternative="use the Anyscale console to view cluster node information",
82
107
  )
83
108
  def list_ips() -> None:
84
- """List IP addresses of head and worker nodes."""
85
- raise click.ClickException("Listing IPs is not supported on Anyscale V2")
109
+ """List IP addresses of head and worker nodes.
110
+
111
+ DEPRECATED: This command will be removed on 2025-10-01.
112
+ Listing IPs is not supported on Anyscale.
113
+ Use the Anyscale console to view cluster node information.
114
+ """
115
+ raise click.ClickException("Listing IPs is not supported on Anyscale")
@@ -12,7 +12,11 @@ import anyscale
12
12
  from anyscale.cli_logger import BlockLogger
13
13
  from anyscale.commands import command_examples
14
14
  from anyscale.commands.list_util import display_list
15
- from anyscale.commands.util import AnyscaleCommand, NotRequiredIf
15
+ from anyscale.commands.util import (
16
+ AnyscaleCommand,
17
+ DeprecatedAnyscaleCommand,
18
+ NotRequiredIf,
19
+ )
16
20
  from anyscale.controllers.project_controller import ProjectController
17
21
  from anyscale.project.models import (
18
22
  CreateProjectCollaborator,
@@ -441,6 +445,10 @@ def _default_project_name() -> str:
441
445
  "[DEPRECATED] Create a new project or attach this directory to an existing project."
442
446
  ),
443
447
  hidden=True,
448
+ cls=DeprecatedAnyscaleCommand,
449
+ removal_date="2025-10-01",
450
+ deprecation_message="`anyscale init` has been deprecated",
451
+ alternative="use `anyscale project create` to create a new project",
444
452
  )
445
453
  @click.option(
446
454
  "--project-id",
@@ -474,10 +482,11 @@ def anyscale_init(
474
482
  config: Optional[str],
475
483
  requirements: Optional[str],
476
484
  ) -> None:
477
- log.warning(
478
- "`anyscale init` has been deprecated. Please use `anyscale project init` "
479
- "to create or attach to a project from this directory."
480
- )
485
+ """Create a new project or attach this directory to an existing project.
486
+
487
+ DEPRECATED: This command will be removed on 2025-10-01.
488
+ Use 'anyscale project create' to create a new project.
489
+ """
481
490
  if (project_id and name) or not (project_id or name):
482
491
  raise click.BadArgumentUsage(
483
492
  "Only one of project_id and name must be provided."
@@ -491,6 +500,10 @@ def anyscale_init(
491
500
  name="init",
492
501
  help="[DEPRECATED] Create a new project or attach this directory to an existing project.",
493
502
  hidden=True,
503
+ cls=DeprecatedAnyscaleCommand,
504
+ removal_date="2025-10-01",
505
+ deprecation_message="`anyscale project init` has been deprecated",
506
+ alternative="use `anyscale project create` to create a new project and specify a project id or name for the other Anyscale CLI commands",
494
507
  )
495
508
  @click.option(
496
509
  "--project-id",
@@ -510,11 +523,11 @@ def anyscale_init(
510
523
  default=_default_project_name(),
511
524
  )
512
525
  def init(project_id: Optional[str], name: Optional[str],) -> None:
513
- log.warning(
514
- "`anyscale project init` has been deprecated and will be removed in "
515
- "April 2022. Please use `anyscale project create` to create a new project "
516
- "and specify a project id or name for the other Anyscale CLI commands."
517
- )
526
+ """Create a new project or attach this directory to an existing project.
527
+
528
+ DEPRECATED: This command will be removed on 2025-10-01.
529
+ Use 'anyscale project create' to create a new project.
530
+ """
518
531
  if (project_id and name) or not (project_id or name):
519
532
  raise click.BadArgumentUsage(
520
533
  "Only one of --project-id and --name must be provided."
@@ -9,7 +9,10 @@ import yaml
9
9
  import anyscale
10
10
  from anyscale.cli_logger import BlockLogger
11
11
  from anyscale.commands import command_examples
12
- from anyscale.commands.util import AnyscaleCommand, LegacyAnyscaleCommand
12
+ from anyscale.commands.util import (
13
+ AnyscaleCommand,
14
+ DeprecatedAnyscaleCommand,
15
+ )
13
16
  from anyscale.controllers.schedule_controller import ScheduleController
14
17
  from anyscale.schedule.models import JobConfig, ScheduleConfig, ScheduleState
15
18
 
@@ -94,9 +97,11 @@ def apply(config_file: str, name: Optional[str],) -> None:
94
97
 
95
98
  @schedule_cli.command(
96
99
  name="create",
97
- cls=LegacyAnyscaleCommand,
98
- new_prefix="anyscale schedule",
99
- new_cli=apply,
100
+ help="[DEPRECATED - use 'apply' instead] Create a schedule.",
101
+ cls=DeprecatedAnyscaleCommand,
102
+ removal_date="2025-10-01",
103
+ deprecation_message="`anyscale schedule create` has been deprecated",
104
+ alternative="use `anyscale schedule apply` instead",
100
105
  )
101
106
  @click.argument("schedule_config_file", required=True)
102
107
  @click.option(
@@ -108,13 +113,15 @@ def apply(config_file: str, name: Optional[str],) -> None:
108
113
  def create(
109
114
  schedule_config_file: str, name: Optional[str], description: Optional[str],
110
115
  ) -> None:
111
- """ Create or Update a Schedule
116
+ """Create or Update a Schedule.
117
+
118
+ DEPRECATED: This command will be removed on 2025-10-01.
119
+ Use 'anyscale schedule apply' instead.
112
120
 
113
121
  This function accepts 1 argument, a path to a YAML config file that defines this schedule.
114
122
 
115
123
  Note: if a schedule with the name exists in the specified project, it will be updated instead.
116
124
  """
117
- log.warning("DEPRECATED: Use `anyscale schedule apply` instead.")
118
125
  job_controller = ScheduleController()
119
126
  job_controller.apply(
120
127
  schedule_config_file, name, description,
@@ -123,9 +130,11 @@ def create(
123
130
 
124
131
  @schedule_cli.command(
125
132
  name="update",
126
- cls=LegacyAnyscaleCommand,
127
- new_prefix="anyscale schedule",
128
- new_cli=apply,
133
+ help="[DEPRECATED - use 'apply' instead] Update a schedule.",
134
+ cls=DeprecatedAnyscaleCommand,
135
+ removal_date="2025-10-01",
136
+ deprecation_message="`anyscale schedule update` has been deprecated",
137
+ alternative="use `anyscale schedule apply` instead",
129
138
  )
130
139
  @click.argument("schedule_config_file", required=True)
131
140
  @click.option(
@@ -137,11 +146,13 @@ def create(
137
146
  def update(
138
147
  schedule_config_file: str, name: Optional[str], description: Optional[str],
139
148
  ) -> None:
140
- """ Create or Update a Schedule
149
+ """Create or Update a Schedule.
150
+
151
+ DEPRECATED: This command will be removed on 2025-10-01.
152
+ Use 'anyscale schedule apply' instead.
141
153
 
142
154
  This function accepts 1 argument, a path to a YAML config file that defines this schedule.
143
155
  """
144
- log.warning("DEPRECATED: Use `anyscale schedule apply` instead.")
145
156
  job_controller = ScheduleController()
146
157
  job_controller.apply(
147
158
  schedule_config_file, name, description,
@@ -22,7 +22,7 @@ from anyscale.commands.list_util import (
22
22
  from anyscale.commands.util import (
23
23
  AnyscaleCommand,
24
24
  convert_kv_strings_to_dict,
25
- LegacyAnyscaleCommand,
25
+ DeprecatedAnyscaleCommand,
26
26
  override_env_vars,
27
27
  )
28
28
  from anyscale.controllers.service_controller import ServiceController
@@ -565,10 +565,11 @@ def controller_logs(
565
565
 
566
566
  @service_cli.command(
567
567
  name="rollout",
568
- help="Roll out a service.",
569
- cls=LegacyAnyscaleCommand,
570
- new_prefix="anyscale service",
571
- new_cli=deploy,
568
+ help="[DEPRECATED - use 'deploy' instead] Roll out a service.",
569
+ cls=DeprecatedAnyscaleCommand,
570
+ removal_date="2025-10-01",
571
+ deprecation_message="`anyscale service rollout` has been deprecated",
572
+ alternative="use `anyscale service deploy` instead",
572
573
  )
573
574
  @click.option(
574
575
  "-f",
@@ -626,7 +627,11 @@ def rollout( # noqa: PLR0913
626
627
  in_place: bool,
627
628
  no_auto_complete_rollout: bool,
628
629
  ):
629
- """Start or update a service rollout to a new version."""
630
+ """Start or update a service rollout to a new version.
631
+
632
+ DEPRECATED: This command will be removed on 2025-10-01.
633
+ Use 'anyscale service deploy' instead.
634
+ """
630
635
  if in_place:
631
636
  if rollout_strategy is not None:
632
637
  raise click.ClickException(
anyscale/commands/util.py CHANGED
@@ -1,9 +1,16 @@
1
1
  from copy import deepcopy
2
- from typing import Dict, Tuple, TypeVar
2
+ from datetime import date, datetime
3
+ import sys
4
+ from typing import Dict, Optional, Tuple, TypeVar
3
5
 
4
6
  import click
7
+ import colorama
5
8
 
6
9
  from anyscale._private.workload import WorkloadConfig
10
+ from anyscale.cli_logger import BlockLogger
11
+
12
+
13
+ logger = BlockLogger()
7
14
 
8
15
 
9
16
  class AnyscaleCommand(click.Command):
@@ -151,3 +158,89 @@ def override_env_vars(config: T, overrides: Dict[str, str]) -> T:
151
158
  final_env_vars = deepcopy(config.env_vars) if config.env_vars else {}
152
159
  final_env_vars.update(overrides)
153
160
  return config.options(env_vars=final_env_vars)
161
+
162
+
163
+ class DeprecatedAnyscaleCommand(click.Command):
164
+ """
165
+ DeprecatedAnyscaleCommand is a subclass of click.Command that shows deprecation warnings.
166
+
167
+ Similar to LegacyAnyscaleCommand but focuses on deprecation with dates and alternatives.
168
+ """
169
+
170
+ def __init__(self, *args, **kwargs):
171
+ self.__removal_date__ = kwargs.pop("removal_date", None)
172
+ self.__deprecation_message__ = kwargs.pop("deprecation_message", None)
173
+ self.__alternative__ = kwargs.pop("alternative", None)
174
+ super().__init__(*args, **kwargs)
175
+
176
+ def get_help(self, ctx):
177
+ """Override get_help to show deprecation warning when help is displayed."""
178
+ self._show_deprecation_warning()
179
+ return super().get_help(ctx)
180
+
181
+ def invoke(self, ctx):
182
+ """Override invoke to show deprecation warning before executing."""
183
+ self._show_deprecation_warning()
184
+ return super().invoke(ctx)
185
+
186
+ def _show_deprecation_warning(self):
187
+ """Show the deprecation warning."""
188
+
189
+ # Visual separator for attention
190
+ print("\n" + "=" * 80, file=sys.stderr)
191
+ print(
192
+ f"{colorama.Style.BRIGHT}{colorama.Fore.YELLOW}⚠️ DEPRECATION WARNING ⚠️{colorama.Style.RESET_ALL}",
193
+ file=sys.stderr,
194
+ )
195
+ print("=" * 80, file=sys.stderr)
196
+
197
+ # Build deprecation message
198
+ base_msg = (
199
+ self.__deprecation_message__
200
+ if self.__deprecation_message__
201
+ else f"Command '{self.name}' is deprecated"
202
+ )
203
+
204
+ # Removal date information with grammar-aware connector
205
+ date_msg = None
206
+ if self.__removal_date__:
207
+ date_str = self._format_removal_date(self.__removal_date__)
208
+ if date_str:
209
+ ends_with_punct = base_msg.strip().endswith((".", "!", "?"))
210
+ if ends_with_punct:
211
+ date_msg = f"It will be removed on {date_str}"
212
+ else:
213
+ date_msg = f"and will be removed on {date_str}"
214
+
215
+ # Alternative suggestion
216
+ alternative_msg = None
217
+ if self.__alternative__:
218
+ alternative_msg = f"\n\n➡️ {colorama.Style.BRIGHT}Please {self.__alternative__}{colorama.Style.RESET_ALL}"
219
+
220
+ main_line_parts = [part for part in (base_msg, date_msg) if part]
221
+ deprecation_msg = " ".join(main_line_parts)
222
+ if alternative_msg:
223
+ deprecation_msg += alternative_msg
224
+
225
+ # Logger warning but also print directly for visibility
226
+ print(
227
+ f"\n{colorama.Fore.YELLOW}{deprecation_msg}{colorama.Style.RESET_ALL}",
228
+ file=sys.stderr,
229
+ )
230
+ print("=" * 80 + "\n", file=sys.stderr)
231
+
232
+ def _format_removal_date(self, removal_date) -> Optional[str]:
233
+ """Format the removal date for display."""
234
+ try:
235
+ if isinstance(removal_date, str):
236
+ parsed_date = datetime.strptime(removal_date, "%Y-%m-%d").date()
237
+ elif isinstance(removal_date, datetime):
238
+ parsed_date = removal_date.date()
239
+ elif isinstance(removal_date, date):
240
+ parsed_date = removal_date
241
+ else:
242
+ return None
243
+
244
+ return parsed_date.strftime("%Y-%m-%d")
245
+ except (ValueError, AttributeError):
246
+ return str(removal_date)