flyte 0.1.0__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 (219) hide show
  1. flyte/__init__.py +78 -2
  2. flyte/_bin/__init__.py +0 -0
  3. flyte/_bin/runtime.py +152 -0
  4. flyte/_build.py +26 -0
  5. flyte/_cache/__init__.py +12 -0
  6. flyte/_cache/cache.py +145 -0
  7. flyte/_cache/defaults.py +9 -0
  8. flyte/_cache/policy_function_body.py +42 -0
  9. flyte/_code_bundle/__init__.py +8 -0
  10. flyte/_code_bundle/_ignore.py +113 -0
  11. flyte/_code_bundle/_packaging.py +187 -0
  12. flyte/_code_bundle/_utils.py +323 -0
  13. flyte/_code_bundle/bundle.py +209 -0
  14. flyte/_context.py +152 -0
  15. flyte/_deploy.py +243 -0
  16. flyte/_doc.py +29 -0
  17. flyte/_docstring.py +32 -0
  18. flyte/_environment.py +84 -0
  19. flyte/_excepthook.py +37 -0
  20. flyte/_group.py +32 -0
  21. flyte/_hash.py +23 -0
  22. flyte/_image.py +762 -0
  23. flyte/_initialize.py +492 -0
  24. flyte/_interface.py +84 -0
  25. flyte/_internal/__init__.py +3 -0
  26. flyte/_internal/controllers/__init__.py +128 -0
  27. flyte/_internal/controllers/_local_controller.py +193 -0
  28. flyte/_internal/controllers/_trace.py +41 -0
  29. flyte/_internal/controllers/remote/__init__.py +60 -0
  30. flyte/_internal/controllers/remote/_action.py +146 -0
  31. flyte/_internal/controllers/remote/_client.py +47 -0
  32. flyte/_internal/controllers/remote/_controller.py +494 -0
  33. flyte/_internal/controllers/remote/_core.py +410 -0
  34. flyte/_internal/controllers/remote/_informer.py +361 -0
  35. flyte/_internal/controllers/remote/_service_protocol.py +50 -0
  36. flyte/_internal/imagebuild/__init__.py +11 -0
  37. flyte/_internal/imagebuild/docker_builder.py +427 -0
  38. flyte/_internal/imagebuild/image_builder.py +246 -0
  39. flyte/_internal/imagebuild/remote_builder.py +0 -0
  40. flyte/_internal/resolvers/__init__.py +0 -0
  41. flyte/_internal/resolvers/_task_module.py +54 -0
  42. flyte/_internal/resolvers/common.py +31 -0
  43. flyte/_internal/resolvers/default.py +28 -0
  44. flyte/_internal/runtime/__init__.py +0 -0
  45. flyte/_internal/runtime/convert.py +342 -0
  46. flyte/_internal/runtime/entrypoints.py +135 -0
  47. flyte/_internal/runtime/io.py +136 -0
  48. flyte/_internal/runtime/resources_serde.py +138 -0
  49. flyte/_internal/runtime/task_serde.py +330 -0
  50. flyte/_internal/runtime/taskrunner.py +191 -0
  51. flyte/_internal/runtime/types_serde.py +54 -0
  52. flyte/_logging.py +135 -0
  53. flyte/_map.py +215 -0
  54. flyte/_pod.py +19 -0
  55. flyte/_protos/__init__.py +0 -0
  56. flyte/_protos/common/authorization_pb2.py +66 -0
  57. flyte/_protos/common/authorization_pb2.pyi +108 -0
  58. flyte/_protos/common/authorization_pb2_grpc.py +4 -0
  59. flyte/_protos/common/identifier_pb2.py +71 -0
  60. flyte/_protos/common/identifier_pb2.pyi +82 -0
  61. flyte/_protos/common/identifier_pb2_grpc.py +4 -0
  62. flyte/_protos/common/identity_pb2.py +48 -0
  63. flyte/_protos/common/identity_pb2.pyi +72 -0
  64. flyte/_protos/common/identity_pb2_grpc.py +4 -0
  65. flyte/_protos/common/list_pb2.py +36 -0
  66. flyte/_protos/common/list_pb2.pyi +71 -0
  67. flyte/_protos/common/list_pb2_grpc.py +4 -0
  68. flyte/_protos/common/policy_pb2.py +37 -0
  69. flyte/_protos/common/policy_pb2.pyi +27 -0
  70. flyte/_protos/common/policy_pb2_grpc.py +4 -0
  71. flyte/_protos/common/role_pb2.py +37 -0
  72. flyte/_protos/common/role_pb2.pyi +53 -0
  73. flyte/_protos/common/role_pb2_grpc.py +4 -0
  74. flyte/_protos/common/runtime_version_pb2.py +28 -0
  75. flyte/_protos/common/runtime_version_pb2.pyi +24 -0
  76. flyte/_protos/common/runtime_version_pb2_grpc.py +4 -0
  77. flyte/_protos/logs/dataplane/payload_pb2.py +100 -0
  78. flyte/_protos/logs/dataplane/payload_pb2.pyi +177 -0
  79. flyte/_protos/logs/dataplane/payload_pb2_grpc.py +4 -0
  80. flyte/_protos/secret/definition_pb2.py +49 -0
  81. flyte/_protos/secret/definition_pb2.pyi +93 -0
  82. flyte/_protos/secret/definition_pb2_grpc.py +4 -0
  83. flyte/_protos/secret/payload_pb2.py +62 -0
  84. flyte/_protos/secret/payload_pb2.pyi +94 -0
  85. flyte/_protos/secret/payload_pb2_grpc.py +4 -0
  86. flyte/_protos/secret/secret_pb2.py +38 -0
  87. flyte/_protos/secret/secret_pb2.pyi +6 -0
  88. flyte/_protos/secret/secret_pb2_grpc.py +198 -0
  89. flyte/_protos/secret/secret_pb2_grpc_grpc.py +198 -0
  90. flyte/_protos/validate/validate/validate_pb2.py +76 -0
  91. flyte/_protos/workflow/common_pb2.py +27 -0
  92. flyte/_protos/workflow/common_pb2.pyi +14 -0
  93. flyte/_protos/workflow/common_pb2_grpc.py +4 -0
  94. flyte/_protos/workflow/environment_pb2.py +29 -0
  95. flyte/_protos/workflow/environment_pb2.pyi +12 -0
  96. flyte/_protos/workflow/environment_pb2_grpc.py +4 -0
  97. flyte/_protos/workflow/node_execution_service_pb2.py +26 -0
  98. flyte/_protos/workflow/node_execution_service_pb2.pyi +4 -0
  99. flyte/_protos/workflow/node_execution_service_pb2_grpc.py +32 -0
  100. flyte/_protos/workflow/queue_service_pb2.py +105 -0
  101. flyte/_protos/workflow/queue_service_pb2.pyi +146 -0
  102. flyte/_protos/workflow/queue_service_pb2_grpc.py +172 -0
  103. flyte/_protos/workflow/run_definition_pb2.py +128 -0
  104. flyte/_protos/workflow/run_definition_pb2.pyi +314 -0
  105. flyte/_protos/workflow/run_definition_pb2_grpc.py +4 -0
  106. flyte/_protos/workflow/run_logs_service_pb2.py +41 -0
  107. flyte/_protos/workflow/run_logs_service_pb2.pyi +28 -0
  108. flyte/_protos/workflow/run_logs_service_pb2_grpc.py +69 -0
  109. flyte/_protos/workflow/run_service_pb2.py +129 -0
  110. flyte/_protos/workflow/run_service_pb2.pyi +171 -0
  111. flyte/_protos/workflow/run_service_pb2_grpc.py +412 -0
  112. flyte/_protos/workflow/state_service_pb2.py +66 -0
  113. flyte/_protos/workflow/state_service_pb2.pyi +75 -0
  114. flyte/_protos/workflow/state_service_pb2_grpc.py +138 -0
  115. flyte/_protos/workflow/task_definition_pb2.py +79 -0
  116. flyte/_protos/workflow/task_definition_pb2.pyi +81 -0
  117. flyte/_protos/workflow/task_definition_pb2_grpc.py +4 -0
  118. flyte/_protos/workflow/task_service_pb2.py +60 -0
  119. flyte/_protos/workflow/task_service_pb2.pyi +59 -0
  120. flyte/_protos/workflow/task_service_pb2_grpc.py +138 -0
  121. flyte/_resources.py +226 -0
  122. flyte/_retry.py +32 -0
  123. flyte/_reusable_environment.py +25 -0
  124. flyte/_run.py +482 -0
  125. flyte/_secret.py +61 -0
  126. flyte/_task.py +449 -0
  127. flyte/_task_environment.py +183 -0
  128. flyte/_timeout.py +47 -0
  129. flyte/_tools.py +27 -0
  130. flyte/_trace.py +120 -0
  131. flyte/_utils/__init__.py +26 -0
  132. flyte/_utils/asyn.py +119 -0
  133. flyte/_utils/async_cache.py +139 -0
  134. flyte/_utils/coro_management.py +23 -0
  135. flyte/_utils/file_handling.py +72 -0
  136. flyte/_utils/helpers.py +134 -0
  137. flyte/_utils/lazy_module.py +54 -0
  138. flyte/_utils/org_discovery.py +57 -0
  139. flyte/_utils/uv_script_parser.py +49 -0
  140. flyte/_version.py +21 -0
  141. flyte/cli/__init__.py +3 -0
  142. flyte/cli/_abort.py +28 -0
  143. flyte/cli/_common.py +337 -0
  144. flyte/cli/_create.py +145 -0
  145. flyte/cli/_delete.py +23 -0
  146. flyte/cli/_deploy.py +152 -0
  147. flyte/cli/_gen.py +163 -0
  148. flyte/cli/_get.py +310 -0
  149. flyte/cli/_params.py +538 -0
  150. flyte/cli/_run.py +231 -0
  151. flyte/cli/main.py +166 -0
  152. flyte/config/__init__.py +3 -0
  153. flyte/config/_config.py +216 -0
  154. flyte/config/_internal.py +64 -0
  155. flyte/config/_reader.py +207 -0
  156. flyte/connectors/__init__.py +0 -0
  157. flyte/errors.py +172 -0
  158. flyte/extras/__init__.py +5 -0
  159. flyte/extras/_container.py +263 -0
  160. flyte/io/__init__.py +27 -0
  161. flyte/io/_dir.py +448 -0
  162. flyte/io/_file.py +467 -0
  163. flyte/io/_structured_dataset/__init__.py +129 -0
  164. flyte/io/_structured_dataset/basic_dfs.py +219 -0
  165. flyte/io/_structured_dataset/structured_dataset.py +1061 -0
  166. flyte/models.py +391 -0
  167. flyte/remote/__init__.py +26 -0
  168. flyte/remote/_client/__init__.py +0 -0
  169. flyte/remote/_client/_protocols.py +133 -0
  170. flyte/remote/_client/auth/__init__.py +12 -0
  171. flyte/remote/_client/auth/_auth_utils.py +14 -0
  172. flyte/remote/_client/auth/_authenticators/__init__.py +0 -0
  173. flyte/remote/_client/auth/_authenticators/base.py +397 -0
  174. flyte/remote/_client/auth/_authenticators/client_credentials.py +73 -0
  175. flyte/remote/_client/auth/_authenticators/device_code.py +118 -0
  176. flyte/remote/_client/auth/_authenticators/external_command.py +79 -0
  177. flyte/remote/_client/auth/_authenticators/factory.py +200 -0
  178. flyte/remote/_client/auth/_authenticators/pkce.py +516 -0
  179. flyte/remote/_client/auth/_channel.py +215 -0
  180. flyte/remote/_client/auth/_client_config.py +83 -0
  181. flyte/remote/_client/auth/_default_html.py +32 -0
  182. flyte/remote/_client/auth/_grpc_utils/__init__.py +0 -0
  183. flyte/remote/_client/auth/_grpc_utils/auth_interceptor.py +288 -0
  184. flyte/remote/_client/auth/_grpc_utils/default_metadata_interceptor.py +151 -0
  185. flyte/remote/_client/auth/_keyring.py +143 -0
  186. flyte/remote/_client/auth/_token_client.py +260 -0
  187. flyte/remote/_client/auth/errors.py +16 -0
  188. flyte/remote/_client/controlplane.py +95 -0
  189. flyte/remote/_console.py +18 -0
  190. flyte/remote/_data.py +159 -0
  191. flyte/remote/_logs.py +176 -0
  192. flyte/remote/_project.py +85 -0
  193. flyte/remote/_run.py +970 -0
  194. flyte/remote/_secret.py +132 -0
  195. flyte/remote/_task.py +391 -0
  196. flyte/report/__init__.py +3 -0
  197. flyte/report/_report.py +178 -0
  198. flyte/report/_template.html +124 -0
  199. flyte/storage/__init__.py +29 -0
  200. flyte/storage/_config.py +233 -0
  201. flyte/storage/_remote_fs.py +34 -0
  202. flyte/storage/_storage.py +271 -0
  203. flyte/storage/_utils.py +5 -0
  204. flyte/syncify/__init__.py +56 -0
  205. flyte/syncify/_api.py +371 -0
  206. flyte/types/__init__.py +36 -0
  207. flyte/types/_interface.py +40 -0
  208. flyte/types/_pickle.py +118 -0
  209. flyte/types/_renderer.py +162 -0
  210. flyte/types/_string_literals.py +120 -0
  211. flyte/types/_type_engine.py +2287 -0
  212. flyte/types/_utils.py +80 -0
  213. flyte-0.2.0a0.dist-info/METADATA +249 -0
  214. flyte-0.2.0a0.dist-info/RECORD +218 -0
  215. {flyte-0.1.0.dist-info → flyte-0.2.0a0.dist-info}/WHEEL +2 -1
  216. flyte-0.2.0a0.dist-info/entry_points.txt +3 -0
  217. flyte-0.2.0a0.dist-info/top_level.txt +1 -0
  218. flyte-0.1.0.dist-info/METADATA +0 -6
  219. flyte-0.1.0.dist-info/RECORD +0 -5
flyte/_run.py ADDED
@@ -0,0 +1,482 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import pathlib
5
+ import uuid
6
+ from typing import TYPE_CHECKING, Any, Literal, Optional, Tuple, Union, cast
7
+
8
+ import flyte.errors
9
+ from flyte.errors import InitializationError
10
+ from flyte.models import ActionID, Checkpoints, CodeBundle, RawDataPath, SerializationContext, TaskContext
11
+ from flyte.syncify import syncify
12
+
13
+ from ._context import contextual_run, internal_ctx
14
+ from ._environment import Environment
15
+ from ._initialize import (
16
+ _get_init_config,
17
+ get_client,
18
+ get_common_config,
19
+ get_storage,
20
+ requires_initialization,
21
+ requires_storage,
22
+ )
23
+ from ._logging import logger
24
+ from ._task import P, R, TaskTemplate
25
+ from ._tools import ipython_check
26
+
27
+ if TYPE_CHECKING:
28
+ from flyte.remote import Run
29
+ from flyte.remote._task import LazyEntity
30
+
31
+ from ._code_bundle import CopyFiles
32
+
33
+ Mode = Literal["local", "remote", "hybrid"]
34
+
35
+
36
+ async def _get_code_bundle_for_run(name: str) -> CodeBundle | None:
37
+ """
38
+ Get the code bundle for the run with the given name.
39
+ This is used to get the code bundle for the run when running in hybrid mode.
40
+ """
41
+ from flyte._internal.runtime.task_serde import extract_code_bundle
42
+ from flyte.remote import Run
43
+
44
+ run = await Run.get.aio(name=name)
45
+ if run:
46
+ run_details = await run.details()
47
+ spec = run_details.action_details.pb2.resolved_task_spec
48
+ return extract_code_bundle(spec)
49
+ return None
50
+
51
+
52
+ class _Runner:
53
+ def __init__(
54
+ self,
55
+ force_mode: Mode | None = None,
56
+ name: Optional[str] = None,
57
+ service_account: Optional[str] = None,
58
+ version: Optional[str] = None,
59
+ copy_style: CopyFiles = "loaded_modules",
60
+ dry_run: bool = False,
61
+ copy_bundle_to: pathlib.Path | None = None,
62
+ interactive_mode: bool | None = None,
63
+ raw_data_path: str | None = None,
64
+ metadata_path: str | None = None,
65
+ run_base_dir: str | None = None,
66
+ overwrite_cache: bool = False,
67
+ ):
68
+ init_config = _get_init_config()
69
+ client = init_config.client if init_config else None
70
+ if not force_mode and client is not None:
71
+ force_mode = "remote"
72
+ force_mode = force_mode or "local"
73
+ logger.debug(f"Effective run mode: `{force_mode}`, client configured: `{client is not None}`")
74
+ self._mode = force_mode
75
+ self._name = name
76
+ self._service_account = service_account
77
+ self._version = version
78
+ self._copy_files = copy_style
79
+ self._dry_run = dry_run
80
+ self._copy_bundle_to = copy_bundle_to
81
+ self._interactive_mode = interactive_mode if interactive_mode else ipython_check()
82
+ self._raw_data_path = raw_data_path
83
+ self._metadata_path = metadata_path or "/tmp"
84
+ self._run_base_dir = run_base_dir or "/tmp/base"
85
+ self._overwrite_cache = overwrite_cache
86
+
87
+ @requires_initialization
88
+ async def _run_remote(self, obj: TaskTemplate[P, R] | LazyEntity, *args: P.args, **kwargs: P.kwargs) -> Run:
89
+ import grpc
90
+
91
+ from flyte.remote import Run
92
+ from flyte.remote._task import LazyEntity
93
+
94
+ from ._code_bundle import build_code_bundle, build_pkl_bundle
95
+ from ._deploy import build_images, plan_deploy
96
+ from ._internal.runtime.convert import convert_from_native_to_inputs
97
+ from ._internal.runtime.task_serde import translate_task_to_wire
98
+ from ._protos.common import identifier_pb2
99
+ from ._protos.workflow import run_definition_pb2, run_service_pb2
100
+
101
+ cfg = get_common_config()
102
+
103
+ if isinstance(obj, LazyEntity):
104
+ task = await obj.fetch.aio()
105
+ task_spec = task.pb2.spec
106
+ inputs = await convert_from_native_to_inputs(task.interface, *args, **kwargs)
107
+ version = task.pb2.task_id.version
108
+ code_bundle = None
109
+ else:
110
+ if obj.parent_env is None:
111
+ raise ValueError("Task is not attached to an environment. Please attach the task to an environment")
112
+
113
+ deploy_plan = plan_deploy(cast(Environment, obj.parent_env()))
114
+ image_cache = await build_images(deploy_plan)
115
+
116
+ if self._interactive_mode:
117
+ code_bundle = await build_pkl_bundle(
118
+ obj, upload_to_controlplane=not self._dry_run, copy_bundle_to=self._copy_bundle_to
119
+ )
120
+ else:
121
+ if self._copy_files != "none":
122
+ code_bundle = await build_code_bundle(
123
+ from_dir=cfg.root_dir,
124
+ dryrun=self._dry_run,
125
+ copy_bundle_to=self._copy_bundle_to,
126
+ copy_style=self._copy_files,
127
+ )
128
+ else:
129
+ code_bundle = None
130
+
131
+ version = self._version or (
132
+ code_bundle.computed_version if code_bundle and code_bundle.computed_version else None
133
+ )
134
+ if not version:
135
+ raise ValueError("Version is required when running a task")
136
+ s_ctx = SerializationContext(
137
+ code_bundle=code_bundle,
138
+ version=version,
139
+ image_cache=image_cache,
140
+ root_dir=cfg.root_dir,
141
+ )
142
+ task_spec = translate_task_to_wire(obj, s_ctx)
143
+ inputs = await convert_from_native_to_inputs(obj.native_interface, *args, **kwargs)
144
+
145
+ if not self._dry_run:
146
+ if get_client() is None:
147
+ # This can only happen, if the user forces flyte.run(mode="remote") without initializing the client
148
+ raise InitializationError(
149
+ "ClientNotInitializedError",
150
+ "user",
151
+ "flyte.run requires client to be initialized. "
152
+ "Call flyte.init() with a valid endpoint or api-key before using this function.",
153
+ )
154
+ run_id = None
155
+ project_id = None
156
+ if self._name:
157
+ run_id = run_definition_pb2.RunIdentifier(
158
+ project=cfg.project,
159
+ domain=cfg.domain,
160
+ org=cfg.org,
161
+ name=self._name if self._name else None,
162
+ )
163
+ else:
164
+ project_id = identifier_pb2.ProjectIdentifier(
165
+ name=cfg.project,
166
+ domain=cfg.domain,
167
+ organization=cfg.org,
168
+ )
169
+ # Fill in task id inside the task template if it's not provided.
170
+ # Maybe this should be done here, or the backend.
171
+ if task_spec.task_template.id.project == "":
172
+ task_spec.task_template.id.project = cfg.project if cfg.project else ""
173
+ if task_spec.task_template.id.domain == "":
174
+ task_spec.task_template.id.domain = cfg.domain if cfg.domain else ""
175
+ if task_spec.task_template.id.org == "":
176
+ task_spec.task_template.id.org = cfg.org if cfg.org else ""
177
+ if task_spec.task_template.id.version == "":
178
+ task_spec.task_template.id.version = version
179
+
180
+ try:
181
+ resp = await get_client().run_service.CreateRun(
182
+ run_service_pb2.CreateRunRequest(
183
+ run_id=run_id,
184
+ project_id=project_id,
185
+ task_spec=task_spec,
186
+ inputs=inputs.proto_inputs,
187
+ run_spec=run_definition_pb2.RunSpec(
188
+ overwrite_cache=self._overwrite_cache,
189
+ ),
190
+ ),
191
+ )
192
+ return Run(pb2=resp.run)
193
+ except grpc.aio.AioRpcError as e:
194
+ if e.code() == grpc.StatusCode.UNAVAILABLE:
195
+ raise flyte.errors.RuntimeSystemError(
196
+ "SystemUnavailableError",
197
+ "Flyte system is currently unavailable. check your configuration, or the service status.",
198
+ ) from e
199
+ elif e.code() == grpc.StatusCode.INVALID_ARGUMENT:
200
+ raise flyte.errors.RuntimeUserError("InvalidArgumentError", e.details())
201
+ elif e.code() == grpc.StatusCode.ALREADY_EXISTS:
202
+ # TODO maybe this should be a pass and return existing run?
203
+ raise flyte.errors.RuntimeUserError(
204
+ "RunAlreadyExistsError",
205
+ f"A run with the name '{self._name}' already exists. Please choose a different name.",
206
+ )
207
+ else:
208
+ raise flyte.errors.RuntimeSystemError(
209
+ "RunCreationError",
210
+ f"Failed to create run: {e.details()}",
211
+ ) from e
212
+
213
+ class DryRun(Run):
214
+ def __init__(self, _task_spec, _inputs, _code_bundle):
215
+ super().__init__(
216
+ pb2=run_definition_pb2.Run(
217
+ action=run_definition_pb2.Action(
218
+ id=run_definition_pb2.ActionIdentifier(
219
+ name="a0", run=run_definition_pb2.RunIdentifier(name="dry-run")
220
+ )
221
+ )
222
+ )
223
+ )
224
+ self.task_spec = _task_spec
225
+ self.inputs = _inputs
226
+ self.code_bundle = _code_bundle
227
+
228
+ return DryRun(_task_spec=task_spec, _inputs=inputs, _code_bundle=code_bundle)
229
+
230
+ @requires_storage
231
+ @requires_initialization
232
+ async def _run_hybrid(self, obj: TaskTemplate[P, R], *args: P.args, **kwargs: P.kwargs) -> R:
233
+ """
234
+ Run a task in hybrid mode. This means that the parent action will be run locally, but the child actions will be
235
+ run in the cluster remotely. This is currently only used for testing,
236
+ over the longer term we will productize this.
237
+ """
238
+ import flyte.report
239
+ from flyte._code_bundle import build_code_bundle, build_pkl_bundle
240
+ from flyte._deploy import build_images, plan_deploy
241
+ from flyte.models import RawDataPath
242
+ from flyte.storage import ABFS, GCS, S3
243
+
244
+ from ._internal import create_controller
245
+ from ._internal.runtime.taskrunner import run_task
246
+
247
+ cfg = get_common_config()
248
+
249
+ if obj.parent_env is None:
250
+ raise ValueError("Task is not attached to an environment. Please attach the task to an environment.")
251
+
252
+ deploy_plan = plan_deploy(cast(Environment, obj.parent_env()))
253
+ image_cache = await build_images(deploy_plan)
254
+
255
+ code_bundle = None
256
+ if self._name is not None:
257
+ # Check if remote run service has this run name already and if exists, then extract the code bundle from it.
258
+ code_bundle = await _get_code_bundle_for_run(name=self._name)
259
+
260
+ if not code_bundle:
261
+ if self._interactive_mode:
262
+ code_bundle = await build_pkl_bundle(
263
+ obj, upload_to_controlplane=not self._dry_run, copy_bundle_to=self._copy_bundle_to
264
+ )
265
+ else:
266
+ if self._copy_files != "none":
267
+ code_bundle = await build_code_bundle(
268
+ from_dir=cfg.root_dir,
269
+ dryrun=self._dry_run,
270
+ copy_bundle_to=self._copy_bundle_to,
271
+ copy_style=self._copy_files,
272
+ )
273
+ else:
274
+ code_bundle = None
275
+
276
+ version = self._version or (
277
+ code_bundle.computed_version if code_bundle and code_bundle.computed_version else None
278
+ )
279
+ if not version:
280
+ raise ValueError("Version is required when running a task")
281
+
282
+ project = cfg.project
283
+ domain = cfg.domain
284
+ org = cfg.org
285
+ action_name = "a0"
286
+ run_name = self._name
287
+ random_id = str(uuid.uuid4())[:6]
288
+
289
+ controller = create_controller("remote", endpoint="localhost:8090", insecure=True)
290
+ action = ActionID(name=action_name, run_name=run_name, project=project, domain=domain, org=org)
291
+
292
+ inputs = obj.native_interface.convert_to_kwargs(*args, **kwargs)
293
+ # TODO: Ideally we should get this from runService
294
+ # The API should be:
295
+ # create new run, from run, in mode hybrid -> new run id, output_base, raw_data_path, inputs_path
296
+ storage = get_storage()
297
+ if type(storage) not in (S3, GCS, ABFS):
298
+ raise ValueError(f"Unsupported storage type: {type(storage)}")
299
+ if self._run_base_dir is None:
300
+ raise ValueError(
301
+ "Raw data path is required when running task, please set it in the run context:",
302
+ " flyte.with_runcontext(run_base_dir='s3://bucket/metadata/outputs')",
303
+ )
304
+ output_path = self._run_base_dir
305
+ raw_data_path = f"{output_path}/rd/{random_id}"
306
+ raw_data_path_obj = RawDataPath(path=raw_data_path)
307
+ checkpoint_path = f"{raw_data_path}/checkpoint"
308
+ prev_checkpoint = f"{raw_data_path}/prev_checkpoint"
309
+ checkpoints = Checkpoints(checkpoint_path, prev_checkpoint)
310
+
311
+ async def _run_task() -> Tuple[Any, Optional[Exception]]:
312
+ ctx = internal_ctx()
313
+ tctx = TaskContext(
314
+ action=action,
315
+ checkpoints=checkpoints,
316
+ code_bundle=code_bundle,
317
+ output_path=output_path,
318
+ version=version if version else "na",
319
+ raw_data_path=raw_data_path_obj,
320
+ compiled_image_cache=image_cache,
321
+ run_base_dir=self._run_base_dir,
322
+ report=flyte.report.Report(name=action.name),
323
+ )
324
+ async with ctx.replace_task_context(tctx):
325
+ return await run_task(tctx=tctx, controller=controller, task=obj, inputs=inputs)
326
+
327
+ outputs, err = await contextual_run(_run_task)
328
+ if err:
329
+ raise err
330
+ return outputs
331
+
332
+ async def _run_local(self, obj: TaskTemplate[P, R], *args: P.args, **kwargs: P.kwargs) -> R:
333
+ from flyte._internal.controllers import create_controller
334
+ from flyte._internal.controllers._local_controller import LocalController
335
+ from flyte.report import Report
336
+
337
+ controller = cast(LocalController, create_controller("local"))
338
+
339
+ if self._name is None:
340
+ action = ActionID.create_random()
341
+ else:
342
+ action = ActionID(name=self._name)
343
+
344
+ ctx = internal_ctx()
345
+ tctx = TaskContext(
346
+ action=action,
347
+ checkpoints=Checkpoints(
348
+ prev_checkpoint_path=internal_ctx().raw_data.path, checkpoint_path=internal_ctx().raw_data.path
349
+ ),
350
+ code_bundle=None,
351
+ output_path=self._metadata_path,
352
+ run_base_dir=self._metadata_path,
353
+ version="na",
354
+ raw_data_path=internal_ctx().raw_data,
355
+ compiled_image_cache=None,
356
+ report=Report(name=action.name),
357
+ mode="local",
358
+ )
359
+ with ctx.replace_task_context(tctx):
360
+ # make the local version always runs on a different thread, returns a wrapped future.
361
+ if obj._call_as_synchronous:
362
+ fut = controller.submit_sync(obj, *args, **kwargs)
363
+ awaitable = asyncio.wrap_future(fut)
364
+ return await awaitable
365
+ else:
366
+ return await controller.submit(obj, *args, **kwargs)
367
+
368
+ @syncify
369
+ async def run(
370
+ self, task: TaskTemplate[P, Union[R, Run]] | LazyEntity, *args: P.args, **kwargs: P.kwargs
371
+ ) -> Union[R, Run]:
372
+ """
373
+ Run an async `@env.task` or `TaskTemplate` instance. The existing async context will be used.
374
+
375
+ Example:
376
+ ```python
377
+ import flyte
378
+ env = flyte.TaskEnvironment("example")
379
+
380
+ @env.task
381
+ async def example_task(x: int, y: str) -> str:
382
+ return f"{x} {y}"
383
+
384
+ if __name__ == "__main__":
385
+ flyte.run(example_task, 1, y="hello")
386
+ ```
387
+
388
+ :param task: TaskTemplate instance `@env.task` or `TaskTemplate`
389
+ :param args: Arguments to pass to the Task
390
+ :param kwargs: Keyword arguments to pass to the Task
391
+ :return: Run instance or the result of the task
392
+ """
393
+ from flyte.remote._task import LazyEntity
394
+
395
+ if isinstance(task, LazyEntity) and self._mode != "remote":
396
+ raise ValueError("Remote task can only be run in remote mode.")
397
+ if self._mode == "remote":
398
+ return await self._run_remote(task, *args, **kwargs)
399
+ task = cast(TaskTemplate, task)
400
+ if self._mode == "hybrid":
401
+ return await self._run_hybrid(task, *args, **kwargs)
402
+
403
+ # TODO We could use this for remote as well and users could simply pass flyte:// or s3:// or file://
404
+ with internal_ctx().new_raw_data_path(
405
+ raw_data_path=RawDataPath.from_local_folder(local_folder=self._raw_data_path)
406
+ ):
407
+ return await self._run_local(task, *args, **kwargs)
408
+
409
+
410
+ def with_runcontext(
411
+ mode: Mode | None = None,
412
+ *,
413
+ name: Optional[str] = None,
414
+ service_account: Optional[str] = None,
415
+ version: Optional[str] = None,
416
+ copy_style: CopyFiles = "loaded_modules",
417
+ dry_run: bool = False,
418
+ copy_bundle_to: pathlib.Path | None = None,
419
+ interactive_mode: bool | None = None,
420
+ raw_data_path: str | None = None,
421
+ run_base_dir: str | None = None,
422
+ overwrite_cache: bool = False,
423
+ ) -> _Runner:
424
+ """
425
+ Launch a new run with the given parameters as the context.
426
+
427
+ Example:
428
+ ```python
429
+ import flyte
430
+ env = flyte.TaskEnvironment("example")
431
+
432
+ @env.task
433
+ async def example_task(x: int, y: str) -> str:
434
+ return f"{x} {y}"
435
+
436
+ if __name__ == "__main__":
437
+ flyte.with_runcontext(name="example_run_id").run(example_task, 1, y="hello")
438
+ ```
439
+
440
+ :param mode: Optional The mode to use for the run, if not provided, it will be computed from flyte.init
441
+ :param version: Optional The version to use for the run, if not provided, it will be computed from the code bundle
442
+ :param name: Optional The name to use for the run
443
+ :param service_account: Optional The service account to use for the run context
444
+ :param copy_style: Optional The copy style to use for the run context
445
+ :param dry_run: Optional If true, the run will not be executed, but the bundle will be created
446
+ :param copy_bundle_to: When dry_run is True, the bundle will be copied to this location if specified
447
+ :param interactive_mode: Optional, can be forced to True or False.
448
+ If not provided, it will be set based on the current environment. For example Jupyter notebooks are considered
449
+ interactive mode, while scripts are not. This is used to determine how the code bundle is created.
450
+ :param raw_data_path: Use this path to store the raw data for the run. Currently only supported for local runs,
451
+ and can be used to store raw data in specific locations. TODO coming soon for remote runs as well.
452
+ :param run_base_dir: Optional The base directory to use for the run. This is used to store the metadata for the run,
453
+ that is passed between tasks.
454
+ :return: runner
455
+ """
456
+ if mode == "hybrid" and not name and not run_base_dir:
457
+ raise ValueError("Run name and run base dir are required for hybrid mode")
458
+ return _Runner(
459
+ force_mode=mode,
460
+ name=name,
461
+ service_account=service_account,
462
+ version=version,
463
+ copy_style=copy_style,
464
+ dry_run=dry_run,
465
+ copy_bundle_to=copy_bundle_to,
466
+ interactive_mode=interactive_mode,
467
+ raw_data_path=raw_data_path,
468
+ run_base_dir=run_base_dir,
469
+ )
470
+
471
+
472
+ @syncify
473
+ async def run(task: TaskTemplate[P, R], *args: P.args, **kwargs: P.kwargs) -> Union[R, Run]:
474
+ """
475
+ Run a task with the given parameters
476
+ :param task: task to run
477
+ :param args: args to pass to the task
478
+ :param kwargs: kwargs to pass to the task
479
+ :return: Run | Result of the task
480
+ """
481
+ # using syncer causes problems
482
+ return await _Runner().run.aio(task, *args, **kwargs) # type: ignore
flyte/_secret.py ADDED
@@ -0,0 +1,61 @@
1
+ import pathlib
2
+ import re
3
+ from dataclasses import dataclass
4
+ from typing import List, Optional, Union
5
+
6
+
7
+ @dataclass
8
+ class Secret:
9
+ """
10
+ Secrets are used to inject sensitive information into tasks. Secrets can be mounted as environment variables or
11
+ files. The secret key is the name of the secret in the secret store. The group is optional and maybe used with some
12
+ secret stores to organize secrets. The secret_mount is used to specify how the secret should be mounted. If the
13
+ secret_mount is set to "env" the secret will be mounted as an environment variable. If the secret_mount is set to
14
+ "file" the secret will be mounted as a file. The as_env_var is an optional parameter that can be used to specify the
15
+ name of the environment variable that the secret should be mounted as.
16
+
17
+ Example:
18
+ ```python
19
+ @task(secrets="MY_SECRET")
20
+ async def my_task():
21
+ os.environ["MY_SECRET"] # This will be set to the value of the secret
22
+
23
+ @task(secrets=Secret("MY_SECRET", mount="/path/to/secret"))
24
+ async def my_task2():
25
+ async with open("/path/to/secret") as f:
26
+ secret_value = f.read()
27
+ ```
28
+
29
+ TODO: Add support for secret versioning (some stores) and secret groups (some stores) and mounting as files.
30
+
31
+ :param key: The name of the secret in the secret store.
32
+ :param group: The group of the secret in the secret store.
33
+ :param mount: Use this to specify the path where the secret should be mounted.
34
+ :param as_env_var: The name of the environment variable that the secret should be mounted as.
35
+ """
36
+
37
+ key: str
38
+ group: Optional[str] = None
39
+ mount: pathlib.Path | None = None
40
+ as_env_var: Optional[str] = None
41
+
42
+ def __post_init__(self):
43
+ if self.as_env_var is not None:
44
+ pattern = r"^[A-Z_][A-Z0-9_]*$"
45
+ if not re.match(pattern, self.as_env_var):
46
+ raise ValueError(f"Invalid environment variable name: {self.as_env_var}, must match {pattern}")
47
+
48
+
49
+ SecretRequest = Union[str, Secret, List[str | Secret]]
50
+
51
+
52
+ def secrets_from_request(secrets: SecretRequest) -> List[Secret]:
53
+ """
54
+ Converts a secret request into a list of secrets.
55
+ """
56
+ if isinstance(secrets, str):
57
+ return [Secret(key=secrets)]
58
+ elif isinstance(secrets, Secret):
59
+ return [secrets]
60
+ else:
61
+ return [Secret(key=s) if isinstance(s, str) else s for s in secrets]