anyscale 0.26.29__py3-none-any.whl → 0.26.30__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.
- anyscale/__init__.py +10 -0
- anyscale/_private/anyscale_client/anyscale_client.py +69 -0
- anyscale/_private/anyscale_client/common.py +38 -0
- anyscale/_private/anyscale_client/fake_anyscale_client.py +11 -0
- anyscale/_private/docgen/__main__.py +4 -0
- anyscale/commands/command_examples.py +10 -0
- anyscale/commands/job_queue_commands.py +295 -104
- anyscale/commands/list_util.py +14 -1
- anyscale/commands/machine_pool_commands.py +14 -2
- anyscale/commands/service_commands.py +6 -12
- anyscale/commands/workspace_commands_v2.py +462 -25
- anyscale/controllers/job_controller.py +5 -210
- anyscale/job_queue/__init__.py +89 -0
- anyscale/job_queue/_private/job_queue_sdk.py +158 -0
- anyscale/job_queue/commands.py +130 -0
- anyscale/job_queue/models.py +284 -0
- anyscale/scripts.py +1 -1
- anyscale/utils/ssh_websocket_proxy.py +178 -0
- anyscale/version.py +1 -1
- {anyscale-0.26.29.dist-info → anyscale-0.26.30.dist-info}/METADATA +3 -1
- {anyscale-0.26.29.dist-info → anyscale-0.26.30.dist-info}/RECORD +26 -21
- {anyscale-0.26.29.dist-info → anyscale-0.26.30.dist-info}/LICENSE +0 -0
- {anyscale-0.26.29.dist-info → anyscale-0.26.30.dist-info}/NOTICE +0 -0
- {anyscale-0.26.29.dist-info → anyscale-0.26.30.dist-info}/WHEEL +0 -0
- {anyscale-0.26.29.dist-info → anyscale-0.26.30.dist-info}/entry_points.txt +0 -0
- {anyscale-0.26.29.dist-info → anyscale-0.26.30.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,284 @@
|
|
1
|
+
from dataclasses import dataclass, field
|
2
|
+
from datetime import datetime
|
3
|
+
from typing import get_type_hints, Literal, Optional, TYPE_CHECKING, Union
|
4
|
+
|
5
|
+
from anyscale._private.models import ModelBase, ModelEnum
|
6
|
+
|
7
|
+
|
8
|
+
class JobQueueState(ModelEnum):
|
9
|
+
"""Current state of a job queue."""
|
10
|
+
|
11
|
+
ACTIVE = "ACTIVE"
|
12
|
+
SEALED = "SEALED"
|
13
|
+
# Add other potential states if necessary based on API reality
|
14
|
+
UNKNOWN = "UNKNOWN"
|
15
|
+
|
16
|
+
__docstrings__ = {
|
17
|
+
ACTIVE: "The job queue is active and accepting jobs.",
|
18
|
+
SEALED: "The job queue is sealed and not accepting new jobs. It may still be processing existing jobs.",
|
19
|
+
UNKNOWN: "The state of the job queue is unknown or could not be determined.",
|
20
|
+
}
|
21
|
+
|
22
|
+
|
23
|
+
class JobQueueSortField(ModelEnum):
|
24
|
+
"""Fields available for sorting job queues."""
|
25
|
+
|
26
|
+
ID = "ID"
|
27
|
+
NAME = "NAME"
|
28
|
+
CREATED_AT = "CREATED_AT"
|
29
|
+
CREATOR_ID = "CREATOR_ID"
|
30
|
+
CREATOR_EMAIL = "CREATOR_EMAIL"
|
31
|
+
PROJECT_ID = "PROJECT_ID"
|
32
|
+
CLOUD_ID = "CLOUD_ID"
|
33
|
+
QUEUE_STATE = "QUEUE_STATE"
|
34
|
+
CLUSTER_STATE = "CLUSTER_STATE"
|
35
|
+
|
36
|
+
__docstrings__ = {
|
37
|
+
ID: "Sort by Job Queue ID.",
|
38
|
+
NAME: "Sort by Job Queue name.",
|
39
|
+
CREATED_AT: "Sort by creation timestamp.",
|
40
|
+
CREATOR_ID: "Sort by the ID of the creator.",
|
41
|
+
CREATOR_EMAIL: "Sort by the email of the creator.",
|
42
|
+
PROJECT_ID: "Sort by the Project ID.",
|
43
|
+
CLOUD_ID: "Sort by the Cloud ID.",
|
44
|
+
QUEUE_STATE: "Sort by the Job Queue's state (ACTIVE, SEALED).",
|
45
|
+
CLUSTER_STATE: "Sort by the state of the associated cluster.",
|
46
|
+
}
|
47
|
+
|
48
|
+
|
49
|
+
class ExecutionMode(ModelEnum):
|
50
|
+
"""Execution mode of a job queue."""
|
51
|
+
|
52
|
+
FIFO = "FIFO"
|
53
|
+
LIFO = "LIFO"
|
54
|
+
PRIORITY = "PRIORITY"
|
55
|
+
# Add other execution modes as needed
|
56
|
+
UNKNOWN = "UNKNOWN"
|
57
|
+
|
58
|
+
__docstrings__ = {
|
59
|
+
FIFO: "FIFO execution mode.",
|
60
|
+
LIFO: "LIFO execution mode.",
|
61
|
+
PRIORITY: "Priority-based execution mode.",
|
62
|
+
UNKNOWN: "Unknown execution mode.",
|
63
|
+
}
|
64
|
+
|
65
|
+
|
66
|
+
class ClusterState(ModelEnum):
|
67
|
+
"""Possible states for a cluster."""
|
68
|
+
|
69
|
+
RUNNING = "RUNNING"
|
70
|
+
TERMINATED = "TERMINATED"
|
71
|
+
PENDING = "PENDING"
|
72
|
+
# Add other states as needed
|
73
|
+
UNKNOWN = "UNKNOWN"
|
74
|
+
|
75
|
+
__docstrings__ = {
|
76
|
+
RUNNING: "The cluster is running.",
|
77
|
+
TERMINATED: "The cluster is terminated.",
|
78
|
+
PENDING: "The cluster is pending creation.",
|
79
|
+
UNKNOWN: "The state of the cluster is unknown.",
|
80
|
+
}
|
81
|
+
|
82
|
+
|
83
|
+
@dataclass(frozen=True)
|
84
|
+
class JobQueueStatus(ModelBase):
|
85
|
+
"""Represents the status and details of a Job Queue."""
|
86
|
+
|
87
|
+
id: str = field(metadata={"docstring": "Unique ID of the job queue."})
|
88
|
+
state: Union[JobQueueState, str] = field(
|
89
|
+
metadata={"docstring": "Current state of the job queue."}
|
90
|
+
)
|
91
|
+
name: Optional[str] = field(
|
92
|
+
default=None, metadata={"docstring": "Name of the job queue."}
|
93
|
+
)
|
94
|
+
creator_email: Optional[str] = field(
|
95
|
+
default=None,
|
96
|
+
metadata={"docstring": "Email of the user who created the job queue."},
|
97
|
+
)
|
98
|
+
project_id: Optional[str] = field(
|
99
|
+
default=None,
|
100
|
+
metadata={"docstring": "ID of the project this job queue belongs to."},
|
101
|
+
)
|
102
|
+
created_at: Optional[datetime] = field(
|
103
|
+
default=None,
|
104
|
+
metadata={"docstring": "Timestamp when the job queue was created."},
|
105
|
+
)
|
106
|
+
max_concurrency: Optional[int] = field(
|
107
|
+
default=None,
|
108
|
+
metadata={"docstring": "Maximum number of jobs allowed to run concurrently."},
|
109
|
+
)
|
110
|
+
idle_timeout_s: Optional[int] = field(
|
111
|
+
default=None,
|
112
|
+
metadata={
|
113
|
+
"docstring": "Idle timeout in seconds before the queue's cluster may shut down."
|
114
|
+
},
|
115
|
+
)
|
116
|
+
user_provided_id: Optional[str] = field(
|
117
|
+
default=None,
|
118
|
+
metadata={"docstring": "User provided identifier of the job queue."},
|
119
|
+
)
|
120
|
+
execution_mode: Optional[Union[ExecutionMode, str]] = field(
|
121
|
+
default=None, metadata={"docstring": "The execution mode of the job queue."}
|
122
|
+
)
|
123
|
+
creator_id: Optional[str] = field(
|
124
|
+
default=None,
|
125
|
+
metadata={"docstring": "Identifier of user who created the job queue."},
|
126
|
+
)
|
127
|
+
cloud_id: Optional[str] = field(
|
128
|
+
default=None,
|
129
|
+
metadata={"docstring": "The cloud ID associated with the job queue."},
|
130
|
+
)
|
131
|
+
total_jobs: Optional[int] = field(
|
132
|
+
default=None, metadata={"docstring": "Total number of jobs in the job queue."},
|
133
|
+
)
|
134
|
+
successful_jobs: Optional[int] = field(
|
135
|
+
default=None,
|
136
|
+
metadata={"docstring": "Number of successful jobs in the job queue."},
|
137
|
+
)
|
138
|
+
failed_jobs: Optional[int] = field(
|
139
|
+
default=None, metadata={"docstring": "Number of failed jobs in the job queue."},
|
140
|
+
)
|
141
|
+
active_jobs: Optional[int] = field(
|
142
|
+
default=None, metadata={"docstring": "Number of active jobs in the job queue."},
|
143
|
+
)
|
144
|
+
|
145
|
+
def _validate_id(self, id: str) -> str: # noqa: A002
|
146
|
+
if not isinstance(id, str) or not id:
|
147
|
+
raise ValueError("'id' must be a non-empty string.")
|
148
|
+
return id
|
149
|
+
|
150
|
+
def _validate_name(self, name: Optional[str]) -> Optional[str]:
|
151
|
+
if name is not None and not isinstance(name, str):
|
152
|
+
raise ValueError("'name' must be a string or None.")
|
153
|
+
return name
|
154
|
+
|
155
|
+
def _validate_state(self, state: Union[JobQueueState, str]) -> JobQueueState:
|
156
|
+
return JobQueueState.validate(state)
|
157
|
+
|
158
|
+
def _validate_creator_email(self, creator_email: Optional[str]) -> Optional[str]:
|
159
|
+
if creator_email is not None and not isinstance(creator_email, str):
|
160
|
+
raise ValueError("'creator_email' must be a string or None.")
|
161
|
+
return creator_email
|
162
|
+
|
163
|
+
def _validate_project_id(self, project_id: Optional[str]) -> Optional[str]:
|
164
|
+
if project_id is not None and not isinstance(project_id, str):
|
165
|
+
raise ValueError("'project_id' must be a string or None.")
|
166
|
+
return project_id
|
167
|
+
|
168
|
+
def _validate_created_at(
|
169
|
+
self, created_at: Optional[datetime]
|
170
|
+
) -> Optional[datetime]:
|
171
|
+
if created_at is not None and not isinstance(created_at, datetime):
|
172
|
+
raise ValueError("'created_at' must be a datetime object or None.")
|
173
|
+
return created_at
|
174
|
+
|
175
|
+
def _validate_max_concurrency(
|
176
|
+
self, max_concurrency: Optional[int]
|
177
|
+
) -> Optional[int]:
|
178
|
+
if max_concurrency is not None:
|
179
|
+
if not isinstance(max_concurrency, int):
|
180
|
+
raise ValueError("'max_concurrency' must be an integer or None.")
|
181
|
+
if max_concurrency < 0:
|
182
|
+
raise ValueError("'max_concurrency' cannot be negative.")
|
183
|
+
return max_concurrency
|
184
|
+
|
185
|
+
def _validate_idle_timeout_s(self, idle_timeout_s: Optional[int]) -> Optional[int]:
|
186
|
+
if idle_timeout_s is not None:
|
187
|
+
if not isinstance(idle_timeout_s, int):
|
188
|
+
raise ValueError("'idle_timeout_s' must be an integer or None.")
|
189
|
+
if idle_timeout_s < 0:
|
190
|
+
raise ValueError("'idle_timeout_s' cannot be negative.")
|
191
|
+
return idle_timeout_s
|
192
|
+
|
193
|
+
def _validate_user_provided_id(
|
194
|
+
self, user_provided_id: Optional[str]
|
195
|
+
) -> Optional[str]:
|
196
|
+
if user_provided_id is not None and not isinstance(user_provided_id, str):
|
197
|
+
raise ValueError("'user_provided_id' must be a string or None.")
|
198
|
+
return user_provided_id
|
199
|
+
|
200
|
+
def _validate_execution_mode(
|
201
|
+
self, execution_mode: Optional[Union[ExecutionMode, str]]
|
202
|
+
) -> Optional[ExecutionMode]:
|
203
|
+
if execution_mode is not None:
|
204
|
+
return ExecutionMode.validate(execution_mode)
|
205
|
+
return None
|
206
|
+
|
207
|
+
def _validate_creator_id(self, creator_id: Optional[str]) -> Optional[str]:
|
208
|
+
if creator_id is not None and not isinstance(creator_id, str):
|
209
|
+
raise ValueError("'creator_id' must be a string or None.")
|
210
|
+
return creator_id
|
211
|
+
|
212
|
+
def _validate_cluster_id(self, cluster_id: Optional[str]) -> Optional[str]:
|
213
|
+
if cluster_id is not None and not isinstance(cluster_id, str):
|
214
|
+
raise ValueError("'cluster_id' must be a string or None.")
|
215
|
+
return cluster_id
|
216
|
+
|
217
|
+
def _validate_current_cluster_state(
|
218
|
+
self, current_cluster_state: Optional[Union[ClusterState, str]]
|
219
|
+
) -> Optional[ClusterState]:
|
220
|
+
if current_cluster_state is not None:
|
221
|
+
return ClusterState.validate(current_cluster_state)
|
222
|
+
return None
|
223
|
+
|
224
|
+
def _validate_cloud_id(self, cloud_id: Optional[str]) -> Optional[str]:
|
225
|
+
if cloud_id is not None and not isinstance(cloud_id, str):
|
226
|
+
raise ValueError("'cloud_id' must be a string or None.")
|
227
|
+
return cloud_id
|
228
|
+
|
229
|
+
def _validate_total_jobs(self, total_jobs: Optional[int]) -> Optional[int]:
|
230
|
+
if total_jobs is not None:
|
231
|
+
if not isinstance(total_jobs, int):
|
232
|
+
raise ValueError("'total_jobs' must be an integer or None.")
|
233
|
+
if total_jobs < 0:
|
234
|
+
raise ValueError("'total_jobs' cannot be negative.")
|
235
|
+
return total_jobs
|
236
|
+
|
237
|
+
def _validate_successful_jobs(
|
238
|
+
self, successful_jobs: Optional[int]
|
239
|
+
) -> Optional[int]:
|
240
|
+
if successful_jobs is not None:
|
241
|
+
if not isinstance(successful_jobs, int):
|
242
|
+
raise ValueError("'successful_jobs' must be an integer or None.")
|
243
|
+
if successful_jobs < 0:
|
244
|
+
raise ValueError("'successful_jobs' cannot be negative.")
|
245
|
+
return successful_jobs
|
246
|
+
|
247
|
+
def _validate_failed_jobs(self, failed_jobs: Optional[int]) -> Optional[int]:
|
248
|
+
if failed_jobs is not None:
|
249
|
+
if not isinstance(failed_jobs, int):
|
250
|
+
raise ValueError("'failed_jobs' must be an integer or None.")
|
251
|
+
if failed_jobs < 0:
|
252
|
+
raise ValueError("'failed_jobs' cannot be negative.")
|
253
|
+
return failed_jobs
|
254
|
+
|
255
|
+
def _validate_active_jobs(self, active_jobs: Optional[int]) -> Optional[int]:
|
256
|
+
if active_jobs is not None:
|
257
|
+
if not isinstance(active_jobs, int):
|
258
|
+
raise ValueError("'active_jobs' must be an integer or None.")
|
259
|
+
if active_jobs < 0:
|
260
|
+
raise ValueError("'active_jobs' cannot be negative.")
|
261
|
+
return active_jobs
|
262
|
+
|
263
|
+
|
264
|
+
if TYPE_CHECKING:
|
265
|
+
JobQueueStatusKeys = Literal[
|
266
|
+
"active_jobs",
|
267
|
+
"cloud_id",
|
268
|
+
"created_at",
|
269
|
+
"creator_email",
|
270
|
+
"creator_id",
|
271
|
+
"execution_mode",
|
272
|
+
"failed_jobs",
|
273
|
+
"id",
|
274
|
+
"idle_timeout_s",
|
275
|
+
"max_concurrency",
|
276
|
+
"name",
|
277
|
+
"project_id",
|
278
|
+
"state",
|
279
|
+
"successful_jobs",
|
280
|
+
"total_jobs",
|
281
|
+
"user_provided_id",
|
282
|
+
]
|
283
|
+
else:
|
284
|
+
JobQueueStatusKeys = Literal[tuple(get_type_hints(JobQueueStatus).keys())]
|
anyscale/scripts.py
CHANGED
@@ -121,7 +121,7 @@ cli.add_command(version_cli)
|
|
121
121
|
cli.add_command(list_cli)
|
122
122
|
cli.add_command(cluster_env_cli)
|
123
123
|
cli.add_command(job_cli)
|
124
|
-
|
124
|
+
cli.add_command(job_queue_cli)
|
125
125
|
cli.add_command(schedule_cli)
|
126
126
|
cli.add_command(service_cli)
|
127
127
|
cli.add_command(cluster_cli)
|
@@ -0,0 +1,178 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
import asyncio
|
3
|
+
import logging
|
4
|
+
import os
|
5
|
+
import sys
|
6
|
+
|
7
|
+
import websockets
|
8
|
+
|
9
|
+
|
10
|
+
# Define global constants
|
11
|
+
DEFAULT_CHUNK_SIZE = 4096
|
12
|
+
LOG_FORMAT = "%(asctime)s - %(name)s - %(levelname)s - %(funcName)s - %(message)s"
|
13
|
+
|
14
|
+
# Configure logging
|
15
|
+
DEBUG_ENABLED = os.environ.get("ANYSCALE_SSH_DEBUG") == "1"
|
16
|
+
LOG_LEVEL = logging.DEBUG if DEBUG_ENABLED else logging.INFO
|
17
|
+
logging.basicConfig(level=LOG_LEVEL, format=LOG_FORMAT, stream=sys.stderr)
|
18
|
+
logger = logging.getLogger(__name__)
|
19
|
+
|
20
|
+
|
21
|
+
async def pump(stream_reader: asyncio.StreamReader, send_func):
|
22
|
+
logger.debug("pump: started")
|
23
|
+
try:
|
24
|
+
while True:
|
25
|
+
logger.debug("pump: trying to read from stream_reader")
|
26
|
+
chunk = await stream_reader.read(DEFAULT_CHUNK_SIZE)
|
27
|
+
logger.debug(
|
28
|
+
f"pump: read {len(chunk) if chunk else 0} bytes from stream_reader"
|
29
|
+
)
|
30
|
+
if not chunk: # EOF
|
31
|
+
logger.debug("pump: EOF from stream_reader, closing send_func")
|
32
|
+
await send_func.close()
|
33
|
+
logger.debug("pump: send_func closed")
|
34
|
+
return
|
35
|
+
logger.debug(f"pump: sending {len(chunk)} bytes to websocket")
|
36
|
+
await send_func(chunk)
|
37
|
+
logger.debug("pump: sent to websocket")
|
38
|
+
except websockets.ConnectionClosedOK:
|
39
|
+
logger.debug("pump: ConnectionClosedOK")
|
40
|
+
except asyncio.CancelledError:
|
41
|
+
logger.debug("pump: CancelledError")
|
42
|
+
raise
|
43
|
+
except Exception:
|
44
|
+
logger.exception("pump: Exception encountered")
|
45
|
+
if not getattr(send_func, "closed", False):
|
46
|
+
logger.debug("pump: Exception, closing send_func")
|
47
|
+
await send_func.close()
|
48
|
+
logger.debug("pump: send_func closed due to exception")
|
49
|
+
raise
|
50
|
+
finally:
|
51
|
+
logger.debug("pump: finished")
|
52
|
+
|
53
|
+
|
54
|
+
async def drain(ws_receive_func):
|
55
|
+
logger.debug("drain: started")
|
56
|
+
try:
|
57
|
+
async for msg in ws_receive_func:
|
58
|
+
logger.debug(
|
59
|
+
f"drain: received {len(msg) if isinstance(msg, bytes) else type(msg)} from websocket"
|
60
|
+
)
|
61
|
+
if isinstance(msg, bytes):
|
62
|
+
sys.stdout.buffer.write(msg)
|
63
|
+
sys.stdout.buffer.flush()
|
64
|
+
logger.debug("drain: wrote to stdout and flushed")
|
65
|
+
else:
|
66
|
+
logger.warning(f"Received unexpected non-bytes message: {type(msg)}")
|
67
|
+
except websockets.ConnectionClosedOK:
|
68
|
+
logger.debug("drain: ConnectionClosedOK")
|
69
|
+
except asyncio.CancelledError:
|
70
|
+
logger.debug("drain: CancelledError")
|
71
|
+
raise
|
72
|
+
except Exception:
|
73
|
+
logger.exception("drain: Exception encountered")
|
74
|
+
raise
|
75
|
+
finally:
|
76
|
+
logger.debug("drain: finished")
|
77
|
+
|
78
|
+
|
79
|
+
async def main():
|
80
|
+
if len(sys.argv) < 2:
|
81
|
+
logger.debug("WebSocket URL must be provided as the first argument.")
|
82
|
+
sys.exit(1)
|
83
|
+
|
84
|
+
ws_url = sys.argv[1]
|
85
|
+
logger.debug(f"WebSocket URL: {ws_url}")
|
86
|
+
|
87
|
+
# Check for optional token argument
|
88
|
+
auth_token = None
|
89
|
+
if len(sys.argv) >= 3:
|
90
|
+
auth_token = sys.argv[2]
|
91
|
+
logger.debug("Authentication token provided as second argument")
|
92
|
+
|
93
|
+
loop = asyncio.get_running_loop()
|
94
|
+
reader = asyncio.StreamReader(loop=loop)
|
95
|
+
protocol = asyncio.StreamReaderProtocol(reader, loop=loop)
|
96
|
+
await loop.connect_read_pipe(lambda: protocol, sys.stdin)
|
97
|
+
logger.debug("stdin connected to StreamReader")
|
98
|
+
|
99
|
+
# Prepare additional headers if token is provided
|
100
|
+
additional_headers = {}
|
101
|
+
if auth_token:
|
102
|
+
additional_headers["Authorization"] = f"Bearer {auth_token}"
|
103
|
+
logger.debug("Added Authorization header with Bearer token")
|
104
|
+
|
105
|
+
try:
|
106
|
+
logger.debug("Attempting to connect to websocket...")
|
107
|
+
async with websockets.connect(
|
108
|
+
ws_url, additional_headers=additional_headers
|
109
|
+
) as ws:
|
110
|
+
logger.debug("Connected to websocket successfully")
|
111
|
+
pump_task = asyncio.create_task(pump(reader, ws.send))
|
112
|
+
drain_task = asyncio.create_task(drain(ws))
|
113
|
+
|
114
|
+
done, pending = await asyncio.wait(
|
115
|
+
[pump_task, drain_task], return_when=asyncio.FIRST_COMPLETED,
|
116
|
+
)
|
117
|
+
|
118
|
+
# Convert pending set to a list to maintain order for results processing
|
119
|
+
pending_list = list(pending)
|
120
|
+
|
121
|
+
for task in pending_list: # Iterate over the list
|
122
|
+
logger.debug(f"Cancelling pending task {task.get_name()}")
|
123
|
+
task.cancel()
|
124
|
+
|
125
|
+
# Wait for pending tasks to finish cancellation
|
126
|
+
# Pass the list of pending tasks to gather
|
127
|
+
results = await asyncio.gather(*pending_list, return_exceptions=True)
|
128
|
+
|
129
|
+
for i, result in enumerate(results):
|
130
|
+
if isinstance(result, Exception) and not isinstance(
|
131
|
+
result, asyncio.CancelledError
|
132
|
+
):
|
133
|
+
# Use pending_list[i] to get the corresponding task
|
134
|
+
task_name = (
|
135
|
+
pending_list[i].get_name()
|
136
|
+
if pending_list[i].get_name()
|
137
|
+
else "unnamed task"
|
138
|
+
)
|
139
|
+
logger.debug(
|
140
|
+
f"Pending task '{task_name}' raised an exception: {result!r}"
|
141
|
+
)
|
142
|
+
logger.debug("Pump and drain tasks finished or cancelled.")
|
143
|
+
|
144
|
+
except asyncio.TimeoutError:
|
145
|
+
logger.error("Connection to websocket timed out.")
|
146
|
+
sys.exit(1)
|
147
|
+
except Exception:
|
148
|
+
logger.exception("Exception in main connection/task management logic")
|
149
|
+
raise # Re-raise after logging
|
150
|
+
finally:
|
151
|
+
logger.debug("main: finished")
|
152
|
+
|
153
|
+
|
154
|
+
if __name__ == "__main__":
|
155
|
+
try:
|
156
|
+
logger.debug("Starting proxy script")
|
157
|
+
asyncio.run(main())
|
158
|
+
except KeyboardInterrupt:
|
159
|
+
logger.debug("KeyboardInterrupt received. Exiting.")
|
160
|
+
# Python's default handler for KeyboardInterrupt will exit (usually with code 130)
|
161
|
+
except SystemExit as e:
|
162
|
+
logger.debug(f"SystemExit called with code {e.code}")
|
163
|
+
sys.exit(e.code) # Propagate the intended exit code
|
164
|
+
except websockets.exceptions.InvalidStatusCode as e_is:
|
165
|
+
# Handle InvalidStatusCode specifically to access status_code and headers
|
166
|
+
logger.debug(
|
167
|
+
f"Unhandled WebSocket InvalidStatusCode error: {e_is!r}, Status code: {e_is.status_code}"
|
168
|
+
)
|
169
|
+
sys.exit(1)
|
170
|
+
except (websockets.exceptions.WebSocketException, OSError) as e_wso:
|
171
|
+
# Handle other WebSocket and OS errors
|
172
|
+
logger.debug(f"Unhandled WebSocket/OS error: {e_wso!r}")
|
173
|
+
sys.exit(1)
|
174
|
+
except Exception as e_unhandled: # noqa: BLE001 # Catch any other unexpected exceptions
|
175
|
+
logger.debug(f"Unhandled exception: {e_unhandled!r}", exc_info=True)
|
176
|
+
sys.exit(1)
|
177
|
+
finally:
|
178
|
+
logger.debug("Proxy script exiting")
|
anyscale/version.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = "0.26.
|
1
|
+
__version__ = "0.26.30"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: anyscale
|
3
|
-
Version: 0.26.
|
3
|
+
Version: 0.26.30
|
4
4
|
Summary: Command Line Interface for Anyscale
|
5
5
|
Author: Anyscale Inc.
|
6
6
|
License: AS License
|
@@ -37,6 +37,8 @@ Requires-Dist: log_symbols>=0.0.14
|
|
37
37
|
Requires-Dist: spinners>=0.0.24
|
38
38
|
Requires-Dist: termcolor>=1.1.0
|
39
39
|
Requires-Dist: colorama>=0.3.9
|
40
|
+
Requires-Dist: websockets; python_version > "3.8"
|
41
|
+
Requires-Dist: websockets==13.1.0; python_version <= "3.8"
|
40
42
|
Provides-Extra: backend
|
41
43
|
Requires-Dist: terminado==0.10.1; extra == "backend"
|
42
44
|
Requires-Dist: tornado; extra == "backend"
|
@@ -1,4 +1,4 @@
|
|
1
|
-
anyscale/__init__.py,sha256=
|
1
|
+
anyscale/__init__.py,sha256=ENKr1ipvMdG5mR9YEPCLBKgNGW-cWW5GHE6GRJZ1GqY,8951
|
2
2
|
anyscale/anyscale-cloud-setup-gcp-oa.yaml,sha256=varT3AFGxDy7V8o3_zCwUyoi22MiDmZovfpvkbkyeAU,2715
|
3
3
|
anyscale/anyscale-cloud-setup-gcp.yaml,sha256=b9fKDG8syJuoZJOAr4LZdLGto3sDDil9EOoboXWC9UE,4011
|
4
4
|
anyscale/anyscale-cloud-setup-oa.yaml,sha256=MMfjT2UCNQS43I3mGOMoSq1cq1dufxtnXU9Zy260TK8,3192
|
@@ -23,19 +23,19 @@ anyscale/integrations.py,sha256=bv8m5rck0Cfxpi59klyYGJhPknpXtnHCSvF1Uu8vTHU,1339
|
|
23
23
|
anyscale/links.py,sha256=xFXN5TjL61p5T23dn66nNalkV47LNkPJxQqOPhGXfww,193
|
24
24
|
anyscale/memorydb_supported_zones.json,sha256=l5Iup9wFDZcHLfZqH640axxe4UiKteuraZRohi3MwRk,1098
|
25
25
|
anyscale/project_utils.py,sha256=SBwkD5B10ku2kkmp6y-Cr5RL7xf52b9zELP35kfg2PE,17621
|
26
|
-
anyscale/scripts.py,sha256=
|
26
|
+
anyscale/scripts.py,sha256=d806tM5h0CNUOdu8e_McVmjttiJeX77rQ_R6L8zAT8I,5593
|
27
27
|
anyscale/snapshot.py,sha256=UGJT5C1s_4xmQxjWODK5DFpGxHRBX5jOCdSCqXESH8E,1685
|
28
28
|
anyscale/tables.py,sha256=TV4F2uLnwehvbkAfaP7iuLlT2wLIo6ORH2LVdRGXW5g,2840
|
29
29
|
anyscale/util.py,sha256=14AHIhl4c4hKAW4gLZIvy5w56-zDjcPmrdWqazsvnHU,41860
|
30
|
-
anyscale/version.py,sha256=
|
30
|
+
anyscale/version.py,sha256=tfQN72SxYDy_EDQ1uFfglNDm1-8v1NaeHxD6QZGWR4Y,24
|
31
31
|
anyscale/workspace_utils.py,sha256=OViE88CnIF5ruVxd3kazQ0Mf2BxqtMq6wx-XQ5A2cp8,1204
|
32
32
|
anyscale/_private/anyscale_client/README.md,sha256=gk8obk7kqg6VWoUHcqDMwJULh35tYKEZFC0UF_dixGA,718
|
33
33
|
anyscale/_private/anyscale_client/__init__.py,sha256=807Blx3RHQeS8BmKZcsOQQ4dYoKlCnpm6Bdsif2CrHg,337
|
34
|
-
anyscale/_private/anyscale_client/anyscale_client.py,sha256=
|
35
|
-
anyscale/_private/anyscale_client/common.py,sha256=
|
36
|
-
anyscale/_private/anyscale_client/fake_anyscale_client.py,sha256=
|
34
|
+
anyscale/_private/anyscale_client/anyscale_client.py,sha256=s9m_xTrVwpcOfhuZnXfu67N5vZ6hAJoB-Mp_wsDqfmI,85335
|
35
|
+
anyscale/_private/anyscale_client/common.py,sha256=h-glZioO0NPfT2tqVYP5f4_zvNy5Q0yyj8nawdIbOPU,26617
|
36
|
+
anyscale/_private/anyscale_client/fake_anyscale_client.py,sha256=pJGJZMCVeSUUheAe50j5mJwjUeyWqONBtcAbiKtDpjo,57533
|
37
37
|
anyscale/_private/docgen/README.md,sha256=z0tj8Jy0KmxWJBQMHKyzXGX_cYYgI8m5DCD6KCMU8oI,762
|
38
|
-
anyscale/_private/docgen/__main__.py,sha256=
|
38
|
+
anyscale/_private/docgen/__main__.py,sha256=cUV0mW8CapfGjcnIzz1FMBN_waZ8TWy_two9WgZcHeQ,25211
|
39
39
|
anyscale/_private/docgen/api.md,sha256=VKW293yubbeUG17A38wYuaONKDL5XICMguyfZ2xkIyY,27495
|
40
40
|
anyscale/_private/docgen/generator.py,sha256=jAOaprAeU659glRDBATAkAQeYC1nDU14jgdobcILS1s,21737
|
41
41
|
anyscale/_private/docgen/generator_legacy.py,sha256=pss_6ONF55XhARrKGcREDmg0J5plWact6USgb5Tr5mM,3002
|
@@ -712,32 +712,32 @@ anyscale/commands/cloud_commands.py,sha256=E2zWvr4154mG7oBsygiDyLQ-zKAS3RwNB4DEB
|
|
712
712
|
anyscale/commands/cloud_commands_util.py,sha256=d-6TSZ_syrGkZ3Fc1uRX6jG4dqYMebNkBNpYLojOJFg,247
|
713
713
|
anyscale/commands/cluster_commands.py,sha256=taNcffyFfqJ1MgOQd0cz9kzRXWFTdp-wfLPM4l_2tBc,13487
|
714
714
|
anyscale/commands/cluster_env_commands.py,sha256=KNWylyE8Ew1sDi7yu2Tp4RLcRu2_KJJJIzVGRyPflJo,3899
|
715
|
-
anyscale/commands/command_examples.py,sha256=
|
715
|
+
anyscale/commands/command_examples.py,sha256=Oc8Y0lhnfK0c2L24ysJgCbz2VfUEbnEfRbMSIYIDQgQ,26778
|
716
716
|
anyscale/commands/compute_config_commands.py,sha256=vdyrtMcdP8eeK32p_Y6zF-qps6_SyzprhbjRZ9p18tQ,7828
|
717
717
|
anyscale/commands/config_commands.py,sha256=p55uM6WrhfbFoRXC9hNAV-8c5ANghw7tBUYwaQDAtjE,7195
|
718
718
|
anyscale/commands/exec_commands.py,sha256=cMOP1u6xQbl81h69Jug3y73XnNSwpbM6XC1X57SIp4c,465
|
719
719
|
anyscale/commands/experimental_integrations_commands.py,sha256=_e1yESwRGu621Y5MUBqdC3S79YJUdEG1W67b_uQL8wY,2038
|
720
720
|
anyscale/commands/image_commands.py,sha256=hdarnfH5upsgErzs8hJ0CcrWiVu7PxWLKEoD3892lk4,3651
|
721
721
|
anyscale/commands/job_commands.py,sha256=hXEwOSD70JEfNsk-cGYXsMT5pEyxtzdX4YJ8ng-D0_U,25194
|
722
|
-
anyscale/commands/job_queue_commands.py,sha256=
|
722
|
+
anyscale/commands/job_queue_commands.py,sha256=FEPzfvD3FO0nbApqh6sZLbEOQoT3EmrkMNBPqeWC4dU,11660
|
723
723
|
anyscale/commands/list_commands.py,sha256=rcDn-Qh3z99zE9oD7RPPa80-y0ml90W4UbGiYMw4aQo,2710
|
724
|
-
anyscale/commands/list_util.py,sha256=
|
724
|
+
anyscale/commands/list_util.py,sha256=up2f4zGNe6oNOqLr8tYnEpBbSD9pHFLC4cYzi1jDzII,4129
|
725
725
|
anyscale/commands/login_commands.py,sha256=0pIjpRC3Mw86WjDubJ5v2FHINke-Tk3JvGal_aiQMG0,3477
|
726
726
|
anyscale/commands/logs_commands.py,sha256=OgOwBsEbhcGH-STQ9MOJy8sQBYcZYmd31wzHzVPUo0g,9495
|
727
727
|
anyscale/commands/machine_commands.py,sha256=73rgz9aTIOBYcWX8zH-OYlUx3UuhODHtl1RnKtBdU1E,3641
|
728
|
-
anyscale/commands/machine_pool_commands.py,sha256=
|
728
|
+
anyscale/commands/machine_pool_commands.py,sha256=mOvcvQkf2IZ46xuNazeY1qHATKm00Z4h9QuVdxWhA2k,9700
|
729
729
|
anyscale/commands/migrate_commands.py,sha256=QL1sVL7KGaOFpL4sVNUM96I4kOEzYmIXFuG90LRJ9Uw,3258
|
730
730
|
anyscale/commands/organization_invitation_commands.py,sha256=L0OEz_mF5Dm02KjVzDu_CRKlLm4-c2w8HcHqEvP2nIs,2723
|
731
731
|
anyscale/commands/project_commands.py,sha256=xVm-W5kKzgfbQjAiHSRhnyMIlYgGji1TUfYAi8QrGBo,7131
|
732
732
|
anyscale/commands/resource_quota_commands.py,sha256=J6r8b6Bo1wMys5pYWieD6F-VsC2OpQZGVLaNFlvAKmI,8536
|
733
733
|
anyscale/commands/schedule_commands.py,sha256=mdwelVght3HnN5YPjtG4Spn0KiEDWmg-UosfaDkQPKE,14485
|
734
734
|
anyscale/commands/service_account_commands.py,sha256=u45N2akHsZxyu5LK03FGEEnZh4dTt4B2Be-dXgbSg3U,3977
|
735
|
-
anyscale/commands/service_commands.py,sha256=
|
735
|
+
anyscale/commands/service_commands.py,sha256=lq_FuS9YWvPJkzH641DBLFISMIi1EwVVwc5kTqOftvo,33660
|
736
736
|
anyscale/commands/session_commands_hidden.py,sha256=APEypnUB1yV2Rr6wdSFWy1vQbAnn-lOn0rU2enF5JdM,6200
|
737
737
|
anyscale/commands/user_commands.py,sha256=C-i1dGpdhboywN_2XgPS2BekKx2y6LZq8c8gvS0S-tY,1259
|
738
738
|
anyscale/commands/util.py,sha256=N8gkVv9LBr5QypBGm2e_Pw2F2e_tiiR4YNLmn8CtsK0,5124
|
739
739
|
anyscale/commands/workspace_commands.py,sha256=RjK8rPlIPoReZBb7RPB6z9aFp7gSmnsxDge8d3uj9t8,15768
|
740
|
-
anyscale/commands/workspace_commands_v2.py,sha256=
|
740
|
+
anyscale/commands/workspace_commands_v2.py,sha256=D6YmcjD7Db7KK6adaGxiyiIdlCzMa4Y1v_xVT_cWmmI,43582
|
741
741
|
anyscale/commands/anyscale_api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
742
742
|
anyscale/commands/anyscale_api/api_commands.py,sha256=Dze2wuzeZurlbxOu4YHZqln026yXgtWi_jxccyKANmw,473
|
743
743
|
anyscale/commands/anyscale_api/session_operations_commands.py,sha256=LiavgWn2-F7b3GZD0ng_Q1HiEP4o32e8nO9yU92AgxI,803
|
@@ -763,7 +763,7 @@ anyscale/controllers/cluster_env_controller.py,sha256=JalGzcmnFtMHefYL5U6ijMY3nX
|
|
763
763
|
anyscale/controllers/compute_config_controller.py,sha256=GnZAJGrPAmGO6MWvqna-0-DBlwC1y8fnKgwsDVa0eDs,14110
|
764
764
|
anyscale/controllers/config_controller.py,sha256=VsfdARHxo4tMLfeiYkTNOMGW3sIcNhVqYi37-SruKnk,17125
|
765
765
|
anyscale/controllers/experimental_integrations_controller.py,sha256=_22_hAQCJIMg3E10s8xajoFF6Lf1HqVlAdAVt0Rh2DY,3889
|
766
|
-
anyscale/controllers/job_controller.py,sha256=
|
766
|
+
anyscale/controllers/job_controller.py,sha256=sNQGzSLtp6e8PSbrmMrW_dp3pYytS8KCGcE-YjaNz5I,25425
|
767
767
|
anyscale/controllers/jobs_bg_controller.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
768
768
|
anyscale/controllers/list_controller.py,sha256=oaOS6oo2TKPpXhGjs_laxuIVKinv3FwYfHt1CIzeTuU,11621
|
769
769
|
anyscale/controllers/logs_controller.py,sha256=x5GBUVdPYhbWRA3RfMQZJi3hBS2i35RkgzROfmY47h4,17647
|
@@ -786,6 +786,10 @@ anyscale/job/__init__.py,sha256=_5Nb3a11F4oAHXirTgx5vDdgpVzJ62FdGFmADxxvcPE,5051
|
|
786
786
|
anyscale/job/commands.py,sha256=je0WDrd3mxt3iuoGqKvE_2TuMWsffXXM_IQ5SRjUKnU,8524
|
787
787
|
anyscale/job/models.py,sha256=w2qqm69ccCf9Y6Hwe5cLszqn3asS19s24t4akuZPsUM,17922
|
788
788
|
anyscale/job/_private/job_sdk.py,sha256=6kQ5bDggA3f1MXcrlXAGrFRA7WAOcvKDVi8FXwq7z-E,18871
|
789
|
+
anyscale/job_queue/__init__.py,sha256=4KhAj-JkzqJdz1AbUoO8sQ4weWISBYaMPe6uRj7-5SA,3194
|
790
|
+
anyscale/job_queue/commands.py,sha256=pVgX6MG15zlUFgz6lUvAFqTSYY7gAZ5cFmDJY3QRTx0,3948
|
791
|
+
anyscale/job_queue/models.py,sha256=gl98WWvvdUlCWaSpLyUHggzNmUr49ti39itB37mznM0,10538
|
792
|
+
anyscale/job_queue/_private/job_queue_sdk.py,sha256=2ytSoTjDQWaWf4iRWtBlaaPsHpajGg_lBL7qbiOmAVc,5992
|
789
793
|
anyscale/llm/__init__.py,sha256=lRp09PWR2gcJF_1Y9ieIIQWirijP5p_l3AQkTir2iN4,76
|
790
794
|
anyscale/llm/sdk.py,sha256=VmA03eegizqikWR3eH2jYNiA8-FY3Qq1GGtvdkgU470,733
|
791
795
|
anyscale/llm/dataset/__init__.py,sha256=J-z8d7Bdvjf_5OOjcHrFQrXnCxbdcvzUBcyapawIuHg,131
|
@@ -1042,6 +1046,7 @@ anyscale/utils/ray_version_checker.py,sha256=IiBJzBFJpOs4or0FeuaXKRysbCaFzCEoO_D
|
|
1042
1046
|
anyscale/utils/ray_version_utils.py,sha256=DhrkyJAB7M-61QA73RkuVwXk3FfApFH3vShGSbcdsck,1932
|
1043
1047
|
anyscale/utils/runtime_env.py,sha256=WDHQXJYFZM9__RvuT6Fq66fWgtMENGP3umz3zLqHczU,18592
|
1044
1048
|
anyscale/utils/s3.py,sha256=cxlR5baJIGaEFRUmND_i66Nh9TA7N0AGgcq8MdLZBdU,3427
|
1049
|
+
anyscale/utils/ssh_websocket_proxy.py,sha256=AHDzFk6G8x4envhWs1IAYjv08VZIMieug7e-1n8-xhw,6805
|
1045
1050
|
anyscale/utils/user_utils.py,sha256=wM0npdWAfaahRMUI26jz8tm3oQsqpk7A1QEyygOtt-M,573
|
1046
1051
|
anyscale/utils/workload_types.py,sha256=kKAaKDh5bd6zA2f1HR0VMlkhu9nAIeUMM-NMIGP0ZR4,128
|
1047
1052
|
anyscale/utils/workspace_notification.py,sha256=3utuEVFj7iHAXTHXDWedPcycIV3b0bpdQtAWz2n2GPI,1129
|
@@ -1058,10 +1063,10 @@ anyscale/workspace/__init__.py,sha256=Innbm5ZhCyADEVBiYSo_vbpKwUNcMzVSAfxIGKOYe6
|
|
1058
1063
|
anyscale/workspace/commands.py,sha256=b1sqNseoPj-1VXznqQOLe0V_a663bOTvJX-TaOMJa1Y,14590
|
1059
1064
|
anyscale/workspace/models.py,sha256=HBvM9ybOdJjqQeViQ30C36gdKT_AwH_JHPoL-DTkESo,9841
|
1060
1065
|
anyscale/workspace/_private/workspace_sdk.py,sha256=2CMeYfJt0UtIFCocDn1ukw1iI5esKHdopLe6duEs-qE,27599
|
1061
|
-
anyscale-0.26.
|
1062
|
-
anyscale-0.26.
|
1063
|
-
anyscale-0.26.
|
1064
|
-
anyscale-0.26.
|
1065
|
-
anyscale-0.26.
|
1066
|
-
anyscale-0.26.
|
1067
|
-
anyscale-0.26.
|
1066
|
+
anyscale-0.26.30.dist-info/LICENSE,sha256=UOPu974Wzsna6frFv1mu4VrZgNdZT7lbcNPzo5ue3qs,3494
|
1067
|
+
anyscale-0.26.30.dist-info/METADATA,sha256=h5NOjd0pDVHlNOcaFFcdJgKeuIR47gCQmEmEgUUTz-M,3269
|
1068
|
+
anyscale-0.26.30.dist-info/NOTICE,sha256=gHqDhSnUYlRXX-mDOL5FtE7774oiKyV_HO80qM3r9Xo,196
|
1069
|
+
anyscale-0.26.30.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
1070
|
+
anyscale-0.26.30.dist-info/entry_points.txt,sha256=NqO18sCZn6zG6J0S38itjcN00s7aE3C3v3k5lMAfCLk,51
|
1071
|
+
anyscale-0.26.30.dist-info/top_level.txt,sha256=g3NVNS8Oh0NZwbFFgeX696C5MZZkS5dqV2NqcsbDRJE,9
|
1072
|
+
anyscale-0.26.30.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|