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.

Files changed (319) hide show
  1. flyte/__init__.py +20 -4
  2. flyte/_bin/runtime.py +33 -7
  3. flyte/_build.py +3 -2
  4. flyte/_cache/cache.py +1 -2
  5. flyte/_code_bundle/_packaging.py +1 -1
  6. flyte/_code_bundle/_utils.py +0 -16
  7. flyte/_code_bundle/bundle.py +43 -12
  8. flyte/_context.py +8 -2
  9. flyte/_deploy.py +56 -15
  10. flyte/_environment.py +45 -4
  11. flyte/_excepthook.py +37 -0
  12. flyte/_group.py +2 -1
  13. flyte/_image.py +8 -4
  14. flyte/_initialize.py +112 -254
  15. flyte/_interface.py +3 -3
  16. flyte/_internal/controllers/__init__.py +19 -6
  17. flyte/_internal/controllers/_local_controller.py +83 -8
  18. flyte/_internal/controllers/_trace.py +2 -1
  19. flyte/_internal/controllers/remote/__init__.py +27 -7
  20. flyte/_internal/controllers/remote/_action.py +7 -2
  21. flyte/_internal/controllers/remote/_client.py +5 -1
  22. flyte/_internal/controllers/remote/_controller.py +159 -26
  23. flyte/_internal/controllers/remote/_core.py +13 -5
  24. flyte/_internal/controllers/remote/_informer.py +4 -4
  25. flyte/_internal/controllers/remote/_service_protocol.py +6 -6
  26. flyte/_internal/imagebuild/docker_builder.py +12 -1
  27. flyte/_internal/imagebuild/image_builder.py +16 -11
  28. flyte/_internal/runtime/convert.py +164 -21
  29. flyte/_internal/runtime/entrypoints.py +1 -1
  30. flyte/_internal/runtime/io.py +3 -3
  31. flyte/_internal/runtime/task_serde.py +140 -20
  32. flyte/_internal/runtime/taskrunner.py +4 -3
  33. flyte/_internal/runtime/types_serde.py +1 -1
  34. flyte/_logging.py +12 -1
  35. flyte/_map.py +215 -0
  36. flyte/_pod.py +19 -0
  37. flyte/_protos/common/list_pb2.py +3 -3
  38. flyte/_protos/common/list_pb2.pyi +2 -0
  39. flyte/_protos/logs/dataplane/payload_pb2.py +28 -24
  40. flyte/_protos/logs/dataplane/payload_pb2.pyi +11 -2
  41. flyte/_protos/workflow/common_pb2.py +27 -0
  42. flyte/_protos/workflow/common_pb2.pyi +14 -0
  43. flyte/_protos/workflow/environment_pb2.py +29 -0
  44. flyte/_protos/workflow/environment_pb2.pyi +12 -0
  45. flyte/_protos/workflow/queue_service_pb2.py +40 -41
  46. flyte/_protos/workflow/queue_service_pb2.pyi +35 -30
  47. flyte/_protos/workflow/queue_service_pb2_grpc.py +15 -15
  48. flyte/_protos/workflow/run_definition_pb2.py +61 -61
  49. flyte/_protos/workflow/run_definition_pb2.pyi +8 -4
  50. flyte/_protos/workflow/run_service_pb2.py +20 -24
  51. flyte/_protos/workflow/run_service_pb2.pyi +2 -6
  52. flyte/_protos/workflow/state_service_pb2.py +36 -28
  53. flyte/_protos/workflow/state_service_pb2.pyi +19 -15
  54. flyte/_protos/workflow/state_service_pb2_grpc.py +28 -28
  55. flyte/_protos/workflow/task_definition_pb2.py +29 -22
  56. flyte/_protos/workflow/task_definition_pb2.pyi +21 -5
  57. flyte/_protos/workflow/task_service_pb2.py +27 -11
  58. flyte/_protos/workflow/task_service_pb2.pyi +29 -1
  59. flyte/_protos/workflow/task_service_pb2_grpc.py +34 -0
  60. flyte/_run.py +166 -95
  61. flyte/_task.py +110 -28
  62. flyte/_task_environment.py +55 -72
  63. flyte/_trace.py +6 -14
  64. flyte/_utils/__init__.py +6 -0
  65. flyte/_utils/async_cache.py +139 -0
  66. flyte/_utils/coro_management.py +0 -2
  67. flyte/_utils/helpers.py +45 -19
  68. flyte/_utils/org_discovery.py +57 -0
  69. flyte/_version.py +2 -2
  70. flyte/cli/__init__.py +3 -0
  71. flyte/cli/_abort.py +28 -0
  72. flyte/{_cli → cli}/_common.py +73 -23
  73. flyte/cli/_create.py +145 -0
  74. flyte/{_cli → cli}/_delete.py +4 -4
  75. flyte/{_cli → cli}/_deploy.py +26 -14
  76. flyte/cli/_gen.py +163 -0
  77. flyte/{_cli → cli}/_get.py +98 -23
  78. {union/_cli → flyte/cli}/_params.py +106 -147
  79. flyte/{_cli → cli}/_run.py +99 -20
  80. flyte/cli/main.py +166 -0
  81. flyte/config/__init__.py +3 -0
  82. flyte/config/_config.py +216 -0
  83. flyte/config/_internal.py +64 -0
  84. flyte/config/_reader.py +207 -0
  85. flyte/errors.py +29 -0
  86. flyte/extras/_container.py +33 -43
  87. flyte/io/__init__.py +17 -1
  88. flyte/io/_dir.py +2 -2
  89. flyte/io/_file.py +3 -4
  90. flyte/io/{structured_dataset → _structured_dataset}/basic_dfs.py +1 -1
  91. flyte/io/{structured_dataset → _structured_dataset}/structured_dataset.py +1 -1
  92. flyte/{_datastructures.py → models.py} +56 -7
  93. flyte/remote/__init__.py +2 -1
  94. flyte/remote/_client/_protocols.py +2 -0
  95. flyte/remote/_client/auth/_auth_utils.py +14 -0
  96. flyte/remote/_client/auth/_channel.py +34 -3
  97. flyte/remote/_client/auth/_token_client.py +3 -3
  98. flyte/remote/_client/controlplane.py +13 -13
  99. flyte/remote/_console.py +1 -1
  100. flyte/remote/_data.py +10 -6
  101. flyte/remote/_logs.py +89 -29
  102. flyte/remote/_project.py +8 -9
  103. flyte/remote/_run.py +228 -131
  104. flyte/remote/_secret.py +12 -12
  105. flyte/remote/_task.py +179 -15
  106. flyte/report/_report.py +4 -4
  107. flyte/storage/__init__.py +5 -0
  108. flyte/storage/_config.py +233 -0
  109. flyte/storage/_storage.py +23 -3
  110. flyte/syncify/__init__.py +56 -0
  111. flyte/syncify/_api.py +371 -0
  112. flyte/types/__init__.py +23 -0
  113. flyte/types/_interface.py +22 -7
  114. flyte/{io/pickle/transformer.py → types/_pickle.py} +2 -1
  115. flyte/types/_type_engine.py +95 -18
  116. flyte-0.2.0a0.dist-info/METADATA +249 -0
  117. flyte-0.2.0a0.dist-info/RECORD +218 -0
  118. {flyte-0.0.1b3.dist-info → flyte-0.2.0a0.dist-info}/entry_points.txt +1 -1
  119. flyte/_api_commons.py +0 -3
  120. flyte/_cli/__init__.py +0 -0
  121. flyte/_cli/_create.py +0 -42
  122. flyte/_cli/main.py +0 -72
  123. flyte/_internal/controllers/pbhash.py +0 -39
  124. flyte/io/_dataframe.py +0 -0
  125. flyte/io/pickle/__init__.py +0 -0
  126. flyte-0.0.1b3.dist-info/METADATA +0 -179
  127. flyte-0.0.1b3.dist-info/RECORD +0 -390
  128. union/__init__.py +0 -54
  129. union/_api_commons.py +0 -3
  130. union/_bin/__init__.py +0 -0
  131. union/_bin/runtime.py +0 -113
  132. union/_build.py +0 -25
  133. union/_cache/__init__.py +0 -12
  134. union/_cache/cache.py +0 -141
  135. union/_cache/defaults.py +0 -9
  136. union/_cache/policy_function_body.py +0 -42
  137. union/_cli/__init__.py +0 -0
  138. union/_cli/_common.py +0 -263
  139. union/_cli/_create.py +0 -40
  140. union/_cli/_delete.py +0 -23
  141. union/_cli/_deploy.py +0 -120
  142. union/_cli/_get.py +0 -162
  143. union/_cli/_run.py +0 -150
  144. union/_cli/main.py +0 -72
  145. union/_code_bundle/__init__.py +0 -8
  146. union/_code_bundle/_ignore.py +0 -113
  147. union/_code_bundle/_packaging.py +0 -187
  148. union/_code_bundle/_utils.py +0 -342
  149. union/_code_bundle/bundle.py +0 -176
  150. union/_context.py +0 -146
  151. union/_datastructures.py +0 -295
  152. union/_deploy.py +0 -185
  153. union/_doc.py +0 -29
  154. union/_docstring.py +0 -26
  155. union/_environment.py +0 -43
  156. union/_group.py +0 -31
  157. union/_hash.py +0 -23
  158. union/_image.py +0 -760
  159. union/_initialize.py +0 -585
  160. union/_interface.py +0 -84
  161. union/_internal/__init__.py +0 -3
  162. union/_internal/controllers/__init__.py +0 -77
  163. union/_internal/controllers/_local_controller.py +0 -77
  164. union/_internal/controllers/pbhash.py +0 -39
  165. union/_internal/controllers/remote/__init__.py +0 -40
  166. union/_internal/controllers/remote/_action.py +0 -131
  167. union/_internal/controllers/remote/_client.py +0 -43
  168. union/_internal/controllers/remote/_controller.py +0 -169
  169. union/_internal/controllers/remote/_core.py +0 -341
  170. union/_internal/controllers/remote/_informer.py +0 -260
  171. union/_internal/controllers/remote/_service_protocol.py +0 -44
  172. union/_internal/imagebuild/__init__.py +0 -11
  173. union/_internal/imagebuild/docker_builder.py +0 -416
  174. union/_internal/imagebuild/image_builder.py +0 -243
  175. union/_internal/imagebuild/remote_builder.py +0 -0
  176. union/_internal/resolvers/__init__.py +0 -0
  177. union/_internal/resolvers/_task_module.py +0 -31
  178. union/_internal/resolvers/common.py +0 -24
  179. union/_internal/resolvers/default.py +0 -27
  180. union/_internal/runtime/__init__.py +0 -0
  181. union/_internal/runtime/convert.py +0 -163
  182. union/_internal/runtime/entrypoints.py +0 -121
  183. union/_internal/runtime/io.py +0 -136
  184. union/_internal/runtime/resources_serde.py +0 -134
  185. union/_internal/runtime/task_serde.py +0 -202
  186. union/_internal/runtime/taskrunner.py +0 -179
  187. union/_internal/runtime/types_serde.py +0 -53
  188. union/_logging.py +0 -124
  189. union/_protos/__init__.py +0 -0
  190. union/_protos/common/authorization_pb2.py +0 -66
  191. union/_protos/common/authorization_pb2.pyi +0 -106
  192. union/_protos/common/identifier_pb2.py +0 -71
  193. union/_protos/common/identifier_pb2.pyi +0 -82
  194. union/_protos/common/identity_pb2.py +0 -48
  195. union/_protos/common/identity_pb2.pyi +0 -72
  196. union/_protos/common/identity_pb2_grpc.py +0 -4
  197. union/_protos/common/list_pb2.py +0 -36
  198. union/_protos/common/list_pb2.pyi +0 -69
  199. union/_protos/common/list_pb2_grpc.py +0 -4
  200. union/_protos/common/policy_pb2.py +0 -37
  201. union/_protos/common/policy_pb2.pyi +0 -27
  202. union/_protos/common/policy_pb2_grpc.py +0 -4
  203. union/_protos/common/role_pb2.py +0 -37
  204. union/_protos/common/role_pb2.pyi +0 -51
  205. union/_protos/common/role_pb2_grpc.py +0 -4
  206. union/_protos/common/runtime_version_pb2.py +0 -28
  207. union/_protos/common/runtime_version_pb2.pyi +0 -24
  208. union/_protos/common/runtime_version_pb2_grpc.py +0 -4
  209. union/_protos/logs/dataplane/payload_pb2.py +0 -96
  210. union/_protos/logs/dataplane/payload_pb2.pyi +0 -168
  211. union/_protos/logs/dataplane/payload_pb2_grpc.py +0 -4
  212. union/_protos/secret/definition_pb2.py +0 -49
  213. union/_protos/secret/definition_pb2.pyi +0 -93
  214. union/_protos/secret/definition_pb2_grpc.py +0 -4
  215. union/_protos/secret/payload_pb2.py +0 -62
  216. union/_protos/secret/payload_pb2.pyi +0 -94
  217. union/_protos/secret/payload_pb2_grpc.py +0 -4
  218. union/_protos/secret/secret_pb2.py +0 -38
  219. union/_protos/secret/secret_pb2.pyi +0 -6
  220. union/_protos/secret/secret_pb2_grpc.py +0 -198
  221. union/_protos/validate/validate/validate_pb2.py +0 -76
  222. union/_protos/workflow/node_execution_service_pb2.py +0 -26
  223. union/_protos/workflow/node_execution_service_pb2.pyi +0 -4
  224. union/_protos/workflow/node_execution_service_pb2_grpc.py +0 -32
  225. union/_protos/workflow/queue_service_pb2.py +0 -75
  226. union/_protos/workflow/queue_service_pb2.pyi +0 -103
  227. union/_protos/workflow/queue_service_pb2_grpc.py +0 -172
  228. union/_protos/workflow/run_definition_pb2.py +0 -100
  229. union/_protos/workflow/run_definition_pb2.pyi +0 -256
  230. union/_protos/workflow/run_definition_pb2_grpc.py +0 -4
  231. union/_protos/workflow/run_logs_service_pb2.py +0 -41
  232. union/_protos/workflow/run_logs_service_pb2.pyi +0 -28
  233. union/_protos/workflow/run_logs_service_pb2_grpc.py +0 -69
  234. union/_protos/workflow/run_service_pb2.py +0 -133
  235. union/_protos/workflow/run_service_pb2.pyi +0 -173
  236. union/_protos/workflow/run_service_pb2_grpc.py +0 -412
  237. union/_protos/workflow/state_service_pb2.py +0 -58
  238. union/_protos/workflow/state_service_pb2.pyi +0 -69
  239. union/_protos/workflow/state_service_pb2_grpc.py +0 -138
  240. union/_protos/workflow/task_definition_pb2.py +0 -72
  241. union/_protos/workflow/task_definition_pb2.pyi +0 -65
  242. union/_protos/workflow/task_definition_pb2_grpc.py +0 -4
  243. union/_protos/workflow/task_service_pb2.py +0 -44
  244. union/_protos/workflow/task_service_pb2.pyi +0 -31
  245. union/_protos/workflow/task_service_pb2_grpc.py +0 -104
  246. union/_resources.py +0 -226
  247. union/_retry.py +0 -32
  248. union/_reusable_environment.py +0 -25
  249. union/_run.py +0 -374
  250. union/_secret.py +0 -61
  251. union/_task.py +0 -354
  252. union/_task_environment.py +0 -186
  253. union/_timeout.py +0 -47
  254. union/_tools.py +0 -27
  255. union/_utils/__init__.py +0 -11
  256. union/_utils/asyn.py +0 -119
  257. union/_utils/file_handling.py +0 -71
  258. union/_utils/helpers.py +0 -46
  259. union/_utils/lazy_module.py +0 -54
  260. union/_utils/uv_script_parser.py +0 -49
  261. union/_version.py +0 -21
  262. union/connectors/__init__.py +0 -0
  263. union/errors.py +0 -128
  264. union/extras/__init__.py +0 -5
  265. union/extras/_container.py +0 -263
  266. union/io/__init__.py +0 -11
  267. union/io/_dataframe.py +0 -0
  268. union/io/_dir.py +0 -425
  269. union/io/_file.py +0 -418
  270. union/io/pickle/__init__.py +0 -0
  271. union/io/pickle/transformer.py +0 -117
  272. union/io/structured_dataset/__init__.py +0 -122
  273. union/io/structured_dataset/basic_dfs.py +0 -219
  274. union/io/structured_dataset/structured_dataset.py +0 -1057
  275. union/py.typed +0 -0
  276. union/remote/__init__.py +0 -23
  277. union/remote/_client/__init__.py +0 -0
  278. union/remote/_client/_protocols.py +0 -129
  279. union/remote/_client/auth/__init__.py +0 -12
  280. union/remote/_client/auth/_authenticators/__init__.py +0 -0
  281. union/remote/_client/auth/_authenticators/base.py +0 -391
  282. union/remote/_client/auth/_authenticators/client_credentials.py +0 -73
  283. union/remote/_client/auth/_authenticators/device_code.py +0 -120
  284. union/remote/_client/auth/_authenticators/external_command.py +0 -77
  285. union/remote/_client/auth/_authenticators/factory.py +0 -200
  286. union/remote/_client/auth/_authenticators/pkce.py +0 -515
  287. union/remote/_client/auth/_channel.py +0 -184
  288. union/remote/_client/auth/_client_config.py +0 -83
  289. union/remote/_client/auth/_default_html.py +0 -32
  290. union/remote/_client/auth/_grpc_utils/__init__.py +0 -0
  291. union/remote/_client/auth/_grpc_utils/auth_interceptor.py +0 -204
  292. union/remote/_client/auth/_grpc_utils/default_metadata_interceptor.py +0 -144
  293. union/remote/_client/auth/_keyring.py +0 -154
  294. union/remote/_client/auth/_token_client.py +0 -258
  295. union/remote/_client/auth/errors.py +0 -16
  296. union/remote/_client/controlplane.py +0 -86
  297. union/remote/_data.py +0 -149
  298. union/remote/_logs.py +0 -74
  299. union/remote/_project.py +0 -86
  300. union/remote/_run.py +0 -820
  301. union/remote/_secret.py +0 -132
  302. union/remote/_task.py +0 -193
  303. union/report/__init__.py +0 -3
  304. union/report/_report.py +0 -178
  305. union/report/_template.html +0 -124
  306. union/storage/__init__.py +0 -24
  307. union/storage/_remote_fs.py +0 -34
  308. union/storage/_storage.py +0 -247
  309. union/storage/_utils.py +0 -5
  310. union/types/__init__.py +0 -11
  311. union/types/_renderer.py +0 -162
  312. union/types/_string_literals.py +0 -120
  313. union/types/_type_engine.py +0 -2131
  314. union/types/_utils.py +0 -80
  315. /union/_protos/common/authorization_pb2_grpc.py → /flyte/_protos/workflow/common_pb2_grpc.py +0 -0
  316. /union/_protos/common/identifier_pb2_grpc.py → /flyte/_protos/workflow/environment_pb2_grpc.py +0 -0
  317. /flyte/io/{structured_dataset → _structured_dataset}/__init__.py +0 -0
  318. {flyte-0.0.1b3.dist-info → flyte-0.2.0a0.dist-info}/WHEEL +0 -0
  319. {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._api_commons import syncer
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 "error", action.error_info if action.HasField("error_info") else "NA"
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, root: bool = False) -> rich.repr.Result:
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-name", action.id.run.name
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 "task", action.metadata.task.id.name
55
- if not root:
56
- yield "group", action.metadata.group
57
- yield "parent", action.metadata.parent
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
- def _action_details_rich_repr(action: run_definition_pb2.ActionDetails, root: bool = False) -> rich.repr.Result:
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
- if not root:
71
- yield "group", action.metadata.group
72
- yield "parent", action.metadata.parent
73
- # TODO attempt info
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
- ) -> Union[Iterator[Run], AsyncGenerator[Run, None]]:
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
- run_details: RunDetails = await RunDetails.get.aio(RunDetails, name=name)
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 run_definition_pb2.Phase.Name(self.action.phase)
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
- @syncer.wrap
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
- console = Console()
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
- async for ad in self.action.watch_details(cache_data_on_done=cache_data_on_done):
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(self, attempt: int = 1, max_lines: int = 100, show_ts: bool = False, raw: bool = False):
247
- return await Logs.create_viewer(
248
- action_id=self.action.action_id,
249
- attempt=attempt,
250
- max_lines=max_lines,
251
- show_ts=show_ts,
252
- raw=raw,
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
- @syncer.wrap
279
- async def cancel(self) -> None:
251
+ @syncify
252
+ async def abort(self):
280
253
  """
281
- Cancel the run.
254
+ Aborts / Terminates the run.
282
255
  """
283
- await get_client().run_service.AbortRun(
284
- run_service_pb2.AbortRunRequest(
285
- run_id=self.pb2.action.id.run,
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, root=True)
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, root=True)
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], AsyncGenerator[Action, None]]:
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, attempt: int | None = None, max_lines: int = 30, show_ts: bool = False, raw: bool = False
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, attempt=attempt, max_lines=max_lines, show_ts=show_ts, raw=raw
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(ActionDetails, self.action_id)
561
+ self._details = await ActionDetails.get_details.aio(self.action_id)
571
562
  return cast(ActionDetails, self._details)
572
563
 
573
- async def watch_details(self, cache_data_on_done: bool = False) -> AsyncGenerator[ActionDetails, None]:
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(ActionDetails, self.action_id):
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.pb2.status.phase)
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, root=True)
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
- @requires_client
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(self)
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
- @syncer.wrap
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(self)
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(self):
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.pb2.status.phase)
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, root=True)
928
+ yield from _action_details_rich_repr(self.pb2)
832
929
 
833
930
  def __repr__(self) -> str:
834
931
  """