flyte 0.0.1b3__py3-none-any.whl → 0.2.0a0__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/__init__.py +20 -4
- flyte/_bin/runtime.py +33 -7
- flyte/_build.py +3 -2
- flyte/_cache/cache.py +1 -2
- flyte/_code_bundle/_packaging.py +1 -1
- flyte/_code_bundle/_utils.py +0 -16
- flyte/_code_bundle/bundle.py +43 -12
- flyte/_context.py +8 -2
- flyte/_deploy.py +56 -15
- flyte/_environment.py +45 -4
- flyte/_excepthook.py +37 -0
- flyte/_group.py +2 -1
- flyte/_image.py +8 -4
- flyte/_initialize.py +112 -254
- flyte/_interface.py +3 -3
- flyte/_internal/controllers/__init__.py +19 -6
- flyte/_internal/controllers/_local_controller.py +83 -8
- flyte/_internal/controllers/_trace.py +2 -1
- flyte/_internal/controllers/remote/__init__.py +27 -7
- flyte/_internal/controllers/remote/_action.py +7 -2
- flyte/_internal/controllers/remote/_client.py +5 -1
- flyte/_internal/controllers/remote/_controller.py +159 -26
- flyte/_internal/controllers/remote/_core.py +13 -5
- flyte/_internal/controllers/remote/_informer.py +4 -4
- flyte/_internal/controllers/remote/_service_protocol.py +6 -6
- flyte/_internal/imagebuild/docker_builder.py +12 -1
- flyte/_internal/imagebuild/image_builder.py +16 -11
- flyte/_internal/runtime/convert.py +164 -21
- flyte/_internal/runtime/entrypoints.py +1 -1
- flyte/_internal/runtime/io.py +3 -3
- flyte/_internal/runtime/task_serde.py +140 -20
- flyte/_internal/runtime/taskrunner.py +4 -3
- flyte/_internal/runtime/types_serde.py +1 -1
- flyte/_logging.py +12 -1
- flyte/_map.py +215 -0
- flyte/_pod.py +19 -0
- flyte/_protos/common/list_pb2.py +3 -3
- flyte/_protos/common/list_pb2.pyi +2 -0
- 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/environment_pb2.py +29 -0
- flyte/_protos/workflow/environment_pb2.pyi +12 -0
- flyte/_protos/workflow/queue_service_pb2.py +40 -41
- flyte/_protos/workflow/queue_service_pb2.pyi +35 -30
- flyte/_protos/workflow/queue_service_pb2_grpc.py +15 -15
- flyte/_protos/workflow/run_definition_pb2.py +61 -61
- flyte/_protos/workflow/run_definition_pb2.pyi +8 -4
- flyte/_protos/workflow/run_service_pb2.py +20 -24
- flyte/_protos/workflow/run_service_pb2.pyi +2 -6
- flyte/_protos/workflow/state_service_pb2.py +36 -28
- flyte/_protos/workflow/state_service_pb2.pyi +19 -15
- flyte/_protos/workflow/state_service_pb2_grpc.py +28 -28
- flyte/_protos/workflow/task_definition_pb2.py +29 -22
- flyte/_protos/workflow/task_definition_pb2.pyi +21 -5
- flyte/_protos/workflow/task_service_pb2.py +27 -11
- flyte/_protos/workflow/task_service_pb2.pyi +29 -1
- flyte/_protos/workflow/task_service_pb2_grpc.py +34 -0
- flyte/_run.py +166 -95
- flyte/_task.py +110 -28
- flyte/_task_environment.py +55 -72
- flyte/_trace.py +6 -14
- flyte/_utils/__init__.py +6 -0
- flyte/_utils/async_cache.py +139 -0
- flyte/_utils/coro_management.py +0 -2
- flyte/_utils/helpers.py +45 -19
- flyte/_utils/org_discovery.py +57 -0
- flyte/_version.py +2 -2
- flyte/cli/__init__.py +3 -0
- flyte/cli/_abort.py +28 -0
- flyte/{_cli → cli}/_common.py +73 -23
- flyte/cli/_create.py +145 -0
- flyte/{_cli → cli}/_delete.py +4 -4
- flyte/{_cli → cli}/_deploy.py +26 -14
- flyte/cli/_gen.py +163 -0
- flyte/{_cli → cli}/_get.py +98 -23
- {union/_cli → flyte/cli}/_params.py +106 -147
- flyte/{_cli → cli}/_run.py +99 -20
- flyte/cli/main.py +166 -0
- flyte/config/__init__.py +3 -0
- flyte/config/_config.py +216 -0
- flyte/config/_internal.py +64 -0
- flyte/config/_reader.py +207 -0
- flyte/errors.py +29 -0
- flyte/extras/_container.py +33 -43
- flyte/io/__init__.py +17 -1
- flyte/io/_dir.py +2 -2
- flyte/io/_file.py +3 -4
- flyte/io/{structured_dataset → _structured_dataset}/basic_dfs.py +1 -1
- flyte/io/{structured_dataset → _structured_dataset}/structured_dataset.py +1 -1
- flyte/{_datastructures.py → models.py} +56 -7
- flyte/remote/__init__.py +2 -1
- flyte/remote/_client/_protocols.py +2 -0
- flyte/remote/_client/auth/_auth_utils.py +14 -0
- flyte/remote/_client/auth/_channel.py +34 -3
- flyte/remote/_client/auth/_token_client.py +3 -3
- flyte/remote/_client/controlplane.py +13 -13
- flyte/remote/_console.py +1 -1
- flyte/remote/_data.py +10 -6
- flyte/remote/_logs.py +89 -29
- flyte/remote/_project.py +8 -9
- flyte/remote/_run.py +228 -131
- flyte/remote/_secret.py +12 -12
- flyte/remote/_task.py +179 -15
- flyte/report/_report.py +4 -4
- flyte/storage/__init__.py +5 -0
- flyte/storage/_config.py +233 -0
- flyte/storage/_storage.py +23 -3
- flyte/syncify/__init__.py +56 -0
- flyte/syncify/_api.py +371 -0
- flyte/types/__init__.py +23 -0
- flyte/types/_interface.py +22 -7
- flyte/{io/pickle/transformer.py → types/_pickle.py} +2 -1
- flyte/types/_type_engine.py +95 -18
- flyte-0.2.0a0.dist-info/METADATA +249 -0
- flyte-0.2.0a0.dist-info/RECORD +218 -0
- {flyte-0.0.1b3.dist-info → flyte-0.2.0a0.dist-info}/entry_points.txt +1 -1
- flyte/_api_commons.py +0 -3
- flyte/_cli/__init__.py +0 -0
- flyte/_cli/_create.py +0 -42
- flyte/_cli/main.py +0 -72
- flyte/_internal/controllers/pbhash.py +0 -39
- flyte/io/_dataframe.py +0 -0
- flyte/io/pickle/__init__.py +0 -0
- flyte-0.0.1b3.dist-info/METADATA +0 -179
- flyte-0.0.1b3.dist-info/RECORD +0 -390
- union/__init__.py +0 -54
- union/_api_commons.py +0 -3
- union/_bin/__init__.py +0 -0
- union/_bin/runtime.py +0 -113
- union/_build.py +0 -25
- union/_cache/__init__.py +0 -12
- union/_cache/cache.py +0 -141
- union/_cache/defaults.py +0 -9
- union/_cache/policy_function_body.py +0 -42
- union/_cli/__init__.py +0 -0
- union/_cli/_common.py +0 -263
- union/_cli/_create.py +0 -40
- union/_cli/_delete.py +0 -23
- union/_cli/_deploy.py +0 -120
- union/_cli/_get.py +0 -162
- union/_cli/_run.py +0 -150
- union/_cli/main.py +0 -72
- union/_code_bundle/__init__.py +0 -8
- union/_code_bundle/_ignore.py +0 -113
- union/_code_bundle/_packaging.py +0 -187
- union/_code_bundle/_utils.py +0 -342
- union/_code_bundle/bundle.py +0 -176
- union/_context.py +0 -146
- union/_datastructures.py +0 -295
- union/_deploy.py +0 -185
- union/_doc.py +0 -29
- union/_docstring.py +0 -26
- union/_environment.py +0 -43
- union/_group.py +0 -31
- union/_hash.py +0 -23
- union/_image.py +0 -760
- union/_initialize.py +0 -585
- union/_interface.py +0 -84
- union/_internal/__init__.py +0 -3
- union/_internal/controllers/__init__.py +0 -77
- union/_internal/controllers/_local_controller.py +0 -77
- union/_internal/controllers/pbhash.py +0 -39
- union/_internal/controllers/remote/__init__.py +0 -40
- union/_internal/controllers/remote/_action.py +0 -131
- union/_internal/controllers/remote/_client.py +0 -43
- union/_internal/controllers/remote/_controller.py +0 -169
- union/_internal/controllers/remote/_core.py +0 -341
- union/_internal/controllers/remote/_informer.py +0 -260
- union/_internal/controllers/remote/_service_protocol.py +0 -44
- union/_internal/imagebuild/__init__.py +0 -11
- union/_internal/imagebuild/docker_builder.py +0 -416
- union/_internal/imagebuild/image_builder.py +0 -243
- union/_internal/imagebuild/remote_builder.py +0 -0
- union/_internal/resolvers/__init__.py +0 -0
- union/_internal/resolvers/_task_module.py +0 -31
- union/_internal/resolvers/common.py +0 -24
- union/_internal/resolvers/default.py +0 -27
- union/_internal/runtime/__init__.py +0 -0
- union/_internal/runtime/convert.py +0 -163
- union/_internal/runtime/entrypoints.py +0 -121
- union/_internal/runtime/io.py +0 -136
- union/_internal/runtime/resources_serde.py +0 -134
- union/_internal/runtime/task_serde.py +0 -202
- union/_internal/runtime/taskrunner.py +0 -179
- union/_internal/runtime/types_serde.py +0 -53
- union/_logging.py +0 -124
- union/_protos/__init__.py +0 -0
- union/_protos/common/authorization_pb2.py +0 -66
- union/_protos/common/authorization_pb2.pyi +0 -106
- union/_protos/common/identifier_pb2.py +0 -71
- union/_protos/common/identifier_pb2.pyi +0 -82
- union/_protos/common/identity_pb2.py +0 -48
- union/_protos/common/identity_pb2.pyi +0 -72
- union/_protos/common/identity_pb2_grpc.py +0 -4
- union/_protos/common/list_pb2.py +0 -36
- union/_protos/common/list_pb2.pyi +0 -69
- union/_protos/common/list_pb2_grpc.py +0 -4
- union/_protos/common/policy_pb2.py +0 -37
- union/_protos/common/policy_pb2.pyi +0 -27
- union/_protos/common/policy_pb2_grpc.py +0 -4
- union/_protos/common/role_pb2.py +0 -37
- union/_protos/common/role_pb2.pyi +0 -51
- union/_protos/common/role_pb2_grpc.py +0 -4
- union/_protos/common/runtime_version_pb2.py +0 -28
- union/_protos/common/runtime_version_pb2.pyi +0 -24
- union/_protos/common/runtime_version_pb2_grpc.py +0 -4
- union/_protos/logs/dataplane/payload_pb2.py +0 -96
- union/_protos/logs/dataplane/payload_pb2.pyi +0 -168
- union/_protos/logs/dataplane/payload_pb2_grpc.py +0 -4
- union/_protos/secret/definition_pb2.py +0 -49
- union/_protos/secret/definition_pb2.pyi +0 -93
- union/_protos/secret/definition_pb2_grpc.py +0 -4
- union/_protos/secret/payload_pb2.py +0 -62
- union/_protos/secret/payload_pb2.pyi +0 -94
- union/_protos/secret/payload_pb2_grpc.py +0 -4
- union/_protos/secret/secret_pb2.py +0 -38
- union/_protos/secret/secret_pb2.pyi +0 -6
- union/_protos/secret/secret_pb2_grpc.py +0 -198
- union/_protos/validate/validate/validate_pb2.py +0 -76
- union/_protos/workflow/node_execution_service_pb2.py +0 -26
- union/_protos/workflow/node_execution_service_pb2.pyi +0 -4
- union/_protos/workflow/node_execution_service_pb2_grpc.py +0 -32
- union/_protos/workflow/queue_service_pb2.py +0 -75
- union/_protos/workflow/queue_service_pb2.pyi +0 -103
- union/_protos/workflow/queue_service_pb2_grpc.py +0 -172
- union/_protos/workflow/run_definition_pb2.py +0 -100
- union/_protos/workflow/run_definition_pb2.pyi +0 -256
- union/_protos/workflow/run_definition_pb2_grpc.py +0 -4
- union/_protos/workflow/run_logs_service_pb2.py +0 -41
- union/_protos/workflow/run_logs_service_pb2.pyi +0 -28
- union/_protos/workflow/run_logs_service_pb2_grpc.py +0 -69
- union/_protos/workflow/run_service_pb2.py +0 -133
- union/_protos/workflow/run_service_pb2.pyi +0 -173
- union/_protos/workflow/run_service_pb2_grpc.py +0 -412
- union/_protos/workflow/state_service_pb2.py +0 -58
- union/_protos/workflow/state_service_pb2.pyi +0 -69
- union/_protos/workflow/state_service_pb2_grpc.py +0 -138
- union/_protos/workflow/task_definition_pb2.py +0 -72
- union/_protos/workflow/task_definition_pb2.pyi +0 -65
- union/_protos/workflow/task_definition_pb2_grpc.py +0 -4
- union/_protos/workflow/task_service_pb2.py +0 -44
- union/_protos/workflow/task_service_pb2.pyi +0 -31
- union/_protos/workflow/task_service_pb2_grpc.py +0 -104
- union/_resources.py +0 -226
- union/_retry.py +0 -32
- union/_reusable_environment.py +0 -25
- union/_run.py +0 -374
- union/_secret.py +0 -61
- union/_task.py +0 -354
- union/_task_environment.py +0 -186
- union/_timeout.py +0 -47
- union/_tools.py +0 -27
- union/_utils/__init__.py +0 -11
- union/_utils/asyn.py +0 -119
- union/_utils/file_handling.py +0 -71
- union/_utils/helpers.py +0 -46
- union/_utils/lazy_module.py +0 -54
- union/_utils/uv_script_parser.py +0 -49
- union/_version.py +0 -21
- union/connectors/__init__.py +0 -0
- union/errors.py +0 -128
- union/extras/__init__.py +0 -5
- union/extras/_container.py +0 -263
- union/io/__init__.py +0 -11
- union/io/_dataframe.py +0 -0
- union/io/_dir.py +0 -425
- union/io/_file.py +0 -418
- union/io/pickle/__init__.py +0 -0
- union/io/pickle/transformer.py +0 -117
- union/io/structured_dataset/__init__.py +0 -122
- union/io/structured_dataset/basic_dfs.py +0 -219
- union/io/structured_dataset/structured_dataset.py +0 -1057
- union/py.typed +0 -0
- union/remote/__init__.py +0 -23
- union/remote/_client/__init__.py +0 -0
- union/remote/_client/_protocols.py +0 -129
- union/remote/_client/auth/__init__.py +0 -12
- union/remote/_client/auth/_authenticators/__init__.py +0 -0
- union/remote/_client/auth/_authenticators/base.py +0 -391
- union/remote/_client/auth/_authenticators/client_credentials.py +0 -73
- union/remote/_client/auth/_authenticators/device_code.py +0 -120
- union/remote/_client/auth/_authenticators/external_command.py +0 -77
- union/remote/_client/auth/_authenticators/factory.py +0 -200
- union/remote/_client/auth/_authenticators/pkce.py +0 -515
- union/remote/_client/auth/_channel.py +0 -184
- union/remote/_client/auth/_client_config.py +0 -83
- union/remote/_client/auth/_default_html.py +0 -32
- union/remote/_client/auth/_grpc_utils/__init__.py +0 -0
- union/remote/_client/auth/_grpc_utils/auth_interceptor.py +0 -204
- union/remote/_client/auth/_grpc_utils/default_metadata_interceptor.py +0 -144
- union/remote/_client/auth/_keyring.py +0 -154
- union/remote/_client/auth/_token_client.py +0 -258
- union/remote/_client/auth/errors.py +0 -16
- union/remote/_client/controlplane.py +0 -86
- union/remote/_data.py +0 -149
- union/remote/_logs.py +0 -74
- union/remote/_project.py +0 -86
- union/remote/_run.py +0 -820
- union/remote/_secret.py +0 -132
- union/remote/_task.py +0 -193
- union/report/__init__.py +0 -3
- union/report/_report.py +0 -178
- union/report/_template.html +0 -124
- union/storage/__init__.py +0 -24
- union/storage/_remote_fs.py +0 -34
- union/storage/_storage.py +0 -247
- union/storage/_utils.py +0 -5
- union/types/__init__.py +0 -11
- union/types/_renderer.py +0 -162
- union/types/_string_literals.py +0 -120
- union/types/_type_engine.py +0 -2131
- union/types/_utils.py +0 -80
- /union/_protos/common/authorization_pb2_grpc.py → /flyte/_protos/workflow/common_pb2_grpc.py +0 -0
- /union/_protos/common/identifier_pb2_grpc.py → /flyte/_protos/workflow/environment_pb2_grpc.py +0 -0
- /flyte/io/{structured_dataset → _structured_dataset}/__init__.py +0 -0
- {flyte-0.0.1b3.dist-info → flyte-0.2.0a0.dist-info}/WHEEL +0 -0
- {flyte-0.0.1b3.dist-info → flyte-0.2.0a0.dist-info}/top_level.txt +0 -0
flyte/remote/_run.py
CHANGED
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import asyncio
|
|
4
4
|
from dataclasses import dataclass, field
|
|
5
5
|
from datetime import datetime, timedelta, timezone
|
|
6
|
-
from typing import AsyncGenerator, AsyncIterator, Iterator, Literal, Tuple, Union, cast
|
|
6
|
+
from typing import AsyncGenerator, AsyncIterator, Iterator, List, Literal, Tuple, Union, cast
|
|
7
7
|
|
|
8
8
|
import grpc
|
|
9
9
|
import rich.repr
|
|
@@ -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
|
"""
|
|
@@ -41,36 +43,48 @@ def _action_time_phase(action: run_definition_pb2.Action | run_definition_pb2.Ac
|
|
|
41
43
|
yield "run_time", f"{(datetime.now(timezone.utc) - start_time).seconds} secs"
|
|
42
44
|
yield "phase", run_definition_pb2.Phase.Name(action.status.phase)
|
|
43
45
|
if isinstance(action, run_definition_pb2.ActionDetails):
|
|
44
|
-
yield
|
|
46
|
+
yield (
|
|
47
|
+
"error",
|
|
48
|
+
f"{action.error_info.kind}: {action.error_info.message}" if action.HasField("error_info") else "NA",
|
|
49
|
+
)
|
|
45
50
|
|
|
46
51
|
|
|
47
|
-
def _action_rich_repr(action: run_definition_pb2.Action
|
|
52
|
+
def _action_rich_repr(action: run_definition_pb2.Action) -> rich.repr.Result:
|
|
48
53
|
"""
|
|
49
54
|
Rich representation of the action.
|
|
50
55
|
"""
|
|
51
|
-
yield "run
|
|
56
|
+
yield "run", action.id.run.name
|
|
57
|
+
if action.metadata.HasField("task"):
|
|
58
|
+
yield "task", action.metadata.task.id.name
|
|
59
|
+
yield "type", "task"
|
|
52
60
|
yield "name", action.id.name
|
|
53
61
|
yield from _action_time_phase(action)
|
|
54
|
-
yield "
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
62
|
+
yield "group", action.metadata.group
|
|
63
|
+
yield "parent", action.metadata.parent
|
|
64
|
+
yield "attempts", action.status.attempts
|
|
65
|
+
|
|
58
66
|
|
|
67
|
+
def _attempt_rich_repr(action: List[run_definition_pb2.ActionAttempt]) -> rich.repr.Result:
|
|
68
|
+
for attempt in action:
|
|
69
|
+
yield "attempt", attempt.attempt
|
|
70
|
+
yield "phase", run_definition_pb2.Phase.Name(attempt.phase)
|
|
71
|
+
yield "logs_available", attempt.logs_available
|
|
59
72
|
|
|
60
|
-
|
|
73
|
+
|
|
74
|
+
def _action_details_rich_repr(action: run_definition_pb2.ActionDetails) -> rich.repr.Result:
|
|
61
75
|
"""
|
|
62
76
|
Rich representation of the action details.
|
|
63
77
|
"""
|
|
64
78
|
yield "name", action.id.run.name
|
|
65
79
|
yield from _action_time_phase(action)
|
|
66
|
-
# yield "task", action.metadata.task.id.name
|
|
67
80
|
yield "task", action.resolved_task_spec.task_template.id.name
|
|
68
81
|
yield "task_type", action.resolved_task_spec.task_template.type
|
|
69
82
|
yield "task_version", action.resolved_task_spec.task_template.id.version
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
83
|
+
yield "attempts", action.attempts
|
|
84
|
+
yield "error", f"{action.error_info.kind}: {action.error_info.message}" if action.HasField("error_info") else "NA"
|
|
85
|
+
yield "phase", run_definition_pb2.Phase.Name(action.status.phase)
|
|
86
|
+
yield "group", action.metadata.group
|
|
87
|
+
yield "parent", action.metadata.parent
|
|
74
88
|
|
|
75
89
|
|
|
76
90
|
def _action_done_check(phase: run_definition_pb2.Phase) -> bool:
|
|
@@ -104,14 +118,13 @@ class Run:
|
|
|
104
118
|
raise RuntimeError("Run does not have an action")
|
|
105
119
|
self.action = Action(self.pb2.action)
|
|
106
120
|
|
|
121
|
+
@syncify
|
|
107
122
|
@classmethod
|
|
108
|
-
@requires_client
|
|
109
|
-
@syncer.wrap
|
|
110
123
|
async def listall(
|
|
111
124
|
cls,
|
|
112
125
|
filters: str | None = None,
|
|
113
126
|
sort_by: Tuple[str, Literal["asc", "desc"]] | None = None,
|
|
114
|
-
) ->
|
|
127
|
+
) -> AsyncIterator[Run]:
|
|
115
128
|
"""
|
|
116
129
|
Get all runs for the current project and domain.
|
|
117
130
|
|
|
@@ -119,6 +132,7 @@ class Run:
|
|
|
119
132
|
:param sort_by: The sorting criteria for the project list, in the format (field, order).
|
|
120
133
|
:return: An iterator of runs.
|
|
121
134
|
"""
|
|
135
|
+
ensure_client()
|
|
122
136
|
token = None
|
|
123
137
|
sort_by = sort_by or ("created_at", "asc")
|
|
124
138
|
sort_pb2 = list_pb2.Sort(
|
|
@@ -148,16 +162,16 @@ class Run:
|
|
|
148
162
|
if not token:
|
|
149
163
|
break
|
|
150
164
|
|
|
165
|
+
@syncify
|
|
151
166
|
@classmethod
|
|
152
|
-
@requires_client
|
|
153
|
-
@syncer.wrap
|
|
154
167
|
async def get(cls, name: str) -> Run:
|
|
155
168
|
"""
|
|
156
169
|
Get the current run.
|
|
157
170
|
|
|
158
171
|
:return: The current run.
|
|
159
172
|
"""
|
|
160
|
-
|
|
173
|
+
ensure_client()
|
|
174
|
+
run_details: RunDetails = await RunDetails.get.aio(name=name)
|
|
161
175
|
run = run_definition_pb2.Run(
|
|
162
176
|
action=run_definition_pb2.Action(
|
|
163
177
|
id=run_details.action_id,
|
|
@@ -179,78 +193,38 @@ class Run:
|
|
|
179
193
|
"""
|
|
180
194
|
Get the phase of the run.
|
|
181
195
|
"""
|
|
182
|
-
return
|
|
196
|
+
return self.action.phase
|
|
197
|
+
|
|
198
|
+
@property
|
|
199
|
+
def raw_phase(self) -> run_definition_pb2.Phase:
|
|
200
|
+
"""
|
|
201
|
+
Get the raw phase of the run.
|
|
202
|
+
"""
|
|
203
|
+
return self.action.raw_phase
|
|
183
204
|
|
|
184
|
-
@
|
|
185
|
-
async def wait(self, quiet: bool = False) -> None:
|
|
205
|
+
@syncify
|
|
206
|
+
async def wait(self, quiet: bool = False, wait_for: Literal["terminal", "running"] = "terminal") -> None:
|
|
186
207
|
"""
|
|
187
208
|
Wait for the run to complete, displaying a rich progress panel with status transitions,
|
|
188
209
|
time elapsed, and error details in case of failure.
|
|
189
210
|
"""
|
|
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]")
|
|
211
|
+
return await self.action.wait(quiet=quiet, wait_for=wait_for)
|
|
236
212
|
|
|
237
213
|
async def watch(self, cache_data_on_done: bool = False) -> AsyncGenerator[ActionDetails, None]:
|
|
238
214
|
"""
|
|
239
215
|
Get the details of the run. This is a placeholder for getting the run details.
|
|
240
216
|
"""
|
|
241
|
-
|
|
242
|
-
if ad is None:
|
|
243
|
-
return
|
|
244
|
-
yield ad
|
|
217
|
+
return self.action.watch(cache_data_on_done=cache_data_on_done)
|
|
245
218
|
|
|
246
|
-
async def show_logs(
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
219
|
+
async def show_logs(
|
|
220
|
+
self,
|
|
221
|
+
attempt: int | None = None,
|
|
222
|
+
max_lines: int = 100,
|
|
223
|
+
show_ts: bool = False,
|
|
224
|
+
raw: bool = False,
|
|
225
|
+
filter_system: bool = False,
|
|
226
|
+
):
|
|
227
|
+
await self.action.show_logs(attempt, max_lines, show_ts, raw, filter_system=filter_system)
|
|
254
228
|
|
|
255
229
|
async def details(self) -> RunDetails:
|
|
256
230
|
"""
|
|
@@ -261,7 +235,6 @@ class Run:
|
|
|
261
235
|
return self._details
|
|
262
236
|
|
|
263
237
|
@property
|
|
264
|
-
@requires_client
|
|
265
238
|
def url(self) -> str:
|
|
266
239
|
"""
|
|
267
240
|
Get the URL of the run.
|
|
@@ -275,16 +248,21 @@ class Run:
|
|
|
275
248
|
run_name=self.name,
|
|
276
249
|
)
|
|
277
250
|
|
|
278
|
-
@
|
|
279
|
-
async def
|
|
251
|
+
@syncify
|
|
252
|
+
async def abort(self):
|
|
280
253
|
"""
|
|
281
|
-
|
|
254
|
+
Aborts / Terminates the run.
|
|
282
255
|
"""
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
256
|
+
try:
|
|
257
|
+
await get_client().run_service.AbortRun(
|
|
258
|
+
run_service_pb2.AbortRunRequest(
|
|
259
|
+
run_id=self.pb2.action.id.run,
|
|
260
|
+
)
|
|
286
261
|
)
|
|
287
|
-
|
|
262
|
+
except grpc.aio.AioRpcError as e:
|
|
263
|
+
if e.code() == grpc.StatusCode.NOT_FOUND:
|
|
264
|
+
return
|
|
265
|
+
raise
|
|
288
266
|
|
|
289
267
|
def done(self) -> bool:
|
|
290
268
|
"""
|
|
@@ -304,7 +282,7 @@ class Run:
|
|
|
304
282
|
"""
|
|
305
283
|
Rich representation of the Run object.
|
|
306
284
|
"""
|
|
307
|
-
yield from _action_rich_repr(self.pb2.action
|
|
285
|
+
yield from _action_rich_repr(self.pb2.action)
|
|
308
286
|
|
|
309
287
|
def __repr__(self) -> str:
|
|
310
288
|
"""
|
|
@@ -331,13 +309,13 @@ class RunDetails:
|
|
|
331
309
|
"""
|
|
332
310
|
self.action_details = ActionDetails(self.pb2.action)
|
|
333
311
|
|
|
312
|
+
@syncify
|
|
334
313
|
@classmethod
|
|
335
|
-
@requires_client
|
|
336
|
-
@syncer.wrap
|
|
337
314
|
async def get_details(cls, run_id: run_definition_pb2.RunIdentifier) -> RunDetails:
|
|
338
315
|
"""
|
|
339
316
|
Get the details of the run. This is a placeholder for getting the run details.
|
|
340
317
|
"""
|
|
318
|
+
ensure_client()
|
|
341
319
|
resp = await get_client().run_service.GetRunDetails(
|
|
342
320
|
run_service_pb2.GetRunDetailsRequest(
|
|
343
321
|
run_id=run_id,
|
|
@@ -345,9 +323,8 @@ class RunDetails:
|
|
|
345
323
|
)
|
|
346
324
|
return cls(resp.details)
|
|
347
325
|
|
|
326
|
+
@syncify
|
|
348
327
|
@classmethod
|
|
349
|
-
@requires_client
|
|
350
|
-
@syncer.wrap
|
|
351
328
|
async def get(cls, name: str | None = None) -> RunDetails:
|
|
352
329
|
"""
|
|
353
330
|
Get a run by its ID or name. If both are provided, the ID will take precedence.
|
|
@@ -355,9 +332,9 @@ class RunDetails:
|
|
|
355
332
|
:param uri: The URI of the run.
|
|
356
333
|
:param name: The name of the run.
|
|
357
334
|
"""
|
|
335
|
+
ensure_client()
|
|
358
336
|
cfg = get_common_config()
|
|
359
337
|
return await RunDetails.get_details.aio(
|
|
360
|
-
cls,
|
|
361
338
|
run_id=run_definition_pb2.RunIdentifier(
|
|
362
339
|
org=cfg.org,
|
|
363
340
|
project=cfg.project,
|
|
@@ -410,7 +387,7 @@ class RunDetails:
|
|
|
410
387
|
"""
|
|
411
388
|
Rich representation of the Run object.
|
|
412
389
|
"""
|
|
413
|
-
yield from _action_details_rich_repr(self.pb2.action
|
|
390
|
+
yield from _action_details_rich_repr(self.pb2.action)
|
|
414
391
|
|
|
415
392
|
def __repr__(self) -> str:
|
|
416
393
|
"""
|
|
@@ -430,15 +407,14 @@ class Action:
|
|
|
430
407
|
pb2: run_definition_pb2.Action
|
|
431
408
|
_details: ActionDetails | None = None
|
|
432
409
|
|
|
410
|
+
@syncify
|
|
433
411
|
@classmethod
|
|
434
|
-
@requires_client
|
|
435
|
-
@syncer.wrap
|
|
436
412
|
async def listall(
|
|
437
413
|
cls,
|
|
438
414
|
for_run_name: str,
|
|
439
415
|
filters: str | None = None,
|
|
440
416
|
sort_by: Tuple[str, Literal["asc", "desc"]] | None = None,
|
|
441
|
-
) -> Union[Iterator[Action],
|
|
417
|
+
) -> Union[Iterator[Action], AsyncIterator[Action]]:
|
|
442
418
|
"""
|
|
443
419
|
Get all actions for a given run.
|
|
444
420
|
|
|
@@ -447,6 +423,7 @@ class Action:
|
|
|
447
423
|
:param sort_by: The sorting criteria for the project list, in the format (field, order).
|
|
448
424
|
:return: An iterator of projects.
|
|
449
425
|
"""
|
|
426
|
+
ensure_client()
|
|
450
427
|
token = None
|
|
451
428
|
sort_by = sort_by or ("created_at", "asc")
|
|
452
429
|
sort_pb2 = list_pb2.Sort(
|
|
@@ -476,9 +453,8 @@ class Action:
|
|
|
476
453
|
if not token:
|
|
477
454
|
break
|
|
478
455
|
|
|
456
|
+
@syncify
|
|
479
457
|
@classmethod
|
|
480
|
-
@requires_client
|
|
481
|
-
@syncer.wrap
|
|
482
458
|
async def get(cls, uri: str | None = None, /, run_name: str | None = None, name: str | None = None) -> Action:
|
|
483
459
|
"""
|
|
484
460
|
Get a run by its ID or name. If both are provided, the ID will take precedence.
|
|
@@ -487,9 +463,9 @@ class Action:
|
|
|
487
463
|
:param run_name: The name of the action.
|
|
488
464
|
:param name: The name of the action.
|
|
489
465
|
"""
|
|
466
|
+
ensure_client()
|
|
490
467
|
cfg = get_common_config()
|
|
491
468
|
details: ActionDetails = await ActionDetails.get_details.aio(
|
|
492
|
-
cls,
|
|
493
469
|
run_definition_pb2.ActionIdentifier(
|
|
494
470
|
run=run_definition_pb2.RunIdentifier(
|
|
495
471
|
org=cfg.org,
|
|
@@ -516,6 +492,13 @@ class Action:
|
|
|
516
492
|
"""
|
|
517
493
|
return run_definition_pb2.Phase.Name(self.pb2.status.phase)
|
|
518
494
|
|
|
495
|
+
@property
|
|
496
|
+
def raw_phase(self) -> run_definition_pb2.Phase:
|
|
497
|
+
"""
|
|
498
|
+
Get the raw phase of the action.
|
|
499
|
+
"""
|
|
500
|
+
return self.pb2.status.phase
|
|
501
|
+
|
|
519
502
|
@property
|
|
520
503
|
def name(self) -> str:
|
|
521
504
|
"""
|
|
@@ -547,19 +530,27 @@ class Action:
|
|
|
547
530
|
return self.pb2.id
|
|
548
531
|
|
|
549
532
|
async def show_logs(
|
|
550
|
-
self,
|
|
533
|
+
self,
|
|
534
|
+
attempt: int | None = None,
|
|
535
|
+
max_lines: int = 30,
|
|
536
|
+
show_ts: bool = False,
|
|
537
|
+
raw: bool = False,
|
|
538
|
+
filter_system: bool = False,
|
|
551
539
|
):
|
|
552
540
|
details = await self.details()
|
|
541
|
+
if not details.is_running and not details.done():
|
|
542
|
+
# TODO we can short circuit here if the attempt is not the last one and it is done!
|
|
543
|
+
await self.wait(wait_for="logs-ready")
|
|
544
|
+
details = await self.details()
|
|
553
545
|
if not attempt:
|
|
554
546
|
attempt = details.attempts
|
|
555
|
-
if details.phase in [
|
|
556
|
-
run_definition_pb2.PHASE_QUEUED,
|
|
557
|
-
run_definition_pb2.PHASE_INITIALIZING,
|
|
558
|
-
run_definition_pb2.PHASE_WAITING_FOR_RESOURCES,
|
|
559
|
-
]:
|
|
560
|
-
raise RuntimeError("Action has not yet started, so logs are not available.")
|
|
561
547
|
return await Logs.create_viewer(
|
|
562
|
-
action_id=self.action_id,
|
|
548
|
+
action_id=self.action_id,
|
|
549
|
+
attempt=attempt,
|
|
550
|
+
max_lines=max_lines,
|
|
551
|
+
show_ts=show_ts,
|
|
552
|
+
raw=raw,
|
|
553
|
+
filter_system=filter_system,
|
|
563
554
|
)
|
|
564
555
|
|
|
565
556
|
async def details(self) -> ActionDetails:
|
|
@@ -567,27 +558,106 @@ class Action:
|
|
|
567
558
|
Get the details of the action. This is a placeholder for getting the action details.
|
|
568
559
|
"""
|
|
569
560
|
if not self._details:
|
|
570
|
-
self._details = await ActionDetails.get_details.aio(
|
|
561
|
+
self._details = await ActionDetails.get_details.aio(self.action_id)
|
|
571
562
|
return cast(ActionDetails, self._details)
|
|
572
563
|
|
|
573
|
-
async def
|
|
564
|
+
async def watch(
|
|
565
|
+
self, cache_data_on_done: bool = False, wait_for: WaitFor = "terminal"
|
|
566
|
+
) -> AsyncGenerator[ActionDetails, None]:
|
|
574
567
|
"""
|
|
575
568
|
Watch the action for updates. This is a placeholder for watching the action.
|
|
576
569
|
"""
|
|
577
570
|
ad = None
|
|
578
|
-
async for ad in ActionDetails.watch.aio(
|
|
571
|
+
async for ad in ActionDetails.watch.aio(self.action_id):
|
|
579
572
|
if ad is None:
|
|
580
573
|
return
|
|
581
574
|
self._details = ad
|
|
582
575
|
yield ad
|
|
576
|
+
if wait_for == "running" and ad.is_running:
|
|
577
|
+
break
|
|
578
|
+
elif wait_for == "logs-ready" and ad.logs_available():
|
|
579
|
+
break
|
|
580
|
+
if ad.done():
|
|
581
|
+
break
|
|
583
582
|
if cache_data_on_done and ad and ad.done():
|
|
584
583
|
await cast(ActionDetails, self._details).outputs()
|
|
585
584
|
|
|
585
|
+
async def wait(self, quiet: bool = False, wait_for: WaitFor = "terminal") -> None:
|
|
586
|
+
"""
|
|
587
|
+
Wait for the run to complete, displaying a rich progress panel with status transitions,
|
|
588
|
+
time elapsed, and error details in case of failure.
|
|
589
|
+
"""
|
|
590
|
+
console = Console()
|
|
591
|
+
if self.done():
|
|
592
|
+
if not quiet:
|
|
593
|
+
if self.pb2.status.phase == run_definition_pb2.PHASE_SUCCEEDED:
|
|
594
|
+
console.print(
|
|
595
|
+
f"[bold green]Action '{self.name}' in Run '{self.run_name}'"
|
|
596
|
+
f" completed successfully.[/bold green]"
|
|
597
|
+
)
|
|
598
|
+
else:
|
|
599
|
+
details = await self.details()
|
|
600
|
+
console.print(
|
|
601
|
+
f"[bold red]Action '{self.name}' in Run '{self.run_name}'"
|
|
602
|
+
f" exited unsuccessfully in state {self.phase} with error: {details.error_info}[/bold red]"
|
|
603
|
+
)
|
|
604
|
+
return
|
|
605
|
+
|
|
606
|
+
try:
|
|
607
|
+
with Progress(
|
|
608
|
+
SpinnerColumn(),
|
|
609
|
+
TextColumn("[progress.description]{task.description}"),
|
|
610
|
+
TimeElapsedColumn(),
|
|
611
|
+
console=console,
|
|
612
|
+
transient=True,
|
|
613
|
+
disable=quiet,
|
|
614
|
+
) as progress:
|
|
615
|
+
task_id = progress.add_task(f"Waiting for run '{self.name}'...", start=False)
|
|
616
|
+
progress.start_task(task_id)
|
|
617
|
+
|
|
618
|
+
async for ad in self.watch(cache_data_on_done=True, wait_for=wait_for):
|
|
619
|
+
if ad is None:
|
|
620
|
+
progress.stop_task(task_id)
|
|
621
|
+
break
|
|
622
|
+
|
|
623
|
+
if ad.is_running and wait_for == "running":
|
|
624
|
+
progress.start_task(task_id)
|
|
625
|
+
break
|
|
626
|
+
|
|
627
|
+
if ad.logs_available() and wait_for == "logs-ready":
|
|
628
|
+
progress.start_task(task_id)
|
|
629
|
+
break
|
|
630
|
+
|
|
631
|
+
# Update progress description with the current phase
|
|
632
|
+
progress.update(
|
|
633
|
+
task_id,
|
|
634
|
+
description=f"Run: {self.name} in {ad.phase}, Runtime: {ad.runtime} secs "
|
|
635
|
+
f"Attempts[{ad.attempts}]",
|
|
636
|
+
)
|
|
637
|
+
|
|
638
|
+
# If the action is done, handle the final state
|
|
639
|
+
if ad.done():
|
|
640
|
+
progress.stop_task(task_id)
|
|
641
|
+
if ad.pb2.status.phase == run_definition_pb2.PHASE_SUCCEEDED:
|
|
642
|
+
console.print(f"[bold green]Run '{self.name}' completed successfully.[/bold green]")
|
|
643
|
+
else:
|
|
644
|
+
console.print(
|
|
645
|
+
f"[bold red]Run '{self.name}' exited unsuccessfully in state {ad.phase}"
|
|
646
|
+
f"with error: {ad.error_info}[/bold red]"
|
|
647
|
+
)
|
|
648
|
+
break
|
|
649
|
+
except asyncio.CancelledError:
|
|
650
|
+
# Handle cancellation gracefully
|
|
651
|
+
pass
|
|
652
|
+
except KeyboardInterrupt:
|
|
653
|
+
# Handle keyboard interrupt gracefully
|
|
654
|
+
pass
|
|
655
|
+
|
|
586
656
|
def done(self) -> bool:
|
|
587
657
|
"""
|
|
588
658
|
Check if the action is done.
|
|
589
659
|
"""
|
|
590
|
-
return _action_done_check(self.
|
|
660
|
+
return _action_done_check(self.raw_phase)
|
|
591
661
|
|
|
592
662
|
async def sync(self) -> Action:
|
|
593
663
|
"""
|
|
@@ -599,7 +669,9 @@ class Action:
|
|
|
599
669
|
"""
|
|
600
670
|
Rich representation of the Action object.
|
|
601
671
|
"""
|
|
602
|
-
yield from _action_rich_repr(self.pb2
|
|
672
|
+
yield from _action_rich_repr(self.pb2)
|
|
673
|
+
if self._details:
|
|
674
|
+
yield from self._details.__rich_repr__()
|
|
603
675
|
|
|
604
676
|
def __repr__(self) -> str:
|
|
605
677
|
"""
|
|
@@ -620,13 +692,13 @@ class ActionDetails:
|
|
|
620
692
|
_inputs: ActionInputs | None = None
|
|
621
693
|
_outputs: ActionOutputs | None = None
|
|
622
694
|
|
|
695
|
+
@syncify
|
|
623
696
|
@classmethod
|
|
624
|
-
@requires_client
|
|
625
|
-
@syncer.wrap
|
|
626
697
|
async def get_details(cls, action_id: run_definition_pb2.ActionIdentifier) -> ActionDetails:
|
|
627
698
|
"""
|
|
628
699
|
Get the details of the action. This is a placeholder for getting the action details.
|
|
629
700
|
"""
|
|
701
|
+
ensure_client()
|
|
630
702
|
resp = await get_client().run_service.GetActionDetails(
|
|
631
703
|
run_service_pb2.GetActionDetailsRequest(
|
|
632
704
|
action_id=action_id,
|
|
@@ -634,9 +706,8 @@ class ActionDetails:
|
|
|
634
706
|
)
|
|
635
707
|
return ActionDetails(resp.details)
|
|
636
708
|
|
|
709
|
+
@syncify
|
|
637
710
|
@classmethod
|
|
638
|
-
@requires_client
|
|
639
|
-
@syncer.wrap
|
|
640
711
|
async def get(
|
|
641
712
|
cls, uri: str | None = None, /, run_name: str | None = None, name: str | None = None
|
|
642
713
|
) -> ActionDetails:
|
|
@@ -647,11 +718,11 @@ class ActionDetails:
|
|
|
647
718
|
:param name: The name of the action.
|
|
648
719
|
:param run_name: The name of the run.
|
|
649
720
|
"""
|
|
721
|
+
ensure_client()
|
|
650
722
|
if not uri:
|
|
651
723
|
assert name is not None and run_name is not None, "Either uri or name and run_name must be provided"
|
|
652
724
|
cfg = get_common_config()
|
|
653
725
|
return await cls.get_details.aio(
|
|
654
|
-
cls,
|
|
655
726
|
run_definition_pb2.ActionIdentifier(
|
|
656
727
|
run=run_definition_pb2.RunIdentifier(
|
|
657
728
|
org=cfg.org,
|
|
@@ -663,13 +734,13 @@ class ActionDetails:
|
|
|
663
734
|
),
|
|
664
735
|
)
|
|
665
736
|
|
|
737
|
+
@syncify
|
|
666
738
|
@classmethod
|
|
667
|
-
|
|
668
|
-
@syncer.wrap
|
|
669
|
-
async def watch(cls, action_id: run_definition_pb2.ActionIdentifier) -> AsyncGenerator[ActionDetails, None]:
|
|
739
|
+
async def watch(cls, action_id: run_definition_pb2.ActionIdentifier) -> AsyncIterator[ActionDetails]:
|
|
670
740
|
"""
|
|
671
741
|
Watch the action for updates. This is a placeholder for watching the action.
|
|
672
742
|
"""
|
|
743
|
+
ensure_client()
|
|
673
744
|
if not action_id:
|
|
674
745
|
raise ValueError("Action ID is required")
|
|
675
746
|
|
|
@@ -701,7 +772,7 @@ class ActionDetails:
|
|
|
701
772
|
break
|
|
702
773
|
|
|
703
774
|
if cache_data_on_done and self.done():
|
|
704
|
-
await self._cache_data.aio(
|
|
775
|
+
await self._cache_data.aio()
|
|
705
776
|
|
|
706
777
|
@property
|
|
707
778
|
def phase(self) -> str:
|
|
@@ -710,6 +781,20 @@ class ActionDetails:
|
|
|
710
781
|
"""
|
|
711
782
|
return run_definition_pb2.Phase.Name(self.status.phase)
|
|
712
783
|
|
|
784
|
+
@property
|
|
785
|
+
def raw_phase(self) -> run_definition_pb2.Phase:
|
|
786
|
+
"""
|
|
787
|
+
Get the raw phase of the action.
|
|
788
|
+
"""
|
|
789
|
+
return self.status.phase
|
|
790
|
+
|
|
791
|
+
@property
|
|
792
|
+
def is_running(self) -> bool:
|
|
793
|
+
"""
|
|
794
|
+
Check if the action is currently running.
|
|
795
|
+
"""
|
|
796
|
+
return self.status.phase == run_definition_pb2.PHASE_RUNNING
|
|
797
|
+
|
|
713
798
|
@property
|
|
714
799
|
def name(self) -> str:
|
|
715
800
|
"""
|
|
@@ -778,7 +863,19 @@ class ActionDetails:
|
|
|
778
863
|
"""
|
|
779
864
|
return self.pb2.status.attempts
|
|
780
865
|
|
|
781
|
-
|
|
866
|
+
def logs_available(self, attempt: int | None = None) -> bool:
|
|
867
|
+
"""
|
|
868
|
+
Check if logs are available for the action, optionally for a specific attempt.
|
|
869
|
+
If attempt is None, it checks for the latest attempt.
|
|
870
|
+
"""
|
|
871
|
+
if attempt is None:
|
|
872
|
+
attempt = self.pb2.status.attempts
|
|
873
|
+
attempts = self.pb2.attempts
|
|
874
|
+
if attempts and len(attempts) >= attempt:
|
|
875
|
+
return attempts[attempt - 1].logs_available
|
|
876
|
+
return False
|
|
877
|
+
|
|
878
|
+
@syncify
|
|
782
879
|
async def _cache_data(self) -> bool:
|
|
783
880
|
"""
|
|
784
881
|
Cache the inputs and outputs of the action.
|
|
@@ -802,7 +899,7 @@ class ActionDetails:
|
|
|
802
899
|
Placeholder for inputs. This can be extended to handle inputs from the run context.
|
|
803
900
|
"""
|
|
804
901
|
if not self._inputs:
|
|
805
|
-
await self._cache_data.aio(
|
|
902
|
+
await self._cache_data.aio()
|
|
806
903
|
return cast(ActionInputs, self._inputs)
|
|
807
904
|
|
|
808
905
|
async def outputs(self) -> ActionOutputs:
|
|
@@ -810,7 +907,7 @@ class ActionDetails:
|
|
|
810
907
|
Placeholder for outputs. This can be extended to handle outputs from the run context.
|
|
811
908
|
"""
|
|
812
909
|
if not self._outputs:
|
|
813
|
-
if not await self._cache_data.aio(
|
|
910
|
+
if not await self._cache_data.aio():
|
|
814
911
|
raise RuntimeError(
|
|
815
912
|
"Action is not in a terminal state, outputs are not available. "
|
|
816
913
|
"Please wait for the action to complete."
|
|
@@ -822,13 +919,13 @@ class ActionDetails:
|
|
|
822
919
|
Check if the action is in a terminal state (completed or failed). This is a placeholder for checking the
|
|
823
920
|
action state.
|
|
824
921
|
"""
|
|
825
|
-
return _action_done_check(self.
|
|
922
|
+
return _action_done_check(self.raw_phase)
|
|
826
923
|
|
|
827
924
|
def __rich_repr__(self) -> rich.repr.Result:
|
|
828
925
|
"""
|
|
829
926
|
Rich representation of the Action object.
|
|
830
927
|
"""
|
|
831
|
-
yield from _action_details_rich_repr(self.pb2
|
|
928
|
+
yield from _action_details_rich_repr(self.pb2)
|
|
832
929
|
|
|
833
930
|
def __repr__(self) -> str:
|
|
834
931
|
"""
|