flyte 0.2.0b3__py3-none-any.whl → 0.2.0b5__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.
Potentially problematic release.
This version of flyte might be problematic. Click here for more details.
- flyte/_build.py +3 -2
- flyte/_deploy.py +4 -4
- flyte/_initialize.py +17 -3
- flyte/_internal/controllers/remote/_core.py +5 -4
- flyte/_internal/controllers/remote/_service_protocol.py +6 -6
- flyte/_protos/logs/dataplane/payload_pb2.py +28 -24
- flyte/_protos/logs/dataplane/payload_pb2.pyi +11 -2
- flyte/_protos/workflow/common_pb2.py +27 -0
- flyte/_protos/workflow/common_pb2.pyi +14 -0
- flyte/_protos/workflow/common_pb2_grpc.py +4 -0
- flyte/_protos/workflow/queue_service_pb2.py +39 -41
- flyte/_protos/workflow/queue_service_pb2.pyi +30 -28
- flyte/_protos/workflow/queue_service_pb2_grpc.py +15 -15
- flyte/_protos/workflow/run_definition_pb2.py +14 -14
- flyte/_protos/workflow/run_definition_pb2.pyi +4 -2
- flyte/_protos/workflow/task_definition_pb2.py +14 -13
- flyte/_protos/workflow/task_definition_pb2.pyi +7 -3
- flyte/_run.py +7 -5
- flyte/_trace.py +1 -6
- flyte/_version.py +2 -2
- flyte/cli/__init__.py +10 -0
- flyte/cli/_abort.py +26 -0
- flyte/{_cli → cli}/_common.py +2 -0
- flyte/{_cli → cli}/_create.py +1 -1
- flyte/{_cli → cli}/_delete.py +1 -1
- flyte/{_cli → cli}/_get.py +12 -3
- flyte/{_cli → cli}/_run.py +49 -16
- flyte/{_cli → cli}/main.py +10 -1
- flyte/config/_config.py +2 -0
- flyte/errors.py +9 -0
- flyte/io/_dir.py +2 -2
- flyte/io/_file.py +1 -4
- flyte/remote/_data.py +3 -3
- flyte/remote/_logs.py +80 -27
- flyte/remote/_project.py +8 -9
- flyte/remote/_run.py +194 -107
- flyte/remote/_secret.py +12 -12
- flyte/remote/_task.py +3 -3
- flyte/report/_report.py +4 -4
- flyte/syncify/__init__.py +5 -0
- flyte/syncify/_api.py +277 -0
- {flyte-0.2.0b3.dist-info → flyte-0.2.0b5.dist-info}/METADATA +2 -3
- {flyte-0.2.0b3.dist-info → flyte-0.2.0b5.dist-info}/RECORD +48 -43
- {flyte-0.2.0b3.dist-info → flyte-0.2.0b5.dist-info}/entry_points.txt +1 -1
- flyte/_api_commons.py +0 -3
- flyte/_cli/__init__.py +0 -0
- /flyte/{_cli → cli}/_deploy.py +0 -0
- /flyte/{_cli → cli}/_params.py +0 -0
- {flyte-0.2.0b3.dist-info → flyte-0.2.0b5.dist-info}/WHEEL +0 -0
- {flyte-0.2.0b3.dist-info → flyte-0.2.0b5.dist-info}/top_level.txt +0 -0
flyte/remote/_run.py
CHANGED
|
@@ -11,15 +11,17 @@ from google.protobuf import timestamp
|
|
|
11
11
|
from rich.console import Console
|
|
12
12
|
from rich.progress import Progress, SpinnerColumn, TextColumn, TimeElapsedColumn
|
|
13
13
|
|
|
14
|
-
from flyte.
|
|
15
|
-
from flyte._initialize import get_client, get_common_config, requires_client
|
|
14
|
+
from flyte._initialize import ensure_client, get_client, get_common_config
|
|
16
15
|
from flyte._protos.common import identifier_pb2, list_pb2
|
|
17
16
|
from flyte._protos.workflow import run_definition_pb2, run_service_pb2
|
|
17
|
+
from flyte.syncify import syncify
|
|
18
18
|
|
|
19
19
|
from .._protos.workflow.run_service_pb2 import WatchActionDetailsResponse
|
|
20
20
|
from ._console import get_run_url
|
|
21
21
|
from ._logs import Logs
|
|
22
22
|
|
|
23
|
+
WaitFor = Literal["terminal", "running", "logs-ready"]
|
|
24
|
+
|
|
23
25
|
|
|
24
26
|
def _action_time_phase(action: run_definition_pb2.Action | run_definition_pb2.ActionDetails) -> rich.repr.Result:
|
|
25
27
|
"""
|
|
@@ -104,14 +106,13 @@ class Run:
|
|
|
104
106
|
raise RuntimeError("Run does not have an action")
|
|
105
107
|
self.action = Action(self.pb2.action)
|
|
106
108
|
|
|
109
|
+
@syncify
|
|
107
110
|
@classmethod
|
|
108
|
-
@requires_client
|
|
109
|
-
@syncer.wrap
|
|
110
111
|
async def listall(
|
|
111
112
|
cls,
|
|
112
113
|
filters: str | None = None,
|
|
113
114
|
sort_by: Tuple[str, Literal["asc", "desc"]] | None = None,
|
|
114
|
-
) ->
|
|
115
|
+
) -> AsyncIterator[Run]:
|
|
115
116
|
"""
|
|
116
117
|
Get all runs for the current project and domain.
|
|
117
118
|
|
|
@@ -119,6 +120,7 @@ class Run:
|
|
|
119
120
|
:param sort_by: The sorting criteria for the project list, in the format (field, order).
|
|
120
121
|
:return: An iterator of runs.
|
|
121
122
|
"""
|
|
123
|
+
ensure_client()
|
|
122
124
|
token = None
|
|
123
125
|
sort_by = sort_by or ("created_at", "asc")
|
|
124
126
|
sort_pb2 = list_pb2.Sort(
|
|
@@ -148,16 +150,16 @@ class Run:
|
|
|
148
150
|
if not token:
|
|
149
151
|
break
|
|
150
152
|
|
|
153
|
+
@syncify
|
|
151
154
|
@classmethod
|
|
152
|
-
@requires_client
|
|
153
|
-
@syncer.wrap
|
|
154
155
|
async def get(cls, name: str) -> Run:
|
|
155
156
|
"""
|
|
156
157
|
Get the current run.
|
|
157
158
|
|
|
158
159
|
:return: The current run.
|
|
159
160
|
"""
|
|
160
|
-
|
|
161
|
+
ensure_client()
|
|
162
|
+
run_details: RunDetails = await RunDetails.get.aio(name=name)
|
|
161
163
|
run = run_definition_pb2.Run(
|
|
162
164
|
action=run_definition_pb2.Action(
|
|
163
165
|
id=run_details.action_id,
|
|
@@ -179,74 +181,38 @@ class Run:
|
|
|
179
181
|
"""
|
|
180
182
|
Get the phase of the run.
|
|
181
183
|
"""
|
|
182
|
-
return
|
|
184
|
+
return self.action.phase
|
|
185
|
+
|
|
186
|
+
@property
|
|
187
|
+
def raw_phase(self) -> run_definition_pb2.Phase:
|
|
188
|
+
"""
|
|
189
|
+
Get the raw phase of the run.
|
|
190
|
+
"""
|
|
191
|
+
return self.action.raw_phase
|
|
183
192
|
|
|
184
|
-
@
|
|
185
|
-
async def wait(self, quiet: bool = False) -> None:
|
|
193
|
+
@syncify
|
|
194
|
+
async def wait(self, quiet: bool = False, wait_for: Literal["terminal", "running"] = "terminal") -> None:
|
|
186
195
|
"""
|
|
187
196
|
Wait for the run to complete, displaying a rich progress panel with status transitions,
|
|
188
197
|
time elapsed, and error details in case of failure.
|
|
189
198
|
"""
|
|
190
|
-
|
|
191
|
-
if self.done():
|
|
192
|
-
if not quiet:
|
|
193
|
-
console.print(f"[bold green]Run '{self.name}' is already completed.[/bold green]")
|
|
194
|
-
return
|
|
195
|
-
|
|
196
|
-
try:
|
|
197
|
-
with Progress(
|
|
198
|
-
SpinnerColumn(),
|
|
199
|
-
TextColumn("[progress.description]{task.description}"),
|
|
200
|
-
TimeElapsedColumn(),
|
|
201
|
-
console=console,
|
|
202
|
-
transient=True,
|
|
203
|
-
disable=quiet,
|
|
204
|
-
) as progress:
|
|
205
|
-
task_id = progress.add_task(f"Waiting for run '{self.name}'...", start=False)
|
|
206
|
-
|
|
207
|
-
async for ad in self.watch(cache_data_on_done=True):
|
|
208
|
-
if ad is None:
|
|
209
|
-
break
|
|
210
|
-
|
|
211
|
-
# Update progress description with the current phase
|
|
212
|
-
progress.update(
|
|
213
|
-
task_id,
|
|
214
|
-
description=f"Run: {self.name} in {ad.phase}, Runtime: {ad.runtime} secs "
|
|
215
|
-
f"Attempts[{ad.attempts}]",
|
|
216
|
-
)
|
|
217
|
-
progress.start_task(task_id)
|
|
218
|
-
|
|
219
|
-
# If the action is done, handle the final state
|
|
220
|
-
if ad.done():
|
|
221
|
-
progress.stop_task(task_id)
|
|
222
|
-
if ad.pb2.status.phase == run_definition_pb2.PHASE_SUCCEEDED:
|
|
223
|
-
console.print(f"[bold green]Run '{self.name}' completed successfully.[/bold green]")
|
|
224
|
-
else:
|
|
225
|
-
console.print(
|
|
226
|
-
f"[bold red]Run '{self.name}' exited unsuccessfully in state {ad.phase}"
|
|
227
|
-
f"with error: {ad.error_info}[/bold red]"
|
|
228
|
-
)
|
|
229
|
-
break
|
|
230
|
-
except asyncio.CancelledError:
|
|
231
|
-
# Handle cancellation gracefully
|
|
232
|
-
pass
|
|
233
|
-
except KeyboardInterrupt:
|
|
234
|
-
# Handle keyboard interrupt gracefully
|
|
235
|
-
console.print(f"\n[bold yellow]Run '{self.name}' was interrupted.[/bold yellow]")
|
|
199
|
+
return await self.action.wait(quiet=quiet, wait_for=wait_for)
|
|
236
200
|
|
|
237
201
|
async def watch(self, cache_data_on_done: bool = False) -> AsyncGenerator[ActionDetails, None]:
|
|
238
202
|
"""
|
|
239
203
|
Get the details of the run. This is a placeholder for getting the run details.
|
|
240
204
|
"""
|
|
241
|
-
|
|
242
|
-
if ad is None:
|
|
243
|
-
return
|
|
244
|
-
yield ad
|
|
205
|
+
return self.action.watch(cache_data_on_done=cache_data_on_done)
|
|
245
206
|
|
|
246
207
|
async def show_logs(
|
|
247
|
-
self,
|
|
208
|
+
self,
|
|
209
|
+
attempt: int | None = None,
|
|
210
|
+
max_lines: int = 100,
|
|
211
|
+
show_ts: bool = False,
|
|
212
|
+
raw: bool = False,
|
|
213
|
+
filter_system: bool = False,
|
|
248
214
|
):
|
|
249
|
-
await self.action.show_logs(attempt, max_lines, show_ts, raw)
|
|
215
|
+
await self.action.show_logs(attempt, max_lines, show_ts, raw, filter_system=filter_system)
|
|
250
216
|
|
|
251
217
|
async def details(self) -> RunDetails:
|
|
252
218
|
"""
|
|
@@ -257,7 +223,6 @@ class Run:
|
|
|
257
223
|
return self._details
|
|
258
224
|
|
|
259
225
|
@property
|
|
260
|
-
@requires_client
|
|
261
226
|
def url(self) -> str:
|
|
262
227
|
"""
|
|
263
228
|
Get the URL of the run.
|
|
@@ -271,16 +236,21 @@ class Run:
|
|
|
271
236
|
run_name=self.name,
|
|
272
237
|
)
|
|
273
238
|
|
|
274
|
-
@
|
|
275
|
-
async def
|
|
239
|
+
@syncify
|
|
240
|
+
async def abort(self):
|
|
276
241
|
"""
|
|
277
|
-
|
|
242
|
+
Aborts / Terminates the run.
|
|
278
243
|
"""
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
244
|
+
try:
|
|
245
|
+
await get_client().run_service.AbortRun(
|
|
246
|
+
run_service_pb2.AbortRunRequest(
|
|
247
|
+
run_id=self.pb2.action.id.run,
|
|
248
|
+
)
|
|
282
249
|
)
|
|
283
|
-
|
|
250
|
+
except grpc.aio.AioRpcError as e:
|
|
251
|
+
if e.code() == grpc.StatusCode.NOT_FOUND:
|
|
252
|
+
return
|
|
253
|
+
raise
|
|
284
254
|
|
|
285
255
|
def done(self) -> bool:
|
|
286
256
|
"""
|
|
@@ -327,13 +297,13 @@ class RunDetails:
|
|
|
327
297
|
"""
|
|
328
298
|
self.action_details = ActionDetails(self.pb2.action)
|
|
329
299
|
|
|
300
|
+
@syncify
|
|
330
301
|
@classmethod
|
|
331
|
-
@requires_client
|
|
332
|
-
@syncer.wrap
|
|
333
302
|
async def get_details(cls, run_id: run_definition_pb2.RunIdentifier) -> RunDetails:
|
|
334
303
|
"""
|
|
335
304
|
Get the details of the run. This is a placeholder for getting the run details.
|
|
336
305
|
"""
|
|
306
|
+
ensure_client()
|
|
337
307
|
resp = await get_client().run_service.GetRunDetails(
|
|
338
308
|
run_service_pb2.GetRunDetailsRequest(
|
|
339
309
|
run_id=run_id,
|
|
@@ -341,9 +311,8 @@ class RunDetails:
|
|
|
341
311
|
)
|
|
342
312
|
return cls(resp.details)
|
|
343
313
|
|
|
314
|
+
@syncify
|
|
344
315
|
@classmethod
|
|
345
|
-
@requires_client
|
|
346
|
-
@syncer.wrap
|
|
347
316
|
async def get(cls, name: str | None = None) -> RunDetails:
|
|
348
317
|
"""
|
|
349
318
|
Get a run by its ID or name. If both are provided, the ID will take precedence.
|
|
@@ -351,9 +320,9 @@ class RunDetails:
|
|
|
351
320
|
:param uri: The URI of the run.
|
|
352
321
|
:param name: The name of the run.
|
|
353
322
|
"""
|
|
323
|
+
ensure_client()
|
|
354
324
|
cfg = get_common_config()
|
|
355
325
|
return await RunDetails.get_details.aio(
|
|
356
|
-
cls,
|
|
357
326
|
run_id=run_definition_pb2.RunIdentifier(
|
|
358
327
|
org=cfg.org,
|
|
359
328
|
project=cfg.project,
|
|
@@ -426,15 +395,14 @@ class Action:
|
|
|
426
395
|
pb2: run_definition_pb2.Action
|
|
427
396
|
_details: ActionDetails | None = None
|
|
428
397
|
|
|
398
|
+
@syncify
|
|
429
399
|
@classmethod
|
|
430
|
-
@requires_client
|
|
431
|
-
@syncer.wrap
|
|
432
400
|
async def listall(
|
|
433
401
|
cls,
|
|
434
402
|
for_run_name: str,
|
|
435
403
|
filters: str | None = None,
|
|
436
404
|
sort_by: Tuple[str, Literal["asc", "desc"]] | None = None,
|
|
437
|
-
) -> Union[Iterator[Action],
|
|
405
|
+
) -> Union[Iterator[Action], AsyncIterator[Action]]:
|
|
438
406
|
"""
|
|
439
407
|
Get all actions for a given run.
|
|
440
408
|
|
|
@@ -443,6 +411,7 @@ class Action:
|
|
|
443
411
|
:param sort_by: The sorting criteria for the project list, in the format (field, order).
|
|
444
412
|
:return: An iterator of projects.
|
|
445
413
|
"""
|
|
414
|
+
ensure_client()
|
|
446
415
|
token = None
|
|
447
416
|
sort_by = sort_by or ("created_at", "asc")
|
|
448
417
|
sort_pb2 = list_pb2.Sort(
|
|
@@ -472,9 +441,8 @@ class Action:
|
|
|
472
441
|
if not token:
|
|
473
442
|
break
|
|
474
443
|
|
|
444
|
+
@syncify
|
|
475
445
|
@classmethod
|
|
476
|
-
@requires_client
|
|
477
|
-
@syncer.wrap
|
|
478
446
|
async def get(cls, uri: str | None = None, /, run_name: str | None = None, name: str | None = None) -> Action:
|
|
479
447
|
"""
|
|
480
448
|
Get a run by its ID or name. If both are provided, the ID will take precedence.
|
|
@@ -483,9 +451,9 @@ class Action:
|
|
|
483
451
|
:param run_name: The name of the action.
|
|
484
452
|
:param name: The name of the action.
|
|
485
453
|
"""
|
|
454
|
+
ensure_client()
|
|
486
455
|
cfg = get_common_config()
|
|
487
456
|
details: ActionDetails = await ActionDetails.get_details.aio(
|
|
488
|
-
cls,
|
|
489
457
|
run_definition_pb2.ActionIdentifier(
|
|
490
458
|
run=run_definition_pb2.RunIdentifier(
|
|
491
459
|
org=cfg.org,
|
|
@@ -512,6 +480,13 @@ class Action:
|
|
|
512
480
|
"""
|
|
513
481
|
return run_definition_pb2.Phase.Name(self.pb2.status.phase)
|
|
514
482
|
|
|
483
|
+
@property
|
|
484
|
+
def raw_phase(self) -> run_definition_pb2.Phase:
|
|
485
|
+
"""
|
|
486
|
+
Get the raw phase of the action.
|
|
487
|
+
"""
|
|
488
|
+
return self.pb2.status.phase
|
|
489
|
+
|
|
515
490
|
@property
|
|
516
491
|
def name(self) -> str:
|
|
517
492
|
"""
|
|
@@ -543,19 +518,27 @@ class Action:
|
|
|
543
518
|
return self.pb2.id
|
|
544
519
|
|
|
545
520
|
async def show_logs(
|
|
546
|
-
self,
|
|
521
|
+
self,
|
|
522
|
+
attempt: int | None = None,
|
|
523
|
+
max_lines: int = 30,
|
|
524
|
+
show_ts: bool = False,
|
|
525
|
+
raw: bool = False,
|
|
526
|
+
filter_system: bool = False,
|
|
547
527
|
):
|
|
548
528
|
details = await self.details()
|
|
529
|
+
if not details.is_running and not details.done():
|
|
530
|
+
# TODO we can short circuit here if the attempt is not the last one and it is done!
|
|
531
|
+
await self.wait(wait_for="logs-ready")
|
|
532
|
+
details = await self.details()
|
|
549
533
|
if not attempt:
|
|
550
534
|
attempt = details.attempts
|
|
551
|
-
if details.phase in [
|
|
552
|
-
run_definition_pb2.PHASE_QUEUED,
|
|
553
|
-
run_definition_pb2.PHASE_INITIALIZING,
|
|
554
|
-
run_definition_pb2.PHASE_WAITING_FOR_RESOURCES,
|
|
555
|
-
]:
|
|
556
|
-
raise RuntimeError("Action has not yet started, so logs are not available.")
|
|
557
535
|
return await Logs.create_viewer(
|
|
558
|
-
action_id=self.action_id,
|
|
536
|
+
action_id=self.action_id,
|
|
537
|
+
attempt=attempt,
|
|
538
|
+
max_lines=max_lines,
|
|
539
|
+
show_ts=show_ts,
|
|
540
|
+
raw=raw,
|
|
541
|
+
filter_system=filter_system,
|
|
559
542
|
)
|
|
560
543
|
|
|
561
544
|
async def details(self) -> ActionDetails:
|
|
@@ -563,27 +546,106 @@ class Action:
|
|
|
563
546
|
Get the details of the action. This is a placeholder for getting the action details.
|
|
564
547
|
"""
|
|
565
548
|
if not self._details:
|
|
566
|
-
self._details = await ActionDetails.get_details.aio(
|
|
549
|
+
self._details = await ActionDetails.get_details.aio(self.action_id)
|
|
567
550
|
return cast(ActionDetails, self._details)
|
|
568
551
|
|
|
569
|
-
async def
|
|
552
|
+
async def watch(
|
|
553
|
+
self, cache_data_on_done: bool = False, wait_for: WaitFor = "terminal"
|
|
554
|
+
) -> AsyncGenerator[ActionDetails, None]:
|
|
570
555
|
"""
|
|
571
556
|
Watch the action for updates. This is a placeholder for watching the action.
|
|
572
557
|
"""
|
|
573
558
|
ad = None
|
|
574
|
-
async for ad in ActionDetails.watch.aio(
|
|
559
|
+
async for ad in ActionDetails.watch.aio(self.action_id):
|
|
575
560
|
if ad is None:
|
|
576
561
|
return
|
|
577
562
|
self._details = ad
|
|
578
563
|
yield ad
|
|
564
|
+
if wait_for == "running" and ad.is_running:
|
|
565
|
+
break
|
|
566
|
+
elif wait_for == "logs-ready" and ad.logs_available():
|
|
567
|
+
break
|
|
568
|
+
if ad.done():
|
|
569
|
+
break
|
|
579
570
|
if cache_data_on_done and ad and ad.done():
|
|
580
571
|
await cast(ActionDetails, self._details).outputs()
|
|
581
572
|
|
|
573
|
+
async def wait(self, quiet: bool = False, wait_for: WaitFor = "terminal") -> None:
|
|
574
|
+
"""
|
|
575
|
+
Wait for the run to complete, displaying a rich progress panel with status transitions,
|
|
576
|
+
time elapsed, and error details in case of failure.
|
|
577
|
+
"""
|
|
578
|
+
console = Console()
|
|
579
|
+
if self.done():
|
|
580
|
+
if not quiet:
|
|
581
|
+
if self.pb2.status.phase == run_definition_pb2.PHASE_SUCCEEDED:
|
|
582
|
+
console.print(
|
|
583
|
+
f"[bold green]Action '{self.name}' in Run '{self.run_name}'"
|
|
584
|
+
f" completed successfully.[/bold green]"
|
|
585
|
+
)
|
|
586
|
+
else:
|
|
587
|
+
details = await self.details()
|
|
588
|
+
console.print(
|
|
589
|
+
f"[bold red]Action '{self.name}' in Run '{self.run_name}'"
|
|
590
|
+
f" exited unsuccessfully in state {self.phase} with error: {details.error_info}[/bold red]"
|
|
591
|
+
)
|
|
592
|
+
return
|
|
593
|
+
|
|
594
|
+
try:
|
|
595
|
+
with Progress(
|
|
596
|
+
SpinnerColumn(),
|
|
597
|
+
TextColumn("[progress.description]{task.description}"),
|
|
598
|
+
TimeElapsedColumn(),
|
|
599
|
+
console=console,
|
|
600
|
+
transient=True,
|
|
601
|
+
disable=quiet,
|
|
602
|
+
) as progress:
|
|
603
|
+
task_id = progress.add_task(f"Waiting for run '{self.name}'...", start=False)
|
|
604
|
+
progress.start_task(task_id)
|
|
605
|
+
|
|
606
|
+
async for ad in self.watch(cache_data_on_done=True, wait_for=wait_for):
|
|
607
|
+
if ad is None:
|
|
608
|
+
progress.stop_task(task_id)
|
|
609
|
+
break
|
|
610
|
+
|
|
611
|
+
if ad.is_running and wait_for == "running":
|
|
612
|
+
progress.start_task(task_id)
|
|
613
|
+
break
|
|
614
|
+
|
|
615
|
+
if ad.logs_available() and wait_for == "logs-ready":
|
|
616
|
+
progress.start_task(task_id)
|
|
617
|
+
break
|
|
618
|
+
|
|
619
|
+
# Update progress description with the current phase
|
|
620
|
+
progress.update(
|
|
621
|
+
task_id,
|
|
622
|
+
description=f"Run: {self.name} in {ad.phase}, Runtime: {ad.runtime} secs "
|
|
623
|
+
f"Attempts[{ad.attempts}]",
|
|
624
|
+
)
|
|
625
|
+
|
|
626
|
+
# If the action is done, handle the final state
|
|
627
|
+
if ad.done():
|
|
628
|
+
progress.stop_task(task_id)
|
|
629
|
+
if ad.pb2.status.phase == run_definition_pb2.PHASE_SUCCEEDED:
|
|
630
|
+
console.print(f"[bold green]Run '{self.name}' completed successfully.[/bold green]")
|
|
631
|
+
else:
|
|
632
|
+
console.print(
|
|
633
|
+
f"[bold red]Run '{self.name}' exited unsuccessfully in state {ad.phase}"
|
|
634
|
+
f"with error: {ad.error_info}[/bold red]"
|
|
635
|
+
)
|
|
636
|
+
break
|
|
637
|
+
except asyncio.CancelledError:
|
|
638
|
+
# Handle cancellation gracefully
|
|
639
|
+
pass
|
|
640
|
+
except KeyboardInterrupt:
|
|
641
|
+
# Handle keyboard interrupt gracefully
|
|
642
|
+
pass
|
|
643
|
+
|
|
582
644
|
def done(self) -> bool:
|
|
583
645
|
"""
|
|
584
646
|
Check if the action is done.
|
|
585
647
|
"""
|
|
586
|
-
return _action_done_check(self.
|
|
648
|
+
return _action_done_check(self.raw_phase)
|
|
587
649
|
|
|
588
650
|
async def sync(self) -> Action:
|
|
589
651
|
"""
|
|
@@ -616,13 +678,13 @@ class ActionDetails:
|
|
|
616
678
|
_inputs: ActionInputs | None = None
|
|
617
679
|
_outputs: ActionOutputs | None = None
|
|
618
680
|
|
|
681
|
+
@syncify
|
|
619
682
|
@classmethod
|
|
620
|
-
@requires_client
|
|
621
|
-
@syncer.wrap
|
|
622
683
|
async def get_details(cls, action_id: run_definition_pb2.ActionIdentifier) -> ActionDetails:
|
|
623
684
|
"""
|
|
624
685
|
Get the details of the action. This is a placeholder for getting the action details.
|
|
625
686
|
"""
|
|
687
|
+
ensure_client()
|
|
626
688
|
resp = await get_client().run_service.GetActionDetails(
|
|
627
689
|
run_service_pb2.GetActionDetailsRequest(
|
|
628
690
|
action_id=action_id,
|
|
@@ -630,9 +692,8 @@ class ActionDetails:
|
|
|
630
692
|
)
|
|
631
693
|
return ActionDetails(resp.details)
|
|
632
694
|
|
|
695
|
+
@syncify
|
|
633
696
|
@classmethod
|
|
634
|
-
@requires_client
|
|
635
|
-
@syncer.wrap
|
|
636
697
|
async def get(
|
|
637
698
|
cls, uri: str | None = None, /, run_name: str | None = None, name: str | None = None
|
|
638
699
|
) -> ActionDetails:
|
|
@@ -643,11 +704,11 @@ class ActionDetails:
|
|
|
643
704
|
:param name: The name of the action.
|
|
644
705
|
:param run_name: The name of the run.
|
|
645
706
|
"""
|
|
707
|
+
ensure_client()
|
|
646
708
|
if not uri:
|
|
647
709
|
assert name is not None and run_name is not None, "Either uri or name and run_name must be provided"
|
|
648
710
|
cfg = get_common_config()
|
|
649
711
|
return await cls.get_details.aio(
|
|
650
|
-
cls,
|
|
651
712
|
run_definition_pb2.ActionIdentifier(
|
|
652
713
|
run=run_definition_pb2.RunIdentifier(
|
|
653
714
|
org=cfg.org,
|
|
@@ -659,13 +720,13 @@ class ActionDetails:
|
|
|
659
720
|
),
|
|
660
721
|
)
|
|
661
722
|
|
|
723
|
+
@syncify
|
|
662
724
|
@classmethod
|
|
663
|
-
|
|
664
|
-
@syncer.wrap
|
|
665
|
-
async def watch(cls, action_id: run_definition_pb2.ActionIdentifier) -> AsyncGenerator[ActionDetails, None]:
|
|
725
|
+
async def watch(cls, action_id: run_definition_pb2.ActionIdentifier) -> AsyncIterator[ActionDetails]:
|
|
666
726
|
"""
|
|
667
727
|
Watch the action for updates. This is a placeholder for watching the action.
|
|
668
728
|
"""
|
|
729
|
+
ensure_client()
|
|
669
730
|
if not action_id:
|
|
670
731
|
raise ValueError("Action ID is required")
|
|
671
732
|
|
|
@@ -697,7 +758,7 @@ class ActionDetails:
|
|
|
697
758
|
break
|
|
698
759
|
|
|
699
760
|
if cache_data_on_done and self.done():
|
|
700
|
-
await self._cache_data.aio(
|
|
761
|
+
await self._cache_data.aio()
|
|
701
762
|
|
|
702
763
|
@property
|
|
703
764
|
def phase(self) -> str:
|
|
@@ -706,6 +767,20 @@ class ActionDetails:
|
|
|
706
767
|
"""
|
|
707
768
|
return run_definition_pb2.Phase.Name(self.status.phase)
|
|
708
769
|
|
|
770
|
+
@property
|
|
771
|
+
def raw_phase(self) -> run_definition_pb2.Phase:
|
|
772
|
+
"""
|
|
773
|
+
Get the raw phase of the action.
|
|
774
|
+
"""
|
|
775
|
+
return self.status.phase
|
|
776
|
+
|
|
777
|
+
@property
|
|
778
|
+
def is_running(self) -> bool:
|
|
779
|
+
"""
|
|
780
|
+
Check if the action is currently running.
|
|
781
|
+
"""
|
|
782
|
+
return self.status.phase == run_definition_pb2.PHASE_RUNNING
|
|
783
|
+
|
|
709
784
|
@property
|
|
710
785
|
def name(self) -> str:
|
|
711
786
|
"""
|
|
@@ -774,7 +849,19 @@ class ActionDetails:
|
|
|
774
849
|
"""
|
|
775
850
|
return self.pb2.status.attempts
|
|
776
851
|
|
|
777
|
-
|
|
852
|
+
def logs_available(self, attempt: int | None = None) -> bool:
|
|
853
|
+
"""
|
|
854
|
+
Check if logs are available for the action, optionally for a specific attempt.
|
|
855
|
+
If attempt is None, it checks for the latest attempt.
|
|
856
|
+
"""
|
|
857
|
+
if attempt is None:
|
|
858
|
+
attempt = self.pb2.status.attempts
|
|
859
|
+
attempts = self.pb2.attempts
|
|
860
|
+
if attempts and len(attempts) >= attempt:
|
|
861
|
+
return attempts[attempt - 1].logs_available
|
|
862
|
+
return False
|
|
863
|
+
|
|
864
|
+
@syncify
|
|
778
865
|
async def _cache_data(self) -> bool:
|
|
779
866
|
"""
|
|
780
867
|
Cache the inputs and outputs of the action.
|
|
@@ -798,7 +885,7 @@ class ActionDetails:
|
|
|
798
885
|
Placeholder for inputs. This can be extended to handle inputs from the run context.
|
|
799
886
|
"""
|
|
800
887
|
if not self._inputs:
|
|
801
|
-
await self._cache_data.aio(
|
|
888
|
+
await self._cache_data.aio()
|
|
802
889
|
return cast(ActionInputs, self._inputs)
|
|
803
890
|
|
|
804
891
|
async def outputs(self) -> ActionOutputs:
|
|
@@ -806,7 +893,7 @@ class ActionDetails:
|
|
|
806
893
|
Placeholder for outputs. This can be extended to handle outputs from the run context.
|
|
807
894
|
"""
|
|
808
895
|
if not self._outputs:
|
|
809
|
-
if not await self._cache_data.aio(
|
|
896
|
+
if not await self._cache_data.aio():
|
|
810
897
|
raise RuntimeError(
|
|
811
898
|
"Action is not in a terminal state, outputs are not available. "
|
|
812
899
|
"Please wait for the action to complete."
|
|
@@ -818,7 +905,7 @@ class ActionDetails:
|
|
|
818
905
|
Check if the action is in a terminal state (completed or failed). This is a placeholder for checking the
|
|
819
906
|
action state.
|
|
820
907
|
"""
|
|
821
|
-
return _action_done_check(self.
|
|
908
|
+
return _action_done_check(self.raw_phase)
|
|
822
909
|
|
|
823
910
|
def __rich_repr__(self) -> rich.repr.Result:
|
|
824
911
|
"""
|
flyte/remote/_secret.py
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
|
-
from typing import
|
|
4
|
+
from typing import AsyncIterator, Literal, Union
|
|
5
5
|
|
|
6
6
|
import rich.repr
|
|
7
7
|
|
|
8
|
-
from flyte.
|
|
9
|
-
from flyte._initialize import get_client, get_common_config, requires_client
|
|
8
|
+
from flyte._initialize import ensure_client, get_client, get_common_config
|
|
10
9
|
from flyte._protos.secret import definition_pb2, payload_pb2
|
|
10
|
+
from flyte.syncify import syncify
|
|
11
11
|
|
|
12
12
|
SecretTypes = Literal["regular", "image_pull"]
|
|
13
13
|
|
|
@@ -16,10 +16,10 @@ SecretTypes = Literal["regular", "image_pull"]
|
|
|
16
16
|
class Secret:
|
|
17
17
|
pb2: definition_pb2.Secret
|
|
18
18
|
|
|
19
|
+
@syncify
|
|
19
20
|
@classmethod
|
|
20
|
-
@requires_client
|
|
21
|
-
@syncer.wrap
|
|
22
21
|
async def create(cls, name: str, value: Union[str, bytes], type: SecretTypes = "regular"):
|
|
22
|
+
ensure_client()
|
|
23
23
|
cfg = get_common_config()
|
|
24
24
|
secret_type = (
|
|
25
25
|
definition_pb2.SecretType.SECRET_TYPE_GENERIC
|
|
@@ -49,10 +49,10 @@ class Secret:
|
|
|
49
49
|
),
|
|
50
50
|
)
|
|
51
51
|
|
|
52
|
+
@syncify
|
|
52
53
|
@classmethod
|
|
53
|
-
@requires_client
|
|
54
|
-
@syncer.wrap
|
|
55
54
|
async def get(cls, name: str) -> Secret:
|
|
55
|
+
ensure_client()
|
|
56
56
|
cfg = get_common_config()
|
|
57
57
|
resp = await get_client().secrets_service.GetSecret(
|
|
58
58
|
request=payload_pb2.GetSecretRequest(
|
|
@@ -66,10 +66,10 @@ class Secret:
|
|
|
66
66
|
)
|
|
67
67
|
return Secret(pb2=resp.secret)
|
|
68
68
|
|
|
69
|
+
@syncify
|
|
69
70
|
@classmethod
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
async def listall(cls, limit: int = 100) -> Union[Iterator[Secret], AsyncGenerator[Secret, None]]:
|
|
71
|
+
async def listall(cls, limit: int = 100) -> AsyncIterator[Secret]:
|
|
72
|
+
ensure_client()
|
|
73
73
|
cfg = get_common_config()
|
|
74
74
|
token = None
|
|
75
75
|
while True:
|
|
@@ -88,10 +88,10 @@ class Secret:
|
|
|
88
88
|
if not token:
|
|
89
89
|
break
|
|
90
90
|
|
|
91
|
+
@syncify
|
|
91
92
|
@classmethod
|
|
92
|
-
@requires_client
|
|
93
|
-
@syncer.wrap
|
|
94
93
|
async def delete(cls, name):
|
|
94
|
+
ensure_client()
|
|
95
95
|
cfg = get_common_config()
|
|
96
96
|
await get_client().secrets_service.DeleteSecret( # type: ignore
|
|
97
97
|
request=payload_pb2.DeleteSecretRequest(
|
flyte/remote/_task.py
CHANGED
|
@@ -9,11 +9,11 @@ import rich.repr
|
|
|
9
9
|
|
|
10
10
|
import flyte
|
|
11
11
|
import flyte.errors
|
|
12
|
-
from flyte._api_commons import syncer
|
|
13
12
|
from flyte._context import internal_ctx
|
|
14
13
|
from flyte._initialize import get_client, get_common_config
|
|
15
14
|
from flyte._protos.workflow import task_definition_pb2, task_service_pb2
|
|
16
15
|
from flyte.models import NativeInterface
|
|
16
|
+
from flyte.syncify import syncify
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
class LazyEntity:
|
|
@@ -32,7 +32,7 @@ class LazyEntity:
|
|
|
32
32
|
def name(self) -> str:
|
|
33
33
|
return self._name
|
|
34
34
|
|
|
35
|
-
@
|
|
35
|
+
@syncify
|
|
36
36
|
async def fetch(self) -> Task:
|
|
37
37
|
"""
|
|
38
38
|
Forwards all other attributes to task, causing the task to be fetched!
|
|
@@ -48,7 +48,7 @@ class LazyEntity:
|
|
|
48
48
|
"""
|
|
49
49
|
Forwards the call to the underlying task. The entity will be fetched if not already present
|
|
50
50
|
"""
|
|
51
|
-
tk = await self.fetch.aio(
|
|
51
|
+
tk = await self.fetch.aio()
|
|
52
52
|
return await tk(*args, **kwargs)
|
|
53
53
|
|
|
54
54
|
def __repr__(self) -> str:
|