flyte 0.2.0b1__py3-none-any.whl → 2.0.0b46__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 (266) hide show
  1. flyte/__init__.py +83 -30
  2. flyte/_bin/connect.py +61 -0
  3. flyte/_bin/debug.py +38 -0
  4. flyte/_bin/runtime.py +87 -19
  5. flyte/_bin/serve.py +351 -0
  6. flyte/_build.py +3 -2
  7. flyte/_cache/cache.py +6 -5
  8. flyte/_cache/local_cache.py +216 -0
  9. flyte/_code_bundle/_ignore.py +31 -5
  10. flyte/_code_bundle/_packaging.py +42 -11
  11. flyte/_code_bundle/_utils.py +57 -34
  12. flyte/_code_bundle/bundle.py +130 -27
  13. flyte/_constants.py +1 -0
  14. flyte/_context.py +21 -5
  15. flyte/_custom_context.py +73 -0
  16. flyte/_debug/constants.py +37 -0
  17. flyte/_debug/utils.py +17 -0
  18. flyte/_debug/vscode.py +315 -0
  19. flyte/_deploy.py +396 -75
  20. flyte/_deployer.py +109 -0
  21. flyte/_environment.py +94 -11
  22. flyte/_excepthook.py +37 -0
  23. flyte/_group.py +2 -1
  24. flyte/_hash.py +1 -16
  25. flyte/_image.py +544 -231
  26. flyte/_initialize.py +456 -316
  27. flyte/_interface.py +40 -5
  28. flyte/_internal/controllers/__init__.py +22 -8
  29. flyte/_internal/controllers/_local_controller.py +159 -35
  30. flyte/_internal/controllers/_trace.py +18 -10
  31. flyte/_internal/controllers/remote/__init__.py +38 -9
  32. flyte/_internal/controllers/remote/_action.py +82 -12
  33. flyte/_internal/controllers/remote/_client.py +6 -2
  34. flyte/_internal/controllers/remote/_controller.py +290 -64
  35. flyte/_internal/controllers/remote/_core.py +155 -95
  36. flyte/_internal/controllers/remote/_informer.py +40 -20
  37. flyte/_internal/controllers/remote/_service_protocol.py +2 -2
  38. flyte/_internal/imagebuild/__init__.py +2 -10
  39. flyte/_internal/imagebuild/docker_builder.py +391 -84
  40. flyte/_internal/imagebuild/image_builder.py +111 -55
  41. flyte/_internal/imagebuild/remote_builder.py +409 -0
  42. flyte/_internal/imagebuild/utils.py +79 -0
  43. flyte/_internal/resolvers/_app_env_module.py +92 -0
  44. flyte/_internal/resolvers/_task_module.py +5 -38
  45. flyte/_internal/resolvers/app_env.py +26 -0
  46. flyte/_internal/resolvers/common.py +8 -1
  47. flyte/_internal/resolvers/default.py +2 -2
  48. flyte/_internal/runtime/convert.py +319 -36
  49. flyte/_internal/runtime/entrypoints.py +106 -18
  50. flyte/_internal/runtime/io.py +71 -23
  51. flyte/_internal/runtime/resources_serde.py +21 -7
  52. flyte/_internal/runtime/reuse.py +125 -0
  53. flyte/_internal/runtime/rusty.py +196 -0
  54. flyte/_internal/runtime/task_serde.py +239 -66
  55. flyte/_internal/runtime/taskrunner.py +48 -8
  56. flyte/_internal/runtime/trigger_serde.py +162 -0
  57. flyte/_internal/runtime/types_serde.py +7 -16
  58. flyte/_keyring/file.py +115 -0
  59. flyte/_link.py +30 -0
  60. flyte/_logging.py +241 -42
  61. flyte/_map.py +312 -0
  62. flyte/_metrics.py +59 -0
  63. flyte/_module.py +74 -0
  64. flyte/_pod.py +30 -0
  65. flyte/_resources.py +296 -33
  66. flyte/_retry.py +1 -7
  67. flyte/_reusable_environment.py +72 -7
  68. flyte/_run.py +462 -132
  69. flyte/_secret.py +47 -11
  70. flyte/_serve.py +333 -0
  71. flyte/_task.py +245 -56
  72. flyte/_task_environment.py +219 -97
  73. flyte/_task_plugins.py +47 -0
  74. flyte/_tools.py +8 -8
  75. flyte/_trace.py +15 -24
  76. flyte/_trigger.py +1027 -0
  77. flyte/_utils/__init__.py +12 -1
  78. flyte/_utils/asyn.py +3 -1
  79. flyte/_utils/async_cache.py +139 -0
  80. flyte/_utils/coro_management.py +5 -4
  81. flyte/_utils/description_parser.py +19 -0
  82. flyte/_utils/docker_credentials.py +173 -0
  83. flyte/_utils/helpers.py +45 -19
  84. flyte/_utils/module_loader.py +123 -0
  85. flyte/_utils/org_discovery.py +57 -0
  86. flyte/_utils/uv_script_parser.py +8 -1
  87. flyte/_version.py +16 -3
  88. flyte/app/__init__.py +27 -0
  89. flyte/app/_app_environment.py +362 -0
  90. flyte/app/_connector_environment.py +40 -0
  91. flyte/app/_deploy.py +130 -0
  92. flyte/app/_parameter.py +343 -0
  93. flyte/app/_runtime/__init__.py +3 -0
  94. flyte/app/_runtime/app_serde.py +383 -0
  95. flyte/app/_types.py +113 -0
  96. flyte/app/extras/__init__.py +9 -0
  97. flyte/app/extras/_auth_middleware.py +217 -0
  98. flyte/app/extras/_fastapi.py +93 -0
  99. flyte/app/extras/_model_loader/__init__.py +3 -0
  100. flyte/app/extras/_model_loader/config.py +7 -0
  101. flyte/app/extras/_model_loader/loader.py +288 -0
  102. flyte/cli/__init__.py +12 -0
  103. flyte/cli/_abort.py +28 -0
  104. flyte/cli/_build.py +114 -0
  105. flyte/cli/_common.py +493 -0
  106. flyte/cli/_create.py +371 -0
  107. flyte/cli/_delete.py +45 -0
  108. flyte/cli/_deploy.py +401 -0
  109. flyte/cli/_gen.py +316 -0
  110. flyte/cli/_get.py +446 -0
  111. flyte/cli/_option.py +33 -0
  112. flyte/{_cli → cli}/_params.py +57 -17
  113. flyte/cli/_plugins.py +209 -0
  114. flyte/cli/_prefetch.py +292 -0
  115. flyte/cli/_run.py +690 -0
  116. flyte/cli/_serve.py +338 -0
  117. flyte/cli/_update.py +86 -0
  118. flyte/cli/_user.py +20 -0
  119. flyte/cli/main.py +246 -0
  120. flyte/config/__init__.py +2 -167
  121. flyte/config/_config.py +215 -163
  122. flyte/config/_internal.py +10 -1
  123. flyte/config/_reader.py +225 -0
  124. flyte/connectors/__init__.py +11 -0
  125. flyte/connectors/_connector.py +330 -0
  126. flyte/connectors/_server.py +194 -0
  127. flyte/connectors/utils.py +159 -0
  128. flyte/errors.py +134 -2
  129. flyte/extend.py +24 -0
  130. flyte/extras/_container.py +69 -56
  131. flyte/git/__init__.py +3 -0
  132. flyte/git/_config.py +279 -0
  133. flyte/io/__init__.py +8 -1
  134. flyte/io/{structured_dataset → _dataframe}/__init__.py +32 -30
  135. flyte/io/{structured_dataset → _dataframe}/basic_dfs.py +75 -68
  136. flyte/io/{structured_dataset/structured_dataset.py → _dataframe/dataframe.py} +207 -242
  137. flyte/io/_dir.py +575 -113
  138. flyte/io/_file.py +587 -141
  139. flyte/io/_hashing_io.py +342 -0
  140. flyte/io/extend.py +7 -0
  141. flyte/models.py +635 -0
  142. flyte/prefetch/__init__.py +22 -0
  143. flyte/prefetch/_hf_model.py +563 -0
  144. flyte/remote/__init__.py +14 -3
  145. flyte/remote/_action.py +879 -0
  146. flyte/remote/_app.py +346 -0
  147. flyte/remote/_auth_metadata.py +42 -0
  148. flyte/remote/_client/_protocols.py +62 -4
  149. flyte/remote/_client/auth/_auth_utils.py +19 -0
  150. flyte/remote/_client/auth/_authenticators/base.py +8 -2
  151. flyte/remote/_client/auth/_authenticators/device_code.py +4 -5
  152. flyte/remote/_client/auth/_authenticators/factory.py +4 -0
  153. flyte/remote/_client/auth/_authenticators/passthrough.py +79 -0
  154. flyte/remote/_client/auth/_authenticators/pkce.py +17 -18
  155. flyte/remote/_client/auth/_channel.py +47 -18
  156. flyte/remote/_client/auth/_client_config.py +5 -3
  157. flyte/remote/_client/auth/_keyring.py +15 -2
  158. flyte/remote/_client/auth/_token_client.py +3 -3
  159. flyte/remote/_client/controlplane.py +206 -18
  160. flyte/remote/_common.py +66 -0
  161. flyte/remote/_data.py +107 -22
  162. flyte/remote/_logs.py +116 -33
  163. flyte/remote/_project.py +21 -19
  164. flyte/remote/_run.py +164 -631
  165. flyte/remote/_secret.py +72 -29
  166. flyte/remote/_task.py +387 -46
  167. flyte/remote/_trigger.py +368 -0
  168. flyte/remote/_user.py +43 -0
  169. flyte/report/_report.py +10 -6
  170. flyte/storage/__init__.py +13 -1
  171. flyte/storage/_config.py +237 -0
  172. flyte/storage/_parallel_reader.py +289 -0
  173. flyte/storage/_storage.py +268 -59
  174. flyte/syncify/__init__.py +56 -0
  175. flyte/syncify/_api.py +414 -0
  176. flyte/types/__init__.py +39 -0
  177. flyte/types/_interface.py +22 -7
  178. flyte/{io/pickle/transformer.py → types/_pickle.py} +37 -9
  179. flyte/types/_string_literals.py +8 -9
  180. flyte/types/_type_engine.py +226 -126
  181. flyte/types/_utils.py +1 -1
  182. flyte-2.0.0b46.data/scripts/debug.py +38 -0
  183. flyte-2.0.0b46.data/scripts/runtime.py +194 -0
  184. flyte-2.0.0b46.dist-info/METADATA +352 -0
  185. flyte-2.0.0b46.dist-info/RECORD +221 -0
  186. flyte-2.0.0b46.dist-info/entry_points.txt +8 -0
  187. flyte-2.0.0b46.dist-info/licenses/LICENSE +201 -0
  188. flyte/_api_commons.py +0 -3
  189. flyte/_cli/_common.py +0 -299
  190. flyte/_cli/_create.py +0 -42
  191. flyte/_cli/_delete.py +0 -23
  192. flyte/_cli/_deploy.py +0 -140
  193. flyte/_cli/_get.py +0 -235
  194. flyte/_cli/_run.py +0 -174
  195. flyte/_cli/main.py +0 -98
  196. flyte/_datastructures.py +0 -342
  197. flyte/_internal/controllers/pbhash.py +0 -39
  198. flyte/_protos/common/authorization_pb2.py +0 -66
  199. flyte/_protos/common/authorization_pb2.pyi +0 -108
  200. flyte/_protos/common/authorization_pb2_grpc.py +0 -4
  201. flyte/_protos/common/identifier_pb2.py +0 -71
  202. flyte/_protos/common/identifier_pb2.pyi +0 -82
  203. flyte/_protos/common/identifier_pb2_grpc.py +0 -4
  204. flyte/_protos/common/identity_pb2.py +0 -48
  205. flyte/_protos/common/identity_pb2.pyi +0 -72
  206. flyte/_protos/common/identity_pb2_grpc.py +0 -4
  207. flyte/_protos/common/list_pb2.py +0 -36
  208. flyte/_protos/common/list_pb2.pyi +0 -69
  209. flyte/_protos/common/list_pb2_grpc.py +0 -4
  210. flyte/_protos/common/policy_pb2.py +0 -37
  211. flyte/_protos/common/policy_pb2.pyi +0 -27
  212. flyte/_protos/common/policy_pb2_grpc.py +0 -4
  213. flyte/_protos/common/role_pb2.py +0 -37
  214. flyte/_protos/common/role_pb2.pyi +0 -53
  215. flyte/_protos/common/role_pb2_grpc.py +0 -4
  216. flyte/_protos/common/runtime_version_pb2.py +0 -28
  217. flyte/_protos/common/runtime_version_pb2.pyi +0 -24
  218. flyte/_protos/common/runtime_version_pb2_grpc.py +0 -4
  219. flyte/_protos/logs/dataplane/payload_pb2.py +0 -96
  220. flyte/_protos/logs/dataplane/payload_pb2.pyi +0 -168
  221. flyte/_protos/logs/dataplane/payload_pb2_grpc.py +0 -4
  222. flyte/_protos/secret/definition_pb2.py +0 -49
  223. flyte/_protos/secret/definition_pb2.pyi +0 -93
  224. flyte/_protos/secret/definition_pb2_grpc.py +0 -4
  225. flyte/_protos/secret/payload_pb2.py +0 -62
  226. flyte/_protos/secret/payload_pb2.pyi +0 -94
  227. flyte/_protos/secret/payload_pb2_grpc.py +0 -4
  228. flyte/_protos/secret/secret_pb2.py +0 -38
  229. flyte/_protos/secret/secret_pb2.pyi +0 -6
  230. flyte/_protos/secret/secret_pb2_grpc.py +0 -198
  231. flyte/_protos/secret/secret_pb2_grpc_grpc.py +0 -198
  232. flyte/_protos/validate/validate/validate_pb2.py +0 -76
  233. flyte/_protos/workflow/node_execution_service_pb2.py +0 -26
  234. flyte/_protos/workflow/node_execution_service_pb2.pyi +0 -4
  235. flyte/_protos/workflow/node_execution_service_pb2_grpc.py +0 -32
  236. flyte/_protos/workflow/queue_service_pb2.py +0 -106
  237. flyte/_protos/workflow/queue_service_pb2.pyi +0 -141
  238. flyte/_protos/workflow/queue_service_pb2_grpc.py +0 -172
  239. flyte/_protos/workflow/run_definition_pb2.py +0 -128
  240. flyte/_protos/workflow/run_definition_pb2.pyi +0 -310
  241. flyte/_protos/workflow/run_definition_pb2_grpc.py +0 -4
  242. flyte/_protos/workflow/run_logs_service_pb2.py +0 -41
  243. flyte/_protos/workflow/run_logs_service_pb2.pyi +0 -28
  244. flyte/_protos/workflow/run_logs_service_pb2_grpc.py +0 -69
  245. flyte/_protos/workflow/run_service_pb2.py +0 -133
  246. flyte/_protos/workflow/run_service_pb2.pyi +0 -175
  247. flyte/_protos/workflow/run_service_pb2_grpc.py +0 -412
  248. flyte/_protos/workflow/state_service_pb2.py +0 -58
  249. flyte/_protos/workflow/state_service_pb2.pyi +0 -71
  250. flyte/_protos/workflow/state_service_pb2_grpc.py +0 -138
  251. flyte/_protos/workflow/task_definition_pb2.py +0 -72
  252. flyte/_protos/workflow/task_definition_pb2.pyi +0 -65
  253. flyte/_protos/workflow/task_definition_pb2_grpc.py +0 -4
  254. flyte/_protos/workflow/task_service_pb2.py +0 -44
  255. flyte/_protos/workflow/task_service_pb2.pyi +0 -31
  256. flyte/_protos/workflow/task_service_pb2_grpc.py +0 -104
  257. flyte/io/_dataframe.py +0 -0
  258. flyte/io/pickle/__init__.py +0 -0
  259. flyte/remote/_console.py +0 -18
  260. flyte-0.2.0b1.dist-info/METADATA +0 -179
  261. flyte-0.2.0b1.dist-info/RECORD +0 -204
  262. flyte-0.2.0b1.dist-info/entry_points.txt +0 -3
  263. /flyte/{_cli → _debug}/__init__.py +0 -0
  264. /flyte/{_protos → _keyring}/__init__.py +0 -0
  265. {flyte-0.2.0b1.dist-info → flyte-2.0.0b46.dist-info}/WHEEL +0 -0
  266. {flyte-0.2.0b1.dist-info → flyte-2.0.0b46.dist-info}/top_level.txt +0 -0
flyte/_initialize.py CHANGED
@@ -1,261 +1,28 @@
1
1
  from __future__ import annotations
2
2
 
3
- import datetime
4
3
  import functools
5
- import os
4
+ import sys
6
5
  import threading
7
6
  import typing
8
- from dataclasses import dataclass, replace
9
- from datetime import timedelta
7
+ from dataclasses import dataclass, field, replace
10
8
  from pathlib import Path
11
- from typing import TYPE_CHECKING, Any, Callable, ClassVar, Dict, List, Literal, Optional, TypeVar
9
+ from typing import TYPE_CHECKING, Callable, List, Literal, Optional, TypeVar
12
10
 
13
11
  from flyte.errors import InitializationError
12
+ from flyte.syncify import syncify
14
13
 
15
- from ._api_commons import syncer
16
- from ._logging import initialize_logger
17
- from ._tools import ipython_check
14
+ from ._logging import LogFormat, initialize_logger, logger
18
15
 
19
16
  if TYPE_CHECKING:
17
+ from flyte._internal.imagebuild import ImageBuildEngine
20
18
  from flyte.config import Config
21
19
  from flyte.remote._client.auth import AuthType, ClientConfig
22
20
  from flyte.remote._client.controlplane import ClientSet
21
+ from flyte.storage import Storage
23
22
 
24
23
  Mode = Literal["local", "remote"]
25
24
 
26
25
 
27
- def set_if_exists(d: dict, k: str, val: typing.Any) -> dict:
28
- """
29
- Given a dict ``d`` sets the key ``k`` with value of config ``v``, if the config value ``v`` is set
30
- and return the updated dictionary.
31
- """
32
- exists = isinstance(val, bool) or bool(val is not None and val)
33
- if exists:
34
- d[k] = val
35
- return d
36
-
37
-
38
- @dataclass(init=True, repr=True, eq=True, frozen=True)
39
- class Storage(object):
40
- """
41
- Data storage configuration that applies across any provider.
42
- """
43
-
44
- retries: int = 3
45
- backoff: datetime.timedelta = datetime.timedelta(seconds=5)
46
- enable_debug: bool = False
47
- attach_execution_metadata: bool = True
48
-
49
- _KEY_ENV_VAR_MAPPING: ClassVar[typing.Dict[str, str]] = {
50
- "enable_debug": "UNION_STORAGE_DEBUG",
51
- "retries": "UNION_STORAGE_RETRIES",
52
- "backoff": "UNION_STORAGE_BACKOFF_SECONDS",
53
- }
54
-
55
- def get_fsspec_kwargs(self, anonymous: bool = False, /, **kwargs) -> Dict[str, Any]:
56
- """
57
- Returns the configuration as kwargs for constructing an fsspec filesystem.
58
- """
59
- return {}
60
-
61
- @classmethod
62
- def _auto_as_kwargs(cls) -> Dict[str, Any]:
63
- retries = os.getenv(cls._KEY_ENV_VAR_MAPPING["retries"])
64
- backoff = os.getenv(cls._KEY_ENV_VAR_MAPPING["backoff"])
65
- enable_debug = os.getenv(cls._KEY_ENV_VAR_MAPPING["enable_debug"])
66
-
67
- kwargs: Dict[str, Any] = {}
68
- kwargs = set_if_exists(kwargs, "enable_debug", enable_debug)
69
- kwargs = set_if_exists(kwargs, "retries", retries)
70
- kwargs = set_if_exists(kwargs, "backoff", backoff)
71
- return kwargs
72
-
73
- @classmethod
74
- def auto(cls) -> Storage:
75
- """
76
- Construct the config object automatically from environment variables.
77
- """
78
- return cls(**cls._auto_as_kwargs())
79
-
80
-
81
- @dataclass(init=True, repr=True, eq=True, frozen=True)
82
- class S3(Storage):
83
- """
84
- S3 specific configuration
85
- """
86
-
87
- endpoint: typing.Optional[str] = None
88
- access_key_id: typing.Optional[str] = None
89
- secret_access_key: typing.Optional[str] = None
90
-
91
- _KEY_ENV_VAR_MAPPING: ClassVar[typing.Dict[str, str]] = {
92
- "endpoint": "FLYTE_AWS_ENDPOINT",
93
- "access_key_id": "FLYTE_AWS_ACCESS_KEY_ID",
94
- "secret_access_key": "FLYTE_AWS_SECRET_ACCESS_KEY",
95
- } | Storage._KEY_ENV_VAR_MAPPING
96
-
97
- # Refer to https://github.com/developmentseed/obstore/blob/33654fc37f19a657689eb93327b621e9f9e01494/obstore/python/obstore/store/_aws.pyi#L11
98
- # for key and secret
99
- _CONFIG_KEY_FSSPEC_S3_KEY_ID: ClassVar = "access_key_id"
100
- _CONFIG_KEY_FSSPEC_S3_SECRET: ClassVar = "secret_access_key"
101
- _CONFIG_KEY_ENDPOINT: ClassVar = "endpoint_url"
102
- _KEY_SKIP_SIGNATURE: ClassVar = "skip_signature"
103
-
104
- @classmethod
105
- def auto(cls) -> S3:
106
- """
107
- :return: Config
108
- """
109
- endpoint = os.getenv(cls._KEY_ENV_VAR_MAPPING["endpoint"], None)
110
- access_key_id = os.getenv(cls._KEY_ENV_VAR_MAPPING["access_key_id"], None)
111
- secret_access_key = os.getenv(cls._KEY_ENV_VAR_MAPPING["secret_access_key"], None)
112
-
113
- kwargs = super()._auto_as_kwargs()
114
- kwargs = set_if_exists(kwargs, "endpoint", endpoint)
115
- kwargs = set_if_exists(kwargs, "access_key_id", access_key_id)
116
- kwargs = set_if_exists(kwargs, "secret_access_key", secret_access_key)
117
-
118
- return S3(**kwargs)
119
-
120
- @classmethod
121
- def for_sandbox(cls) -> S3:
122
- """
123
- :return:
124
- """
125
- kwargs = super()._auto_as_kwargs()
126
- final_kwargs = kwargs | {
127
- "endpoint": "http://localhost:4566",
128
- "access_key_id": "minio",
129
- "secret_access_key": "miniostorage",
130
- }
131
- return S3(**final_kwargs)
132
-
133
- def get_fsspec_kwargs(self, anonymous: bool = False, /, **kwargs) -> Dict[str, Any]:
134
- # Construct the config object
135
- config: Dict[str, Any] = {}
136
- if self._CONFIG_KEY_FSSPEC_S3_KEY_ID in kwargs or self.access_key_id:
137
- config[self._CONFIG_KEY_FSSPEC_S3_KEY_ID] = kwargs.pop(
138
- self._CONFIG_KEY_FSSPEC_S3_KEY_ID, self.access_key_id
139
- )
140
- if self._CONFIG_KEY_FSSPEC_S3_SECRET in kwargs or self.secret_access_key:
141
- config[self._CONFIG_KEY_FSSPEC_S3_SECRET] = kwargs.pop(
142
- self._CONFIG_KEY_FSSPEC_S3_SECRET, self.secret_access_key
143
- )
144
- if self._CONFIG_KEY_ENDPOINT in kwargs or self.endpoint:
145
- config["endpoint_url"] = kwargs.pop(self._CONFIG_KEY_ENDPOINT, self.endpoint)
146
-
147
- retries = kwargs.pop("retries", self.retries)
148
- backoff = kwargs.pop("backoff", self.backoff)
149
-
150
- if anonymous:
151
- config[self._KEY_SKIP_SIGNATURE] = True
152
-
153
- retry_config = {
154
- "max_retries": retries,
155
- "backoff": {
156
- "base": 2,
157
- "init_backoff": backoff,
158
- "max_backoff": timedelta(seconds=16),
159
- },
160
- "retry_timeout": timedelta(minutes=3),
161
- }
162
-
163
- client_options = {"timeout": "99999s", "allow_http": True}
164
-
165
- if config:
166
- kwargs["config"] = config
167
- kwargs["client_options"] = client_options or None
168
- kwargs["retry_config"] = retry_config or None
169
-
170
- return kwargs
171
-
172
-
173
- @dataclass(init=True, repr=True, eq=True, frozen=True)
174
- class GCS(Storage):
175
- """
176
- Any GCS specific configuration.
177
- """
178
-
179
- gsutil_parallelism: bool = False
180
-
181
- _KEY_ENV_VAR_MAPPING: ClassVar[dict[str, str]] = {
182
- "gsutil_parallelism": "GCP_GSUTIL_PARALLELISM",
183
- }
184
-
185
- @classmethod
186
- def auto(cls) -> GCS:
187
- gsutil_parallelism = os.getenv(cls._KEY_ENV_VAR_MAPPING["gsutil_parallelism"], None)
188
-
189
- kwargs: Dict[str, Any] = {}
190
- kwargs = set_if_exists(kwargs, "gsutil_parallelism", gsutil_parallelism)
191
- return GCS(**kwargs)
192
-
193
- def get_fsspec_kwargs(self, anonymous: bool = False, /, **kwargs) -> Dict[str, Any]:
194
- return kwargs
195
-
196
-
197
- @dataclass(init=True, repr=True, eq=True, frozen=True)
198
- class ABFS(Storage):
199
- """
200
- Any Azure Blob Storage specific configuration.
201
- """
202
-
203
- account_name: typing.Optional[str] = None
204
- account_key: typing.Optional[str] = None
205
- tenant_id: typing.Optional[str] = None
206
- client_id: typing.Optional[str] = None
207
- client_secret: typing.Optional[str] = None
208
-
209
- _KEY_ENV_VAR_MAPPING: ClassVar[dict[str, str]] = {
210
- "account_name": "AZURE_STORAGE_ACCOUNT_NAME",
211
- "account_key": "AZURE_STORAGE_ACCOUNT_KEY",
212
- "tenant_id": "AZURE_TENANT_ID",
213
- "client_id": "AZURE_CLIENT_ID",
214
- "client_secret": "AZURE_CLIENT_SECRET",
215
- }
216
- _KEY_SKIP_SIGNATURE: ClassVar = "skip_signature"
217
-
218
- @classmethod
219
- def auto(cls) -> ABFS:
220
- account_name = os.getenv(cls._KEY_ENV_VAR_MAPPING["account_name"], None)
221
- account_key = os.getenv(cls._KEY_ENV_VAR_MAPPING["account_key"], None)
222
- tenant_id = os.getenv(cls._KEY_ENV_VAR_MAPPING["tenant_id"], None)
223
- client_id = os.getenv(cls._KEY_ENV_VAR_MAPPING["client_id"], None)
224
- client_secret = os.getenv(cls._KEY_ENV_VAR_MAPPING["client_secret"], None)
225
-
226
- kwargs: Dict[str, Any] = {}
227
- kwargs = set_if_exists(kwargs, "account_name", account_name)
228
- kwargs = set_if_exists(kwargs, "account_key", account_key)
229
- kwargs = set_if_exists(kwargs, "tenant_id", tenant_id)
230
- kwargs = set_if_exists(kwargs, "client_id", client_id)
231
- kwargs = set_if_exists(kwargs, "client_secret", client_secret)
232
- return ABFS(**kwargs)
233
-
234
- def get_fsspec_kwargs(self, anonymous: bool = False, /, **kwargs) -> Dict[str, Any]:
235
- config: Dict[str, Any] = {}
236
- if "account_name" in kwargs or self.account_name:
237
- config["account_name"] = kwargs.get("account_name", self.account_name)
238
- if "account_key" in kwargs or self.account_key:
239
- config["account_key"] = kwargs.get("account_key", self.account_key)
240
- if "client_id" in kwargs or self.client_id:
241
- config["client_id"] = kwargs.get("client_id", self.client_id)
242
- if "client_secret" in kwargs or self.client_secret:
243
- config["client_secret"] = kwargs.get("client_secret", self.client_secret)
244
- if "tenant_id" in kwargs or self.tenant_id:
245
- config["tenant_id"] = kwargs.get("tenant_id", self.tenant_id)
246
-
247
- if anonymous:
248
- config[self._KEY_SKIP_SIGNATURE] = True
249
-
250
- client_options = {"timeout": "99999s", "allow_http": "true"}
251
-
252
- if config:
253
- kwargs["config"] = config
254
- kwargs["client_options"] = client_options
255
-
256
- return kwargs
257
-
258
-
259
26
  @dataclass(init=True, repr=True, eq=True, frozen=True, kw_only=True)
260
27
  class CommonInit:
261
28
  """
@@ -266,12 +33,17 @@ class CommonInit:
266
33
  org: str | None = None
267
34
  project: str | None = None
268
35
  domain: str | None = None
36
+ batch_size: int = 1000
37
+ source_config_path: Optional[Path] = None # Only used for documentation
38
+ sync_local_sys_paths: bool = True
269
39
 
270
40
 
271
41
  @dataclass(init=True, kw_only=True, repr=True, eq=True, frozen=True)
272
42
  class _InitConfig(CommonInit):
273
43
  client: Optional[ClientSet] = None
274
44
  storage: Optional[Storage] = None
45
+ image_builder: "ImageBuildEngine.ImageBuilderType" = "local"
46
+ images: typing.Dict[str, str] = field(default_factory=dict)
275
47
 
276
48
  def replace(self, **kwargs) -> _InitConfig:
277
49
  return replace(self, **kwargs)
@@ -304,11 +76,19 @@ async def _initialize_client(
304
76
  """
305
77
  from flyte.remote._client.controlplane import ClientSet
306
78
 
307
- if endpoint is not None:
79
+ # https://grpc.io/docs/guides/keepalive/#keepalive-configuration-specification
80
+ channel_options = [
81
+ ("grpc.keepalive_permit_without_calls", 1),
82
+ ("grpc.keepalive_time_ms", 30000), # Send keepalive ping every 30 seconds
83
+ ("grpc.keepalive_timeout_ms", 10000), # Wait 10 seconds for keepalive response
84
+ ("grpc.http2.max_pings_without_data", 0), # Allow unlimited pings without data
85
+ ("grpc.http2.min_ping_interval_without_data_ms", 30000), # Min 30s between pings
86
+ ]
87
+
88
+ if endpoint and api_key is None:
308
89
  return await ClientSet.for_endpoint(
309
90
  endpoint,
310
91
  insecure=insecure,
311
- api_key=api_key,
312
92
  insecure_skip_verify=insecure_skip_verify,
313
93
  auth_type=auth_type,
314
94
  headless=headless,
@@ -320,17 +100,46 @@ async def _initialize_client(
320
100
  client_config=client_config,
321
101
  rpc_retries=rpc_retries,
322
102
  http_proxy_url=http_proxy_url,
103
+ grpc_options=channel_options,
323
104
  )
324
- raise NotImplementedError("Currently only endpoints are supported.")
105
+ elif api_key:
106
+ return await ClientSet.for_api_key(
107
+ api_key,
108
+ insecure=insecure,
109
+ insecure_skip_verify=insecure_skip_verify,
110
+ auth_type=auth_type,
111
+ headless=headless,
112
+ ca_cert_file_path=ca_cert_file_path,
113
+ command=command,
114
+ proxy_command=proxy_command,
115
+ client_id=client_id,
116
+ client_credentials_secret=client_credentials_secret,
117
+ client_config=client_config,
118
+ rpc_retries=rpc_retries,
119
+ http_proxy_url=http_proxy_url,
120
+ grpc_options=channel_options,
121
+ )
122
+
123
+ raise InitializationError(
124
+ "MissingEndpointOrApiKeyError", "user", "Either endpoint or api_key must be provided to initialize the client."
125
+ )
325
126
 
326
127
 
327
- @syncer.wrap
128
+ def _initialize_logger(
129
+ log_level: int | None = None, log_format: LogFormat | None = None, reset_root_logger: bool = False
130
+ ) -> None:
131
+ initialize_logger(log_level=log_level, log_format=log_format, enable_rich=True, reset_root_logger=reset_root_logger)
132
+
133
+
134
+ @syncify
328
135
  async def init(
329
136
  org: str | None = None,
330
137
  project: str | None = None,
331
138
  domain: str | None = None,
332
139
  root_dir: Path | None = None,
333
140
  log_level: int | None = None,
141
+ log_format: LogFormat | None = None,
142
+ reset_root_logger: bool = False,
334
143
  endpoint: str | None = None,
335
144
  headless: bool = False,
336
145
  insecure: bool = False,
@@ -346,7 +155,12 @@ async def init(
346
155
  rpc_retries: int = 3,
347
156
  http_proxy_url: str | None = None,
348
157
  storage: Storage | None = None,
349
- config: Config | None = None,
158
+ batch_size: int = 1000,
159
+ image_builder: ImageBuildEngine.ImageBuilderType = "local",
160
+ images: typing.Dict[str, str] | None = None,
161
+ source_config_path: Optional[Path] = None,
162
+ sync_local_sys_paths: bool = True,
163
+ load_plugin_type_transformers: bool = True,
350
164
  ) -> None:
351
165
  """
352
166
  Initialize the Flyte system with the given configuration. This method should be called before any other Flyte
@@ -355,14 +169,15 @@ async def init(
355
169
  :param project: Optional project name (not used in this implementation)
356
170
  :param domain: Optional domain name (not used in this implementation)
357
171
  :param root_dir: Optional root directory from which to determine how to load files, and find paths to files.
172
+ This is useful for determining the root directory for the current project, and for locating files like config etc.
173
+ also use to determine all the code that needs to be copied to the remote location.
358
174
  defaults to the editable install directory if the cwd is in a Python editable install, else just the cwd.
359
175
  :param log_level: Optional logging level for the logger, default is set using the default initialization policies
176
+ :param log_format: Optional logging format for the logger, default is "console"
177
+ :param reset_root_logger: By default, we clear out root logger handlers and set up our own.
360
178
  :param api_key: Optional API key for authentication
361
179
  :param endpoint: Optional API endpoint URL
362
180
  :param headless: Optional Whether to run in headless mode
363
- :param mode: Optional execution model (local, remote). Default is local. When local is used,
364
- the execution will be done locally. When remote is used, the execution will be sent to a remote server,
365
- In the remote case, the endpoint or api_key must be set.
366
181
  :param insecure_skip_verify: Whether to skip SSL certificate verification
367
182
  :param auth_client_config: Optional client configuration for authentication
368
183
  :param auth_type: The authentication type to use (Pkce, ClientSecret, ExternalCommand, DeviceFlow)
@@ -376,61 +191,338 @@ async def init(
376
191
  :param ca_cert_file_path: [optional] str Root Cert to be loaded and used to verify admin
377
192
  :param http_proxy_url: [optional] HTTP Proxy to be used for OAuth requests
378
193
  :param rpc_retries: [optional] int Number of times to retry the platform calls
379
- :param audience: oauth2 audience for the token request. This is used to validate the token
380
194
  :param insecure: insecure flag for the client
381
195
  :param storage: Optional blob store (S3, GCS, Azure) configuration if needed to access (i.e. using Minio)
382
196
  :param org: Optional organization override for the client. Should be set by auth instead.
383
- :param config: Optional config to override the init parameters
384
-
197
+ :param batch_size: Optional batch size for operations that use listings, defaults to 1000, so limit larger than
198
+ batch_size will be split into multiple requests.
199
+ :param image_builder: Optional image builder configuration, if not provided, the default image builder will be used.
200
+ :param images: Optional dict of images that can be used by referencing the image name.
201
+ :param source_config_path: Optional path to the source configuration file (This is only used for documentation)
202
+ :param sync_local_sys_paths: Whether to include and synchronize local sys.path entries under the root directory
203
+ into the remote container (default: True).
204
+ :param load_plugin_type_transformers: If enabled (default True), load the type transformer plugins registered under
205
+ the "flyte.plugins.types" entry point group.
385
206
  :return: None
386
207
  """
387
- from flyte._utils import get_cwd_editable_install
208
+ from flyte._utils import get_cwd_editable_install, org_from_endpoint, sanitize_endpoint
209
+ from flyte.types import _load_custom_type_transformers
388
210
 
389
- interactive_mode = ipython_check()
390
-
391
- initialize_logger(enable_rich=interactive_mode)
392
- if log_level:
393
- initialize_logger(log_level=log_level, enable_rich=interactive_mode)
211
+ _initialize_logger(log_level=log_level, log_format=log_format, reset_root_logger=reset_root_logger)
212
+ if load_plugin_type_transformers:
213
+ _load_custom_type_transformers()
394
214
 
395
215
  global _init_config # noqa: PLW0603
396
216
 
397
- with _init_lock:
398
- if config is None:
399
- from flyte.config import Config
217
+ endpoint = sanitize_endpoint(endpoint)
400
218
 
401
- config = Config.auto()
402
- platform_cfg = config.platform
403
- task_cfg = config.task
219
+ with _init_lock:
404
220
  client = None
405
- if endpoint or platform_cfg.endpoint or api_key:
221
+ if endpoint or api_key:
406
222
  client = await _initialize_client(
407
223
  api_key=api_key,
408
- auth_type=auth_type or platform_cfg.auth_mode,
409
- endpoint=endpoint or platform_cfg.endpoint,
224
+ auth_type=auth_type,
225
+ endpoint=endpoint,
410
226
  headless=headless,
411
- insecure=insecure or platform_cfg.insecure,
412
- insecure_skip_verify=insecure_skip_verify or platform_cfg.insecure_skip_verify,
413
- ca_cert_file_path=ca_cert_file_path or platform_cfg.ca_cert_file_path,
414
- command=command or platform_cfg.command,
415
- proxy_command=proxy_command or platform_cfg.proxy_command,
416
- client_id=client_id or platform_cfg.client_id,
417
- client_credentials_secret=client_credentials_secret or platform_cfg.client_credentials_secret,
227
+ insecure=insecure,
228
+ insecure_skip_verify=insecure_skip_verify,
229
+ ca_cert_file_path=ca_cert_file_path,
230
+ command=command,
231
+ proxy_command=proxy_command,
232
+ client_id=client_id,
233
+ client_credentials_secret=client_credentials_secret,
418
234
  client_config=auth_client_config,
419
- rpc_retries=rpc_retries or platform_cfg.rpc_retries,
420
- http_proxy_url=http_proxy_url or platform_cfg.http_proxy_url,
235
+ rpc_retries=rpc_retries,
236
+ http_proxy_url=http_proxy_url,
421
237
  )
422
238
 
423
- root_dir = root_dir or get_cwd_editable_install() or Path.cwd()
239
+ if not root_dir:
240
+ editable_root = get_cwd_editable_install()
241
+ if editable_root:
242
+ logger.info(f"Using editable install as root directory: {editable_root}")
243
+ root_dir = editable_root
244
+ else:
245
+ logger.info("No editable install found, using current working directory as root directory.")
246
+ root_dir = Path.cwd()
247
+ # We will inject the root_dir into the sys,path for module resolution
248
+ sys.path.append(str(root_dir))
249
+
424
250
  _init_config = _InitConfig(
425
251
  root_dir=root_dir,
426
- project=project or task_cfg.project,
427
- domain=domain or task_cfg.domain,
252
+ project=project,
253
+ domain=domain,
428
254
  client=client,
429
255
  storage=storage,
430
- org=org or task_cfg.org,
256
+ org=org or org_from_endpoint(endpoint),
257
+ batch_size=batch_size,
258
+ image_builder=image_builder,
259
+ images=images or {},
260
+ source_config_path=source_config_path,
261
+ sync_local_sys_paths=sync_local_sys_paths,
431
262
  )
432
263
 
433
264
 
265
+ @syncify
266
+ async def init_from_config(
267
+ path_or_config: str | Path | Config | None = None,
268
+ root_dir: Path | None = None,
269
+ log_level: int | None = None,
270
+ log_format: LogFormat = "console",
271
+ project: str | None = None,
272
+ domain: str | None = None,
273
+ storage: Storage | None = None,
274
+ batch_size: int = 1000,
275
+ image_builder: ImageBuildEngine.ImageBuilderType | None = None,
276
+ images: tuple[str, ...] | None = None,
277
+ sync_local_sys_paths: bool = True,
278
+ ) -> None:
279
+ """
280
+ Initialize the Flyte system using a configuration file or Config object. This method should be called before any
281
+ other Flyte remote API methods are called. Thread-safe implementation.
282
+
283
+ :param path_or_config: Path to the configuration file or Config object
284
+ :param project: Project name, this will override any project names in the configuration file
285
+ :param domain: Domain name, this will override any domain names in the configuration file
286
+ :param root_dir: Optional root directory from which to determine how to load files, and find paths to
287
+ files like config etc. For example if one uses the copy-style=="all", it is essential to determine the
288
+ root directory for the current project. If not provided, it defaults to the editable install directory or
289
+ if not available, the current working directory.
290
+ :param log_level: Optional logging level for the framework logger,
291
+ default is set using the default initialization policies
292
+ :param log_format: Optional logging format for the logger, default is "console"
293
+ :param storage: Optional blob store (S3, GCS, Azure) configuration if needed to access (i.e. using Minio)
294
+ :param images: List of image strings in format "imagename=imageuri" or just "imageuri".
295
+ :param sync_local_sys_paths: Whether to include and synchronize local sys.path entries under the root directory
296
+ into the remote container (default: True).
297
+ :param batch_size: Optional batch size for operations that use listings, defaults to 1000
298
+ :param image_builder: Optional image builder configuration, if provided,
299
+ will override any defaults set in the configuration.
300
+ :return: None
301
+ """
302
+ from rich.highlighter import ReprHighlighter
303
+
304
+ import flyte.config as config
305
+ from flyte.cli._common import parse_images
306
+
307
+ cfg: config.Config
308
+ cfg_path: Optional[Path] = None
309
+ if path_or_config is None:
310
+ # If no path is provided, use the default config file
311
+ cfg = config.auto()
312
+ elif isinstance(path_or_config, (str, Path)):
313
+ if root_dir:
314
+ cfg_path = root_dir.expanduser() / path_or_config
315
+ else:
316
+ cfg_path = Path(path_or_config).expanduser()
317
+ if not Path(cfg_path).exists():
318
+ raise InitializationError(
319
+ "ConfigFileNotFoundError",
320
+ "user",
321
+ f"Configuration file '{cfg_path}' does not exist., current working directory is {Path.cwd()}",
322
+ )
323
+ cfg = config.auto(cfg_path)
324
+ else:
325
+ cfg = path_or_config
326
+
327
+ logger.info(f"Flyte config initialized as {cfg}", extra={"highlighter": ReprHighlighter()})
328
+
329
+ # parse image, this will overwrite the image_refs set in the config file
330
+ parse_images(cfg, images)
331
+
332
+ await init.aio(
333
+ org=cfg.task.org,
334
+ project=project or cfg.task.project,
335
+ domain=domain or cfg.task.domain,
336
+ endpoint=cfg.platform.endpoint,
337
+ insecure=cfg.platform.insecure,
338
+ insecure_skip_verify=cfg.platform.insecure_skip_verify,
339
+ ca_cert_file_path=cfg.platform.ca_cert_file_path,
340
+ auth_type=cfg.platform.auth_mode,
341
+ command=cfg.platform.command,
342
+ proxy_command=cfg.platform.proxy_command,
343
+ client_id=cfg.platform.client_id,
344
+ client_credentials_secret=cfg.platform.client_credentials_secret,
345
+ root_dir=root_dir,
346
+ log_level=log_level,
347
+ log_format=log_format,
348
+ image_builder=image_builder or cfg.image.builder,
349
+ batch_size=batch_size,
350
+ images=cfg.image.image_refs,
351
+ storage=storage,
352
+ source_config_path=cfg_path,
353
+ sync_local_sys_paths=sync_local_sys_paths,
354
+ )
355
+
356
+
357
+ @syncify
358
+ async def init_from_api_key(
359
+ api_key: str | None = None,
360
+ project: str | None = None,
361
+ domain: str | None = None,
362
+ root_dir: Path | None = None,
363
+ log_level: int | None = None,
364
+ log_format: LogFormat | None = None,
365
+ storage: Storage | None = None,
366
+ batch_size: int = 1000,
367
+ image_builder: ImageBuildEngine.ImageBuilderType = "local",
368
+ images: typing.Dict[str, str] | None = None,
369
+ sync_local_sys_paths: bool = True,
370
+ ) -> None:
371
+ """
372
+ Initialize the Flyte system using an API key for authentication. This is a convenience
373
+ method for API key-based authentication. Thread-safe implementation.
374
+
375
+ The API key should be an encoded API key that contains the endpoint, client ID, client secret,
376
+ and organization information. You can obtain this encoded API key from your Flyte administrator
377
+ or cloud provider.
378
+
379
+ :param api_key: Optional encoded API key for authentication. If None, reads from FLYTE_API_KEY
380
+ environment variable. The API key is a base64-encoded string containing endpoint, client_id,
381
+ client_secret, and org information.
382
+ :param project: Optional project name
383
+ :param domain: Optional domain name
384
+ :param root_dir: Optional root directory from which to determine how to load files, and find paths to files.
385
+ defaults to the editable install directory if the cwd is in a Python editable install, else just the cwd.
386
+ :param log_level: Optional logging level for the logger
387
+ :param log_format: Optional logging format for the logger, default is "console"
388
+ :param storage: Optional blob store (S3, GCS, Azure) configuration
389
+ :param batch_size: Optional batch size for operations that use listings, defaults to 1000
390
+ :param image_builder: Optional image builder configuration
391
+ :param images: Optional dict of images that can be used by referencing the image name
392
+ :param sync_local_sys_paths: Whether to include and synchronize local sys.path entries under the root directory
393
+ into the remote container (default: True)
394
+ :return: None
395
+ """
396
+ import os
397
+
398
+ from flyte._utils import sanitize_endpoint
399
+ from flyte.remote._client.auth._auth_utils import decode_api_key
400
+
401
+ # If api_key is not provided, read from environment variable
402
+ if api_key is None:
403
+ api_key = os.getenv("FLYTE_API_KEY")
404
+ if api_key is None:
405
+ raise InitializationError(
406
+ "MissingApiKeyError",
407
+ "user",
408
+ "API key must be provided either as a parameter or via the FLYTE_API_KEY environment variable.",
409
+ )
410
+
411
+ # Decode the API key to extract endpoint, client_id, client_secret, and org
412
+ endpoint, client_id, client_secret, org = decode_api_key(api_key)
413
+
414
+ # Sanitize the endpoint
415
+ endpoint = sanitize_endpoint(endpoint) # type: ignore[assignment]
416
+
417
+ await init.aio(
418
+ org=None if org == "None" else org,
419
+ project=project,
420
+ domain=domain,
421
+ endpoint=endpoint,
422
+ api_key=api_key,
423
+ client_id=client_id,
424
+ client_credentials_secret=client_secret,
425
+ auth_type="ClientSecret", # API keys use client credentials flow
426
+ root_dir=root_dir,
427
+ log_level=log_level,
428
+ log_format=log_format,
429
+ insecure=False,
430
+ insecure_skip_verify=False,
431
+ storage=storage,
432
+ batch_size=batch_size,
433
+ image_builder=image_builder,
434
+ images=images,
435
+ sync_local_sys_paths=sync_local_sys_paths,
436
+ )
437
+
438
+
439
+ @syncify
440
+ async def init_in_cluster(
441
+ org: str | None = None,
442
+ project: str | None = None,
443
+ domain: str | None = None,
444
+ api_key: str | None = None,
445
+ endpoint: str | None = None,
446
+ insecure: bool = False,
447
+ ) -> dict[str, typing.Any]:
448
+ import os
449
+
450
+ from flyte._utils import str2bool
451
+
452
+ PROJECT_NAME = "FLYTE_INTERNAL_EXECUTION_PROJECT"
453
+ DOMAIN_NAME = "FLYTE_INTERNAL_EXECUTION_DOMAIN"
454
+ ORG_NAME = "_U_ORG_NAME"
455
+ ENDPOINT_OVERRIDE = "_U_EP_OVERRIDE"
456
+ INSECURE_SKIP_VERIFY_OVERRIDE = "_U_INSECURE_SKIP_VERIFY"
457
+ INSECURE_OVERRIDE = "_U_INSECURE"
458
+ _UNION_EAGER_API_KEY_ENV_VAR = "_UNION_EAGER_API_KEY"
459
+ EAGER_API_KEY = "EAGER_API_KEY"
460
+
461
+ org = org or os.getenv(ORG_NAME)
462
+ project = project or os.getenv(PROJECT_NAME)
463
+ domain = domain or os.getenv(DOMAIN_NAME)
464
+ api_key = api_key or os.getenv(_UNION_EAGER_API_KEY_ENV_VAR) or os.getenv(EAGER_API_KEY)
465
+
466
+ remote_kwargs: dict[str, typing.Any] = {"insecure": insecure}
467
+ if api_key:
468
+ logger.info("Using api key from environment")
469
+ remote_kwargs["api_key"] = api_key
470
+ else:
471
+ ep = endpoint or os.environ.get(ENDPOINT_OVERRIDE, "host.docker.internal:8090")
472
+ remote_kwargs["endpoint"] = ep
473
+ if not insecure:
474
+ if "localhost" in ep or "docker" in ep:
475
+ remote_kwargs["insecure"] = True
476
+ if str2bool(os.getenv(INSECURE_OVERRIDE, "")):
477
+ remote_kwargs["insecure"] = True
478
+ logger.debug(f"Using controller endpoint: {ep} with kwargs: {remote_kwargs}")
479
+
480
+ # Check for insecure_skip_verify override (e.g. for self-signed certs)
481
+ insecure_skip_verify_str = os.getenv(INSECURE_SKIP_VERIFY_OVERRIDE, "")
482
+ if str2bool(insecure_skip_verify_str):
483
+ remote_kwargs["insecure_skip_verify"] = True
484
+ logger.info("SSL certificate verification disabled (insecure_skip_verify=True)")
485
+
486
+ await init.aio(
487
+ org=org, project=project, domain=domain, root_dir=Path.cwd(), image_builder="remote", **remote_kwargs
488
+ )
489
+ return remote_kwargs
490
+
491
+
492
+ @syncify
493
+ async def init_passthrough(
494
+ endpoint: str | None = None,
495
+ org: str | None = None,
496
+ project: str | None = None,
497
+ domain: str | None = None,
498
+ insecure: bool = False,
499
+ ) -> dict[str, typing.Any]:
500
+ """
501
+ Initialize the Flyte system with passthrough authentication.
502
+
503
+ This authentication mode allows you to pass custom authentication metadata
504
+ using the `flyte.remote.auth_metadata()` context manager.
505
+
506
+ :param org: Optional organization name
507
+ :param project: Optional project name
508
+ :param domain: Optional domain name
509
+ :param endpoint: Optional API endpoint URL
510
+ :param insecure: Whether to use an insecure channel
511
+ :return: Dictionary of remote kwargs used for initialization
512
+ """
513
+ await init.aio(
514
+ org=org,
515
+ project=project,
516
+ domain=domain,
517
+ root_dir=Path.cwd(),
518
+ image_builder="remote",
519
+ endpoint=endpoint,
520
+ insecure=insecure,
521
+ auth_type="Passthrough",
522
+ )
523
+ return {"endpoint": endpoint, "insecure": insecure}
524
+
525
+
434
526
  def _get_init_config() -> Optional[_InitConfig]:
435
527
  """
436
528
  Get the current initialization configuration. Thread-safe implementation.
@@ -441,7 +533,7 @@ def _get_init_config() -> Optional[_InitConfig]:
441
533
  return _init_config
442
534
 
443
535
 
444
- def get_common_config() -> CommonInit:
536
+ def get_init_config() -> _InitConfig:
445
537
  """
446
538
  Get the current initialization configuration. Thread-safe implementation.
447
539
 
@@ -450,15 +542,15 @@ def get_common_config() -> CommonInit:
450
542
  cfg = _get_init_config()
451
543
  if cfg is None:
452
544
  raise InitializationError(
453
- "StorageNotInitializedError",
545
+ "ClientNotInitializedError",
454
546
  "user",
455
- "Configuration has not been initialized. Call flyte.init() with a valid endpoint or",
456
- " api-key before using this function.",
547
+ "Configuration has not been initialized. Call flyte.init() with a valid endpoint/api-key before",
548
+ " using this function or Call flyte.init_from_config() with a valid path to the config file",
457
549
  )
458
550
  return cfg
459
551
 
460
552
 
461
- def get_storage() -> Storage:
553
+ def get_storage() -> Storage | None:
462
554
  """
463
555
  Get the current storage configuration. Thread-safe implementation.
464
556
 
@@ -469,12 +561,10 @@ def get_storage() -> Storage:
469
561
  raise InitializationError(
470
562
  "StorageNotInitializedError",
471
563
  "user",
472
- "Configuration has not been initialized. Call flyte.init() with a valid endpoint or",
473
- " api-key before using this function.",
564
+ "Configuration has not been initialized. Call flyte.init() with a valid"
565
+ " storage configuration before using this function or Call flyte.init_from_config()"
566
+ " with a valid path to the config file",
474
567
  )
475
- if cfg.storage is None:
476
- # return default local storage
477
- return typing.cast(Storage, cfg.replace(storage=Storage()).storage)
478
568
  return cfg.storage
479
569
 
480
570
 
@@ -489,8 +579,8 @@ def get_client() -> ClientSet:
489
579
  raise InitializationError(
490
580
  "ClientNotInitializedError",
491
581
  "user",
492
- "Client has not been initialized. Call flyte.init() with a valid endpoint or"
493
- " api-key before using this function.",
582
+ "Client has not been initialized. Call flyte.init() with a valid endpoint/api-key "
583
+ "before using this function or Call flyte.init_from_config() with a valid path to the config file",
494
584
  )
495
585
  return cfg.client
496
586
 
@@ -504,41 +594,31 @@ def is_initialized() -> bool:
504
594
  return _get_init_config() is not None
505
595
 
506
596
 
507
- def initialize_in_cluster(storage: Storage | None = None) -> None:
597
+ def initialize_in_cluster() -> None:
508
598
  """
509
599
  Initialize the system for in-cluster execution. This is a placeholder function and does not perform any actions.
510
600
 
511
601
  :return: None
512
602
  """
513
- init(storage=storage)
603
+ init()
514
604
 
515
605
 
516
606
  # Define a generic type variable for the decorated function
517
607
  T = TypeVar("T", bound=Callable)
518
608
 
519
609
 
520
- def requires_client(func: T) -> T:
610
+ def ensure_client():
521
611
  """
522
- Decorator that checks if the client has been initialized before executing the function.
523
- Raises InitializationError if the client is not initialized.
524
-
525
- :param func: Function to decorate
526
- :return: Decorated function that checks for initialization
612
+ Ensure that the client is initialized. If not, raise an InitializationError.
613
+ This function is used to check if the client is initialized before executing any Flyte remote API methods.
527
614
  """
528
-
529
- @functools.wraps(func)
530
- def wrapper(*args, **kwargs) -> T:
531
- init_config = _get_init_config()
532
- if init_config is None or init_config.client is None:
533
- raise InitializationError(
534
- "ClientNotInitializedError",
535
- "user",
536
- f"Function '{func.__name__}' requires client to be initialized. "
537
- f"Call flyte.init() with a valid endpoint or api-key before using this function.",
538
- )
539
- return func(*args, **kwargs)
540
-
541
- return typing.cast(T, wrapper)
615
+ if _get_init_config() is None or _get_init_config().client is None:
616
+ raise InitializationError(
617
+ "ClientNotInitializedError",
618
+ "user",
619
+ "Client has not been initialized. Call flyte.init() with a valid endpoint/api-key before using"
620
+ " this function or Call flyte.init_from_config() with a valid path to the config file",
621
+ )
542
622
 
543
623
 
544
624
  def requires_storage(func: T) -> T:
@@ -557,7 +637,8 @@ def requires_storage(func: T) -> T:
557
637
  "StorageNotInitializedError",
558
638
  "user",
559
639
  f"Function '{func.__name__}' requires storage to be initialized. "
560
- f"Call flyte.init() with a valid storage configuration before using this function.",
640
+ "Call flyte.init() with a valid storage configuration before using this function."
641
+ "or Call flyte.init_from_config() with a valid path to the config file",
561
642
  )
562
643
  return func(*args, **kwargs)
563
644
 
@@ -583,7 +664,8 @@ def requires_upload_location(func: T) -> T:
583
664
  "No upload path configured",
584
665
  "user",
585
666
  f"Function '{func.__name__}' requires client to be initialized. "
586
- f"Call flyte.init() with storage configuration before using this function.",
667
+ "Call flyte.init() with storage configuration before using this function."
668
+ "or Call flyte.init_from_config() with a valid path to the config file."
587
669
  )
588
670
  return func(*args, **kwargs)
589
671
 
@@ -605,13 +687,42 @@ def requires_initialization(func: T) -> T:
605
687
  raise InitializationError(
606
688
  "NotInitConfiguredError",
607
689
  "user",
608
- f"Function '{func.__name__}' requires initialization. Call flyte.init() before using this function.",
690
+ f"Function '{func.__name__}' requires initialization. Call flyte.init() before using this function"
691
+ " or Call flyte.init_from_config() with a valid path to the config file."
609
692
  )
610
693
  return func(*args, **kwargs)
611
694
 
612
695
  return typing.cast(T, wrapper)
613
696
 
614
697
 
698
+ def require_project_and_domain(func):
699
+ """
700
+ Decorator that ensures the current Flyte configuration defines
701
+ both 'project' and 'domain'. Raises a clear error if not found.
702
+ """
703
+
704
+ @functools.wraps(func)
705
+ def wrapper(*args, **kwargs):
706
+ cfg = get_init_config()
707
+ if cfg.project is None:
708
+ raise ValueError(
709
+ "Project must be provided to initialize the client. "
710
+ "Please set 'project' in the 'task' section of your config file, "
711
+ "or pass it directly to flyte.init(project='your-project-name')."
712
+ )
713
+
714
+ if cfg.domain is None:
715
+ raise ValueError(
716
+ "Domain must be provided to initialize the client. "
717
+ "Please set 'domain' in the 'task' section of your config file, "
718
+ "or pass it directly to flyte.init(domain='your-domain-name')."
719
+ )
720
+
721
+ return func(*args, **kwargs)
722
+
723
+ return wrapper
724
+
725
+
615
726
  async def _init_for_testing(
616
727
  project: str | None = None,
617
728
  domain: str | None = None,
@@ -641,3 +752,32 @@ def replace_client(client):
641
752
 
642
753
  with _init_lock:
643
754
  _init_config = _init_config.replace(client=client)
755
+
756
+
757
+ def current_domain() -> str:
758
+ """
759
+ Returns the current domain from Runtime environment (on the cluster) or from the initialized configuration.
760
+ This is safe to be used during `deploy`, `run` and within `task` code.
761
+
762
+ NOTE: This will not work if you deploy a task to a domain and then run it in another domain.
763
+
764
+ Raises InitializationError if the configuration is not initialized or domain is not set.
765
+ :return: The current domain
766
+ """
767
+ from ._context import ctx
768
+
769
+ tctx = ctx()
770
+ if tctx is not None:
771
+ domain = tctx.action.domain
772
+ if domain is not None:
773
+ return domain
774
+
775
+ cfg = _get_init_config()
776
+ if cfg is None or cfg.domain is None:
777
+ raise InitializationError(
778
+ "DomainNotInitializedError",
779
+ "user",
780
+ "Domain has not been initialized. Call flyte.init() with a valid domain before using this function"
781
+ " or Call flyte.init_from_config() with a valid path to the config file"
782
+ )
783
+ return cfg.domain