flyte 2.0.0b13__py3-none-any.whl → 2.0.0b30__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. flyte/__init__.py +18 -2
  2. flyte/_bin/debug.py +38 -0
  3. flyte/_bin/runtime.py +62 -8
  4. flyte/_cache/cache.py +4 -2
  5. flyte/_cache/local_cache.py +216 -0
  6. flyte/_code_bundle/_ignore.py +12 -4
  7. flyte/_code_bundle/_packaging.py +13 -9
  8. flyte/_code_bundle/_utils.py +18 -10
  9. flyte/_code_bundle/bundle.py +17 -9
  10. flyte/_constants.py +1 -0
  11. flyte/_context.py +4 -1
  12. flyte/_custom_context.py +73 -0
  13. flyte/_debug/constants.py +38 -0
  14. flyte/_debug/utils.py +17 -0
  15. flyte/_debug/vscode.py +307 -0
  16. flyte/_deploy.py +235 -61
  17. flyte/_environment.py +20 -6
  18. flyte/_excepthook.py +1 -1
  19. flyte/_hash.py +1 -16
  20. flyte/_image.py +178 -81
  21. flyte/_initialize.py +132 -51
  22. flyte/_interface.py +39 -2
  23. flyte/_internal/controllers/__init__.py +4 -5
  24. flyte/_internal/controllers/_local_controller.py +70 -29
  25. flyte/_internal/controllers/_trace.py +1 -1
  26. flyte/_internal/controllers/remote/__init__.py +0 -2
  27. flyte/_internal/controllers/remote/_action.py +14 -16
  28. flyte/_internal/controllers/remote/_client.py +1 -1
  29. flyte/_internal/controllers/remote/_controller.py +68 -70
  30. flyte/_internal/controllers/remote/_core.py +127 -99
  31. flyte/_internal/controllers/remote/_informer.py +19 -10
  32. flyte/_internal/controllers/remote/_service_protocol.py +7 -7
  33. flyte/_internal/imagebuild/docker_builder.py +181 -69
  34. flyte/_internal/imagebuild/image_builder.py +0 -5
  35. flyte/_internal/imagebuild/remote_builder.py +155 -64
  36. flyte/_internal/imagebuild/utils.py +51 -2
  37. flyte/_internal/resolvers/_task_module.py +5 -38
  38. flyte/_internal/resolvers/default.py +2 -2
  39. flyte/_internal/runtime/convert.py +110 -21
  40. flyte/_internal/runtime/entrypoints.py +27 -1
  41. flyte/_internal/runtime/io.py +21 -8
  42. flyte/_internal/runtime/resources_serde.py +20 -6
  43. flyte/_internal/runtime/reuse.py +1 -1
  44. flyte/_internal/runtime/rusty.py +20 -5
  45. flyte/_internal/runtime/task_serde.py +34 -19
  46. flyte/_internal/runtime/taskrunner.py +22 -4
  47. flyte/_internal/runtime/trigger_serde.py +160 -0
  48. flyte/_internal/runtime/types_serde.py +1 -1
  49. flyte/_keyring/__init__.py +0 -0
  50. flyte/_keyring/file.py +115 -0
  51. flyte/_logging.py +201 -39
  52. flyte/_map.py +111 -14
  53. flyte/_module.py +70 -0
  54. flyte/_pod.py +4 -3
  55. flyte/_resources.py +213 -31
  56. flyte/_run.py +110 -39
  57. flyte/_task.py +75 -16
  58. flyte/_task_environment.py +105 -29
  59. flyte/_task_plugins.py +4 -2
  60. flyte/_trace.py +5 -0
  61. flyte/_trigger.py +1000 -0
  62. flyte/_utils/__init__.py +2 -1
  63. flyte/_utils/asyn.py +3 -1
  64. flyte/_utils/coro_management.py +2 -1
  65. flyte/_utils/docker_credentials.py +173 -0
  66. flyte/_utils/module_loader.py +17 -2
  67. flyte/_version.py +3 -3
  68. flyte/cli/_abort.py +3 -3
  69. flyte/cli/_build.py +3 -6
  70. flyte/cli/_common.py +78 -7
  71. flyte/cli/_create.py +182 -4
  72. flyte/cli/_delete.py +23 -1
  73. flyte/cli/_deploy.py +63 -16
  74. flyte/cli/_get.py +79 -34
  75. flyte/cli/_params.py +26 -10
  76. flyte/cli/_plugins.py +209 -0
  77. flyte/cli/_run.py +151 -26
  78. flyte/cli/_serve.py +64 -0
  79. flyte/cli/_update.py +37 -0
  80. flyte/cli/_user.py +17 -0
  81. flyte/cli/main.py +30 -4
  82. flyte/config/_config.py +10 -6
  83. flyte/config/_internal.py +1 -0
  84. flyte/config/_reader.py +29 -8
  85. flyte/connectors/__init__.py +11 -0
  86. flyte/connectors/_connector.py +270 -0
  87. flyte/connectors/_server.py +197 -0
  88. flyte/connectors/utils.py +135 -0
  89. flyte/errors.py +22 -2
  90. flyte/extend.py +8 -1
  91. flyte/extras/_container.py +6 -1
  92. flyte/git/__init__.py +3 -0
  93. flyte/git/_config.py +21 -0
  94. flyte/io/__init__.py +2 -0
  95. flyte/io/_dataframe/__init__.py +2 -0
  96. flyte/io/_dataframe/basic_dfs.py +17 -8
  97. flyte/io/_dataframe/dataframe.py +98 -132
  98. flyte/io/_dir.py +575 -113
  99. flyte/io/_file.py +582 -139
  100. flyte/io/_hashing_io.py +342 -0
  101. flyte/models.py +74 -15
  102. flyte/remote/__init__.py +6 -1
  103. flyte/remote/_action.py +34 -26
  104. flyte/remote/_client/_protocols.py +39 -4
  105. flyte/remote/_client/auth/_authenticators/device_code.py +4 -5
  106. flyte/remote/_client/auth/_authenticators/pkce.py +1 -1
  107. flyte/remote/_client/auth/_channel.py +10 -6
  108. flyte/remote/_client/controlplane.py +17 -5
  109. flyte/remote/_console.py +3 -2
  110. flyte/remote/_data.py +6 -6
  111. flyte/remote/_logs.py +3 -3
  112. flyte/remote/_run.py +64 -8
  113. flyte/remote/_secret.py +26 -17
  114. flyte/remote/_task.py +75 -33
  115. flyte/remote/_trigger.py +306 -0
  116. flyte/remote/_user.py +33 -0
  117. flyte/report/_report.py +1 -1
  118. flyte/storage/__init__.py +6 -1
  119. flyte/storage/_config.py +5 -1
  120. flyte/storage/_parallel_reader.py +274 -0
  121. flyte/storage/_storage.py +200 -103
  122. flyte/types/__init__.py +16 -0
  123. flyte/types/_interface.py +2 -2
  124. flyte/types/_pickle.py +35 -8
  125. flyte/types/_string_literals.py +8 -9
  126. flyte/types/_type_engine.py +40 -70
  127. flyte/types/_utils.py +1 -1
  128. flyte-2.0.0b30.data/scripts/debug.py +38 -0
  129. {flyte-2.0.0b13.data → flyte-2.0.0b30.data}/scripts/runtime.py +62 -8
  130. {flyte-2.0.0b13.dist-info → flyte-2.0.0b30.dist-info}/METADATA +11 -3
  131. flyte-2.0.0b30.dist-info/RECORD +192 -0
  132. {flyte-2.0.0b13.dist-info → flyte-2.0.0b30.dist-info}/entry_points.txt +3 -0
  133. flyte/_protos/common/authorization_pb2.py +0 -66
  134. flyte/_protos/common/authorization_pb2.pyi +0 -108
  135. flyte/_protos/common/authorization_pb2_grpc.py +0 -4
  136. flyte/_protos/common/identifier_pb2.py +0 -93
  137. flyte/_protos/common/identifier_pb2.pyi +0 -110
  138. flyte/_protos/common/identifier_pb2_grpc.py +0 -4
  139. flyte/_protos/common/identity_pb2.py +0 -48
  140. flyte/_protos/common/identity_pb2.pyi +0 -72
  141. flyte/_protos/common/identity_pb2_grpc.py +0 -4
  142. flyte/_protos/common/list_pb2.py +0 -36
  143. flyte/_protos/common/list_pb2.pyi +0 -71
  144. flyte/_protos/common/list_pb2_grpc.py +0 -4
  145. flyte/_protos/common/policy_pb2.py +0 -37
  146. flyte/_protos/common/policy_pb2.pyi +0 -27
  147. flyte/_protos/common/policy_pb2_grpc.py +0 -4
  148. flyte/_protos/common/role_pb2.py +0 -37
  149. flyte/_protos/common/role_pb2.pyi +0 -53
  150. flyte/_protos/common/role_pb2_grpc.py +0 -4
  151. flyte/_protos/common/runtime_version_pb2.py +0 -28
  152. flyte/_protos/common/runtime_version_pb2.pyi +0 -24
  153. flyte/_protos/common/runtime_version_pb2_grpc.py +0 -4
  154. flyte/_protos/imagebuilder/definition_pb2.py +0 -59
  155. flyte/_protos/imagebuilder/definition_pb2.pyi +0 -140
  156. flyte/_protos/imagebuilder/definition_pb2_grpc.py +0 -4
  157. flyte/_protos/imagebuilder/payload_pb2.py +0 -32
  158. flyte/_protos/imagebuilder/payload_pb2.pyi +0 -21
  159. flyte/_protos/imagebuilder/payload_pb2_grpc.py +0 -4
  160. flyte/_protos/imagebuilder/service_pb2.py +0 -29
  161. flyte/_protos/imagebuilder/service_pb2.pyi +0 -5
  162. flyte/_protos/imagebuilder/service_pb2_grpc.py +0 -66
  163. flyte/_protos/logs/dataplane/payload_pb2.py +0 -100
  164. flyte/_protos/logs/dataplane/payload_pb2.pyi +0 -177
  165. flyte/_protos/logs/dataplane/payload_pb2_grpc.py +0 -4
  166. flyte/_protos/secret/definition_pb2.py +0 -49
  167. flyte/_protos/secret/definition_pb2.pyi +0 -93
  168. flyte/_protos/secret/definition_pb2_grpc.py +0 -4
  169. flyte/_protos/secret/payload_pb2.py +0 -62
  170. flyte/_protos/secret/payload_pb2.pyi +0 -94
  171. flyte/_protos/secret/payload_pb2_grpc.py +0 -4
  172. flyte/_protos/secret/secret_pb2.py +0 -38
  173. flyte/_protos/secret/secret_pb2.pyi +0 -6
  174. flyte/_protos/secret/secret_pb2_grpc.py +0 -198
  175. flyte/_protos/secret/secret_pb2_grpc_grpc.py +0 -198
  176. flyte/_protos/validate/validate/validate_pb2.py +0 -76
  177. flyte/_protos/workflow/common_pb2.py +0 -27
  178. flyte/_protos/workflow/common_pb2.pyi +0 -14
  179. flyte/_protos/workflow/common_pb2_grpc.py +0 -4
  180. flyte/_protos/workflow/environment_pb2.py +0 -29
  181. flyte/_protos/workflow/environment_pb2.pyi +0 -12
  182. flyte/_protos/workflow/environment_pb2_grpc.py +0 -4
  183. flyte/_protos/workflow/node_execution_service_pb2.py +0 -26
  184. flyte/_protos/workflow/node_execution_service_pb2.pyi +0 -4
  185. flyte/_protos/workflow/node_execution_service_pb2_grpc.py +0 -32
  186. flyte/_protos/workflow/queue_service_pb2.py +0 -109
  187. flyte/_protos/workflow/queue_service_pb2.pyi +0 -166
  188. flyte/_protos/workflow/queue_service_pb2_grpc.py +0 -172
  189. flyte/_protos/workflow/run_definition_pb2.py +0 -121
  190. flyte/_protos/workflow/run_definition_pb2.pyi +0 -327
  191. flyte/_protos/workflow/run_definition_pb2_grpc.py +0 -4
  192. flyte/_protos/workflow/run_logs_service_pb2.py +0 -41
  193. flyte/_protos/workflow/run_logs_service_pb2.pyi +0 -28
  194. flyte/_protos/workflow/run_logs_service_pb2_grpc.py +0 -69
  195. flyte/_protos/workflow/run_service_pb2.py +0 -137
  196. flyte/_protos/workflow/run_service_pb2.pyi +0 -185
  197. flyte/_protos/workflow/run_service_pb2_grpc.py +0 -446
  198. flyte/_protos/workflow/state_service_pb2.py +0 -67
  199. flyte/_protos/workflow/state_service_pb2.pyi +0 -76
  200. flyte/_protos/workflow/state_service_pb2_grpc.py +0 -138
  201. flyte/_protos/workflow/task_definition_pb2.py +0 -79
  202. flyte/_protos/workflow/task_definition_pb2.pyi +0 -81
  203. flyte/_protos/workflow/task_definition_pb2_grpc.py +0 -4
  204. flyte/_protos/workflow/task_service_pb2.py +0 -60
  205. flyte/_protos/workflow/task_service_pb2.pyi +0 -59
  206. flyte/_protos/workflow/task_service_pb2_grpc.py +0 -138
  207. flyte-2.0.0b13.dist-info/RECORD +0 -239
  208. /flyte/{_protos → _debug}/__init__.py +0 -0
  209. {flyte-2.0.0b13.dist-info → flyte-2.0.0b30.dist-info}/WHEEL +0 -0
  210. {flyte-2.0.0b13.dist-info → flyte-2.0.0b30.dist-info}/licenses/LICENSE +0 -0
  211. {flyte-2.0.0b13.dist-info → flyte-2.0.0b30.dist-info}/top_level.txt +0 -0
flyte/_utils/__init__.py CHANGED
@@ -9,12 +9,13 @@ from .coro_management import run_coros
9
9
  from .file_handling import filehash_update, update_hasher_for_source
10
10
  from .helpers import get_cwd_editable_install
11
11
  from .lazy_module import lazy_module
12
- from .module_loader import load_python_modules
12
+ from .module_loader import adjust_sys_path, load_python_modules
13
13
  from .org_discovery import hostname_from_url, org_from_endpoint, sanitize_endpoint
14
14
  from .uv_script_parser import parse_uv_script_file
15
15
 
16
16
  __all__ = [
17
17
  "AsyncLRUCache",
18
+ "adjust_sys_path",
18
19
  "filehash_update",
19
20
  "get_cwd_editable_install",
20
21
  "hostname_from_url",
flyte/_utils/asyn.py CHANGED
@@ -9,6 +9,8 @@ async def async_add(a: int, b: int) -> int:
9
9
  result = run_sync(async_add, a=10, b=12)
10
10
  """
11
11
 
12
+ from __future__ import annotations
13
+
12
14
  import asyncio
13
15
  import atexit
14
16
  import functools
@@ -88,7 +90,7 @@ class _TaskRunner:
88
90
 
89
91
 
90
92
  class _AsyncLoopManager:
91
- def __init__(self):
93
+ def __init__(self: _AsyncLoopManager):
92
94
  self._runner_map: dict[str, _TaskRunner] = {}
93
95
 
94
96
  def run_sync(self, coro_func: Callable[..., Awaitable[T]], *args, **kwargs) -> T:
@@ -11,7 +11,8 @@ async def run_coros(*coros: typing.Coroutine, return_when: str = asyncio.FIRST_C
11
11
  :param return_when:
12
12
  :return:
13
13
  """
14
- tasks: typing.List[asyncio.Task[typing.Never]] = [asyncio.create_task(c) for c in coros]
14
+ # tasks: typing.List[asyncio.Task[typing.Never]] = [asyncio.create_task(c) for c in coros] # Python 3.11+
15
+ tasks: typing.List[asyncio.Task] = [asyncio.create_task(c) for c in coros]
15
16
  done, pending = await asyncio.wait(tasks, return_when=return_when)
16
17
  # TODO we might want to handle asyncio.CancelledError here, for cases when the `action` is cancelled
17
18
  # and we want to propagate it to all tasks. Though the backend will handle it anyway,
@@ -0,0 +1,173 @@
1
+ """Helper functions for creating Docker registry credentials for image pull secrets."""
2
+
3
+ import base64
4
+ import json
5
+ import logging
6
+ import os
7
+ import subprocess
8
+ from pathlib import Path
9
+ from typing import Any
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+ _CONFIG_JSON = "config.json"
14
+ _DEFAULT_CONFIG_PATH = f"~/.docker/{_CONFIG_JSON}"
15
+ _CRED_HELPERS = "credHelpers"
16
+ _CREDS_STORE = "credsStore"
17
+
18
+
19
+ def _load_docker_config(config_path: str | Path | None = None) -> dict[str, Any]:
20
+ """
21
+ Load Docker config from specified path.
22
+
23
+ Args:
24
+ config_path: Path to Docker config file. If None, uses DOCKER_CONFIG env var
25
+ or defaults to ~/.docker/config.json
26
+
27
+ Returns:
28
+ Dictionary containing Docker config
29
+
30
+ Raises:
31
+ FileNotFoundError: If the config file does not exist
32
+ json.JSONDecodeError: If the config file is not valid JSON
33
+ """
34
+ if not config_path:
35
+ docker_config_env = os.environ.get("DOCKER_CONFIG")
36
+ if docker_config_env:
37
+ config_path = Path(docker_config_env) / _CONFIG_JSON
38
+ else:
39
+ config_path = Path(_DEFAULT_CONFIG_PATH).expanduser()
40
+ else:
41
+ config_path = Path(config_path).expanduser()
42
+
43
+ with open(config_path) as f:
44
+ return json.load(f)
45
+
46
+
47
+ def _get_credential_helper(config: dict[str, Any], registry: str | None = None) -> str | None:
48
+ """Get credential helper for registry or global default."""
49
+ if registry and _CRED_HELPERS in config and registry in config[_CRED_HELPERS]:
50
+ return config[_CRED_HELPERS].get(registry)
51
+ return config.get(_CREDS_STORE)
52
+
53
+
54
+ def _get_credentials_from_helper(helper: str, registry: str) -> tuple[str, str] | None:
55
+ """
56
+ Get credentials from system credential helper.
57
+
58
+ Args:
59
+ helper: Name of the credential helper (e.g., "osxkeychain", "wincred")
60
+ registry: Registry hostname to get credentials for
61
+
62
+ Returns:
63
+ Tuple of (username, password) or None if credentials cannot be retrieved
64
+ """
65
+ helper_cmd = f"docker-credential-{helper}"
66
+
67
+ try:
68
+ process = subprocess.Popen(
69
+ [helper_cmd, "get"],
70
+ stdin=subprocess.PIPE,
71
+ stdout=subprocess.PIPE,
72
+ stderr=subprocess.PIPE,
73
+ text=True,
74
+ )
75
+ output, error = process.communicate(input=registry)
76
+
77
+ if process.returncode != 0:
78
+ logger.error(f"Credential helper error: {error}")
79
+ return None
80
+
81
+ creds = json.loads(output)
82
+ return creds.get("Username"), creds.get("Secret")
83
+ except FileNotFoundError:
84
+ logger.error(f"Credential helper {helper_cmd} not found in PATH")
85
+ return None
86
+ except Exception as e:
87
+ logger.error(f"Error getting credentials: {e!s}")
88
+ return None
89
+
90
+
91
+ def create_dockerconfigjson_from_config(
92
+ registries: list[str] | None = None,
93
+ docker_config_path: str | Path | None = None,
94
+ ) -> str:
95
+ """
96
+ Create a dockerconfigjson string from existing Docker config.
97
+
98
+ This function extracts Docker registry credentials from the user's Docker config file
99
+ and creates a JSON string containing only the credentials for the specified registries.
100
+ It handles credentials stored directly in the config file as well as those managed by
101
+ credential helpers.
102
+
103
+ Args:
104
+ registries: List of registries to extract credentials for. If None, all registries
105
+ from the config will be used.
106
+ docker_config_path: Path to the Docker config file. If None, the function will look
107
+ for the config file in the standard locations.
108
+
109
+ Returns:
110
+ JSON string in dockerconfigjson format: {"auths": {"registry": {"auth": "..."}}}
111
+
112
+ Raises:
113
+ FileNotFoundError: If Docker config file cannot be found
114
+ ValueError: If no credentials can be extracted
115
+ """
116
+ config = _load_docker_config(docker_config_path)
117
+
118
+ # Create new config structure with empty auths
119
+ new_config: dict[str, Any] = {"auths": {}}
120
+
121
+ # Use specified registries or all from config
122
+ target_registries = registries or list(config.get("auths", {}).keys())
123
+
124
+ if not target_registries:
125
+ raise ValueError("No registries found in Docker config and none specified")
126
+
127
+ for registry in target_registries:
128
+ registry_config = config.get("auths", {}).get(registry, {})
129
+ if registry_config.get("auth"):
130
+ # Direct auth token exists
131
+ new_config["auths"][registry] = {"auth": registry_config["auth"]}
132
+ else:
133
+ # Try to get credentials from helper
134
+ helper = _get_credential_helper(config, registry)
135
+ if helper:
136
+ creds = _get_credentials_from_helper(helper, registry)
137
+ if creds:
138
+ username, password = creds
139
+ auth_string = f"{username}:{password}"
140
+ new_config["auths"][registry] = {"auth": base64.b64encode(auth_string.encode()).decode()}
141
+ else:
142
+ logger.warning(f"Could not retrieve credentials for {registry} from credential helper")
143
+ else:
144
+ logger.warning(f"No credentials found for {registry}")
145
+
146
+ if not new_config["auths"]:
147
+ raise ValueError(f"No credentials could be extracted for registries: {', '.join(target_registries)}")
148
+
149
+ return json.dumps(new_config)
150
+
151
+
152
+ def create_dockerconfigjson_from_credentials(
153
+ registry: str,
154
+ username: str,
155
+ password: str,
156
+ ) -> str:
157
+ """
158
+ Create a dockerconfigjson string from explicit credentials.
159
+
160
+ Args:
161
+ registry: Registry hostname (e.g., "ghcr.io", "docker.io")
162
+ username: Username or token name for the registry
163
+ password: Password or access token for the registry
164
+
165
+ Returns:
166
+ JSON string in dockerconfigjson format: {"auths": {"registry": {"auth": "..."}}}
167
+ """
168
+ auth_string = f"{username}:{password}"
169
+ auth_token = base64.b64encode(auth_string.encode()).decode()
170
+
171
+ config = {"auths": {registry: {"auth": auth_token}}}
172
+
173
+ return json.dumps(config)
@@ -5,9 +5,9 @@ import sys
5
5
  from pathlib import Path
6
6
  from typing import List, Tuple
7
7
 
8
- from rich.progress import BarColumn, Progress, TextColumn, TimeElapsedColumn, TimeRemainingColumn
9
-
10
8
  import flyte.errors
9
+ from flyte._constants import FLYTE_SYS_PATH
10
+ from flyte._logging import logger
11
11
 
12
12
 
13
13
  def load_python_modules(path: Path, recursive: bool = False) -> Tuple[List[str], List[Tuple[Path, str]]]:
@@ -18,6 +18,8 @@ def load_python_modules(path: Path, recursive: bool = False) -> Tuple[List[str],
18
18
  :param recursive: If True, load modules recursively from subdirectories
19
19
  :return: List of loaded module names, and list of file paths that failed to load
20
20
  """
21
+ from rich.progress import BarColumn, Progress, TextColumn, TimeElapsedColumn, TimeRemainingColumn
22
+
21
23
  loaded_modules = []
22
24
  failed_paths = []
23
25
 
@@ -87,3 +89,16 @@ def _load_module_from_file(file_path: Path) -> str | None:
87
89
 
88
90
  except Exception as e:
89
91
  raise flyte.errors.ModuleLoadError(f"Failed to load module from {file_path}: {e}") from e
92
+
93
+
94
+ def adjust_sys_path():
95
+ """
96
+ Adjust sys.path to include local sys.path entries under the root directory.
97
+ """
98
+ if "." not in sys.path or os.getcwd() not in sys.path:
99
+ sys.path.insert(0, ".")
100
+ logger.info(f"Added {os.getcwd()} to sys.path")
101
+ for p in os.environ.get(FLYTE_SYS_PATH, "").split(":"):
102
+ if p and p not in sys.path:
103
+ sys.path.insert(0, p)
104
+ logger.info(f"Added {p} to sys.path")
flyte/_version.py CHANGED
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '2.0.0b13'
32
- __version_tuple__ = version_tuple = (2, 0, 0, 'b13')
31
+ __version__ = version = '2.0.0b30'
32
+ __version_tuple__ = version_tuple = (2, 0, 0, 'b30')
33
33
 
34
- __commit_id__ = commit_id = 'g07f30e36d'
34
+ __commit_id__ = commit_id = 'gf1b5e2b2b'
flyte/cli/_abort.py CHANGED
@@ -1,5 +1,4 @@
1
1
  import rich_click as click
2
- from rich.console import Console
3
2
 
4
3
  from flyte.cli import _common as common
5
4
 
@@ -23,6 +22,7 @@ def run(cfg: common.CLIConfig, run_name: str, project: str | None = None, domain
23
22
  cfg.init(project=project, domain=domain)
24
23
  r = Run.get(name=run_name)
25
24
  if r:
26
- console = Console()
27
- r.abort()
25
+ console = common.get_console()
26
+ with console.status(f"Aborting run '{run_name}'...", spinner="dots"):
27
+ r.abort()
28
28
  console.print(f"Run '{run_name}' has been aborted.")
flyte/cli/_build.py CHANGED
@@ -3,8 +3,7 @@ from pathlib import Path
3
3
  from types import ModuleType
4
4
  from typing import Any, Dict, List, cast
5
5
 
6
- import click
7
- from click import Context
6
+ import rich_click as click
8
7
 
9
8
  import flyte
10
9
 
@@ -44,10 +43,8 @@ class BuildEnvCommand(click.Command):
44
43
  self.build_args = build_args
45
44
  super().__init__(*args, **kwargs)
46
45
 
47
- def invoke(self, ctx: Context):
48
- from rich.console import Console
49
-
50
- console = Console()
46
+ def invoke(self, ctx: click.Context):
47
+ console = common.get_console()
51
48
  console.print(f"Building Environment: {self.obj_name}")
52
49
  obj: CLIConfig = ctx.obj
53
50
  obj.init()
flyte/cli/_common.py CHANGED
@@ -1,11 +1,14 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import importlib.util
4
+ import json
4
5
  import logging
5
6
  import os
7
+ import pathlib
6
8
  import sys
7
9
  from abc import abstractmethod
8
10
  from dataclasses import dataclass, replace
11
+ from functools import lru_cache
9
12
  from pathlib import Path
10
13
  from types import MappingProxyType, ModuleType
11
14
  from typing import Any, Dict, Iterable, List, Literal, Optional
@@ -19,10 +22,12 @@ from rich.pretty import pretty_repr
19
22
  from rich.table import Table
20
23
  from rich.traceback import Traceback
21
24
 
25
+ import flyte.config
22
26
  import flyte.errors
27
+ from flyte._logging import LogFormat
23
28
  from flyte.config import Config
24
29
 
25
- OutputFormat = Literal["table", "json", "table-simple"]
30
+ OutputFormat = Literal["table", "json", "table-simple", "json-raw"]
26
31
 
27
32
  PREFERRED_BORDER_COLOR = "dim cyan"
28
33
  PREFERRED_ACCENT_COLOR = "bold #FFD700"
@@ -99,6 +104,7 @@ class CLIConfig:
99
104
  config: Config
100
105
  ctx: click.Context
101
106
  log_level: int | None = logging.ERROR
107
+ log_format: LogFormat = "console"
102
108
  endpoint: str | None = None
103
109
  insecure: bool = False
104
110
  org: str | None = None
@@ -111,13 +117,20 @@ class CLIConfig:
111
117
  """
112
118
  return replace(self, **kwargs)
113
119
 
114
- def init(self, project: str | None = None, domain: str | None = None):
120
+ def init(
121
+ self,
122
+ project: str | None = None,
123
+ domain: str | None = None,
124
+ root_dir: str | None = None,
125
+ images: tuple[str, ...] | None = None,
126
+ sync_local_sys_paths: bool = True,
127
+ ):
115
128
  from flyte.config._config import TaskConfig
116
129
 
117
130
  task_cfg = TaskConfig(
118
131
  org=self.org or self.config.task.org,
119
- project=project or self.config.task.project,
120
- domain=domain or self.config.task.domain,
132
+ project=project if project is not None else self.config.task.project,
133
+ domain=domain if domain is not None else self.config.task.domain,
121
134
  )
122
135
 
123
136
  kwargs: Dict[str, Any] = {}
@@ -131,7 +144,14 @@ class CLIConfig:
131
144
 
132
145
  updated_config = self.config.with_params(platform_cfg, task_cfg)
133
146
 
134
- flyte.init_from_config(updated_config, log_level=self.log_level)
147
+ flyte.init_from_config(
148
+ updated_config,
149
+ log_level=self.log_level,
150
+ log_format=self.log_format,
151
+ root_dir=pathlib.Path(root_dir) if root_dir else None,
152
+ images=images,
153
+ sync_local_sys_paths=sync_local_sys_paths,
154
+ )
135
155
 
136
156
 
137
157
  class InvokeBaseMixin:
@@ -177,7 +197,7 @@ class InvokeBaseMixin:
177
197
  except Exception as e:
178
198
  if ctx.obj and ctx.obj.log_level and ctx.obj.log_level <= logging.DEBUG:
179
199
  # If the user has requested verbose output, print the full traceback
180
- console = Console()
200
+ console = get_console()
181
201
  console.print(Traceback.from_exception(type(e), e, e.__traceback__))
182
202
  exit(1)
183
203
  else:
@@ -354,7 +374,7 @@ def _table_format(table: Table, vals: Iterable[Any]) -> Table:
354
374
  if headers is None:
355
375
  headers = [k for k, _ in o]
356
376
  for h in headers:
357
- table.add_column(h.capitalize())
377
+ table.add_column(h.capitalize(), no_wrap=True if "name" in h.casefold() else False)
358
378
  table.add_row(*[str(v) for _, v in o])
359
379
  return table
360
380
 
@@ -374,6 +394,7 @@ def format(title: str, vals: Iterable[Any], of: OutputFormat = "table") -> Table
374
394
  header_style=HEADER_STYLE,
375
395
  show_header=True,
376
396
  border_style=PREFERRED_BORDER_COLOR,
397
+ expand=True,
377
398
  ),
378
399
  vals,
379
400
  )
@@ -381,6 +402,11 @@ def format(title: str, vals: Iterable[Any], of: OutputFormat = "table") -> Table
381
402
  if not vals:
382
403
  return pretty_repr([])
383
404
  return pretty_repr([v.to_dict() for v in vals])
405
+ case "json-raw":
406
+ if not vals:
407
+ return []
408
+ return json.dumps([v.to_dict() for v in vals])
409
+
384
410
  raise click.ClickException("Unknown output format. Supported formats are: table, table-simple, json.")
385
411
 
386
412
 
@@ -395,3 +421,48 @@ def get_panel(title: str, renderable: Any, of: OutputFormat = "table") -> Panel:
395
421
  title=f"[{PREFERRED_ACCENT_COLOR}]{title}[/{PREFERRED_ACCENT_COLOR}]",
396
422
  border_style=PREFERRED_BORDER_COLOR,
397
423
  )
424
+
425
+
426
+ def get_console() -> Console:
427
+ """
428
+ Get a console that is configured to use colors if the terminal supports it.
429
+ """
430
+ return Console(color_system="auto", force_terminal=True, width=120)
431
+
432
+
433
+ def parse_images(cfg: Config, values: tuple[str, ...] | None) -> None:
434
+ """
435
+ Parse image values and update the config.
436
+
437
+ Args:
438
+ cfg: The Config object to write images to
439
+ values: List of image strings in format "imagename=imageuri" or just "imageuri"
440
+ """
441
+ if values is None:
442
+ return
443
+ for value in values:
444
+ if "=" in value:
445
+ image_name, image_uri = value.split("=", 1)
446
+ cfg.image.image_refs[image_name] = image_uri
447
+ else:
448
+ # If no name specified, use "default" as the name
449
+ cfg.image.image_refs["default"] = value
450
+
451
+
452
+ @lru_cache()
453
+ def initialize_config(
454
+ ctx: click.Context,
455
+ project: str,
456
+ domain: str,
457
+ root_dir: str | None = None,
458
+ images: tuple[str, ...] | None = None,
459
+ sync_local_sys_paths: bool = True,
460
+ ):
461
+ obj: CLIConfig | None = ctx.obj
462
+ if obj is None:
463
+ import flyte.config
464
+
465
+ obj = CLIConfig(flyte.config.auto(), ctx)
466
+
467
+ obj.init(project, domain, root_dir, images, sync_local_sys_paths)
468
+ return obj