flyte 0.2.0b8__py3-none-any.whl → 0.2.0b10__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of flyte might be problematic. Click here for more details.
- flyte/__init__.py +4 -2
- flyte/_context.py +7 -1
- flyte/_deploy.py +3 -0
- flyte/_group.py +1 -0
- flyte/_initialize.py +15 -5
- flyte/_internal/controllers/__init__.py +13 -2
- flyte/_internal/controllers/_local_controller.py +67 -5
- flyte/_internal/controllers/remote/_controller.py +47 -2
- flyte/_internal/runtime/taskrunner.py +2 -1
- flyte/_map.py +215 -0
- flyte/_run.py +109 -64
- flyte/_task.py +56 -7
- flyte/_utils/helpers.py +15 -0
- flyte/_version.py +2 -2
- flyte/cli/__init__.py +0 -7
- flyte/cli/_abort.py +1 -1
- flyte/cli/_common.py +7 -7
- flyte/cli/_create.py +44 -29
- flyte/cli/_delete.py +2 -2
- flyte/cli/_deploy.py +3 -3
- flyte/cli/_gen.py +12 -4
- flyte/cli/_get.py +35 -27
- flyte/cli/_params.py +1 -1
- flyte/cli/main.py +32 -29
- flyte/extras/_container.py +29 -32
- flyte/io/__init__.py +17 -1
- flyte/io/_file.py +2 -0
- flyte/io/{structured_dataset → _structured_dataset}/basic_dfs.py +1 -1
- flyte/io/{structured_dataset → _structured_dataset}/structured_dataset.py +1 -1
- flyte/models.py +11 -1
- flyte/syncify/_api.py +43 -15
- flyte/types/__init__.py +23 -0
- flyte/{io/pickle/transformer.py → types/_pickle.py} +2 -1
- flyte/types/_type_engine.py +4 -4
- {flyte-0.2.0b8.dist-info → flyte-0.2.0b10.dist-info}/METADATA +7 -6
- {flyte-0.2.0b8.dist-info → flyte-0.2.0b10.dist-info}/RECORD +40 -41
- flyte/io/_dataframe.py +0 -0
- flyte/io/pickle/__init__.py +0 -0
- /flyte/io/{structured_dataset → _structured_dataset}/__init__.py +0 -0
- {flyte-0.2.0b8.dist-info → flyte-0.2.0b10.dist-info}/WHEEL +0 -0
- {flyte-0.2.0b8.dist-info → flyte-0.2.0b10.dist-info}/entry_points.txt +0 -0
- {flyte-0.2.0b8.dist-info → flyte-0.2.0b10.dist-info}/top_level.txt +0 -0
flyte/_run.py
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import asyncio
|
|
3
4
|
import pathlib
|
|
4
5
|
import uuid
|
|
5
6
|
from typing import TYPE_CHECKING, Any, Literal, Optional, Tuple, Union, cast
|
|
6
7
|
|
|
8
|
+
import flyte.errors
|
|
7
9
|
from flyte.errors import InitializationError
|
|
8
10
|
from flyte.models import ActionID, Checkpoints, CodeBundle, RawDataPath, SerializationContext, TaskContext
|
|
9
11
|
from flyte.syncify import syncify
|
|
@@ -24,6 +26,7 @@ from ._tools import ipython_check
|
|
|
24
26
|
|
|
25
27
|
if TYPE_CHECKING:
|
|
26
28
|
from flyte.remote import Run
|
|
29
|
+
from flyte.remote._task import LazyEntity
|
|
27
30
|
|
|
28
31
|
from ._code_bundle import CopyFiles
|
|
29
32
|
|
|
@@ -80,8 +83,11 @@ class _Runner:
|
|
|
80
83
|
self._run_base_dir = run_base_dir or "/tmp/base"
|
|
81
84
|
|
|
82
85
|
@requires_initialization
|
|
83
|
-
async def _run_remote(self, obj: TaskTemplate[P, R], *args: P.args, **kwargs: P.kwargs) -> Run:
|
|
86
|
+
async def _run_remote(self, obj: TaskTemplate[P, R] | LazyEntity, *args: P.args, **kwargs: P.kwargs) -> Run:
|
|
87
|
+
import grpc
|
|
88
|
+
|
|
84
89
|
from flyte.remote import Run
|
|
90
|
+
from flyte.remote._task import LazyEntity
|
|
85
91
|
|
|
86
92
|
from ._code_bundle import build_code_bundle, build_pkl_bundle
|
|
87
93
|
from ._deploy import build_images, plan_deploy
|
|
@@ -92,37 +98,47 @@ class _Runner:
|
|
|
92
98
|
|
|
93
99
|
cfg = get_common_config()
|
|
94
100
|
|
|
95
|
-
if obj
|
|
96
|
-
|
|
101
|
+
if isinstance(obj, LazyEntity):
|
|
102
|
+
task = await obj.fetch.aio()
|
|
103
|
+
task_spec = task.pb2.spec
|
|
104
|
+
inputs = await convert_from_native_to_inputs(task.interface, *args, **kwargs)
|
|
105
|
+
version = task.pb2.task_id.version
|
|
106
|
+
code_bundle = None
|
|
107
|
+
else:
|
|
108
|
+
if obj.parent_env is None:
|
|
109
|
+
raise ValueError("Task is not attached to an environment. Please attach the task to an environment")
|
|
97
110
|
|
|
98
|
-
|
|
99
|
-
|
|
111
|
+
deploy_plan = plan_deploy(cast(Environment, obj.parent_env()))
|
|
112
|
+
image_cache = await build_images(deploy_plan)
|
|
100
113
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
)
|
|
105
|
-
else:
|
|
106
|
-
if self._copy_files != "none":
|
|
107
|
-
code_bundle = await build_code_bundle(
|
|
108
|
-
from_dir=cfg.root_dir, dryrun=self._dry_run, copy_bundle_to=self._copy_bundle_to
|
|
114
|
+
if self._interactive_mode:
|
|
115
|
+
code_bundle = await build_pkl_bundle(
|
|
116
|
+
obj, upload_to_controlplane=not self._dry_run, copy_bundle_to=self._copy_bundle_to
|
|
109
117
|
)
|
|
110
118
|
else:
|
|
111
|
-
|
|
119
|
+
if self._copy_files != "none":
|
|
120
|
+
code_bundle = await build_code_bundle(
|
|
121
|
+
from_dir=cfg.root_dir,
|
|
122
|
+
dryrun=self._dry_run,
|
|
123
|
+
copy_bundle_to=self._copy_bundle_to,
|
|
124
|
+
copy_style=self._copy_files,
|
|
125
|
+
)
|
|
126
|
+
else:
|
|
127
|
+
code_bundle = None
|
|
112
128
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
129
|
+
version = self._version or (
|
|
130
|
+
code_bundle.computed_version if code_bundle and code_bundle.computed_version else None
|
|
131
|
+
)
|
|
132
|
+
if not version:
|
|
133
|
+
raise ValueError("Version is required when running a task")
|
|
134
|
+
s_ctx = SerializationContext(
|
|
135
|
+
code_bundle=code_bundle,
|
|
136
|
+
version=version,
|
|
137
|
+
image_cache=image_cache,
|
|
138
|
+
root_dir=cfg.root_dir,
|
|
139
|
+
)
|
|
140
|
+
task_spec = translate_task_to_wire(obj, s_ctx)
|
|
141
|
+
inputs = await convert_from_native_to_inputs(obj.native_interface, *args, **kwargs)
|
|
126
142
|
|
|
127
143
|
if not self._dry_run:
|
|
128
144
|
if get_client() is None:
|
|
@@ -159,15 +175,35 @@ class _Runner:
|
|
|
159
175
|
if task_spec.task_template.id.version == "":
|
|
160
176
|
task_spec.task_template.id.version = version
|
|
161
177
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
178
|
+
try:
|
|
179
|
+
resp = await get_client().run_service.CreateRun(
|
|
180
|
+
run_service_pb2.CreateRunRequest(
|
|
181
|
+
run_id=run_id,
|
|
182
|
+
project_id=project_id,
|
|
183
|
+
task_spec=task_spec,
|
|
184
|
+
inputs=inputs.proto_inputs,
|
|
185
|
+
),
|
|
186
|
+
)
|
|
187
|
+
return Run(pb2=resp.run)
|
|
188
|
+
except grpc.aio.AioRpcError as e:
|
|
189
|
+
if e.code() == grpc.StatusCode.UNAVAILABLE:
|
|
190
|
+
raise flyte.errors.RuntimeSystemError(
|
|
191
|
+
"SystemUnavailableError",
|
|
192
|
+
"Flyte system is currently unavailable. check your configuration, or the service status.",
|
|
193
|
+
) from e
|
|
194
|
+
elif e.code() == grpc.StatusCode.INVALID_ARGUMENT:
|
|
195
|
+
raise flyte.errors.RuntimeUserError("InvalidArgumentError", e.details())
|
|
196
|
+
elif e.code() == grpc.StatusCode.ALREADY_EXISTS:
|
|
197
|
+
# TODO maybe this should be a pass and return existing run?
|
|
198
|
+
raise flyte.errors.RuntimeUserError(
|
|
199
|
+
"RunAlreadyExistsError",
|
|
200
|
+
f"A run with the name '{self._name}' already exists. Please choose a different name.",
|
|
201
|
+
)
|
|
202
|
+
else:
|
|
203
|
+
raise flyte.errors.RuntimeSystemError(
|
|
204
|
+
"RunCreationError",
|
|
205
|
+
f"Failed to create run: {e.details()}",
|
|
206
|
+
) from e
|
|
171
207
|
|
|
172
208
|
class DryRun(Run):
|
|
173
209
|
def __init__(self, _task_spec, _inputs, _code_bundle):
|
|
@@ -224,7 +260,10 @@ class _Runner:
|
|
|
224
260
|
else:
|
|
225
261
|
if self._copy_files != "none":
|
|
226
262
|
code_bundle = await build_code_bundle(
|
|
227
|
-
from_dir=cfg.root_dir,
|
|
263
|
+
from_dir=cfg.root_dir,
|
|
264
|
+
dryrun=self._dry_run,
|
|
265
|
+
copy_bundle_to=self._copy_bundle_to,
|
|
266
|
+
copy_style=self._copy_files,
|
|
228
267
|
)
|
|
229
268
|
else:
|
|
230
269
|
code_bundle = None
|
|
@@ -287,43 +326,44 @@ class _Runner:
|
|
|
287
326
|
|
|
288
327
|
async def _run_local(self, obj: TaskTemplate[P, R], *args: P.args, **kwargs: P.kwargs) -> R:
|
|
289
328
|
from flyte._internal.controllers import create_controller
|
|
290
|
-
from flyte._internal.
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
)
|
|
295
|
-
from flyte._internal.runtime.entrypoints import direct_dispatch
|
|
329
|
+
from flyte._internal.controllers._local_controller import LocalController
|
|
330
|
+
from flyte.report import Report
|
|
331
|
+
|
|
332
|
+
controller = cast(LocalController, create_controller("local"))
|
|
296
333
|
|
|
297
|
-
controller = create_controller("local")
|
|
298
|
-
inputs = await convert_from_native_to_inputs(obj.native_interface, *args, **kwargs)
|
|
299
334
|
if self._name is None:
|
|
300
335
|
action = ActionID.create_random()
|
|
301
336
|
else:
|
|
302
337
|
action = ActionID(name=self._name)
|
|
303
|
-
|
|
304
|
-
|
|
338
|
+
|
|
339
|
+
ctx = internal_ctx()
|
|
340
|
+
tctx = TaskContext(
|
|
305
341
|
action=action,
|
|
306
|
-
raw_data_path=internal_ctx().raw_data,
|
|
307
|
-
version="na",
|
|
308
|
-
controller=controller,
|
|
309
|
-
inputs=inputs,
|
|
310
|
-
output_path=self._metadata_path,
|
|
311
|
-
run_base_dir=self._metadata_path,
|
|
312
342
|
checkpoints=Checkpoints(
|
|
313
343
|
prev_checkpoint_path=internal_ctx().raw_data.path, checkpoint_path=internal_ctx().raw_data.path
|
|
314
344
|
),
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
345
|
+
code_bundle=None,
|
|
346
|
+
output_path=self._metadata_path,
|
|
347
|
+
run_base_dir=self._metadata_path,
|
|
348
|
+
version="na",
|
|
349
|
+
raw_data_path=internal_ctx().raw_data,
|
|
350
|
+
compiled_image_cache=None,
|
|
351
|
+
report=Report(name=action.name),
|
|
352
|
+
mode="local",
|
|
353
|
+
)
|
|
354
|
+
with ctx.replace_task_context(tctx):
|
|
355
|
+
# make the local version always runs on a different thread, returns a wrapped future.
|
|
356
|
+
if obj._call_as_synchronous:
|
|
357
|
+
fut = controller.submit_sync(obj, *args, **kwargs)
|
|
358
|
+
awaitable = asyncio.wrap_future(fut)
|
|
359
|
+
return await awaitable
|
|
360
|
+
else:
|
|
361
|
+
return await controller.submit(obj, *args, **kwargs)
|
|
324
362
|
|
|
325
363
|
@syncify
|
|
326
|
-
async def run(
|
|
364
|
+
async def run(
|
|
365
|
+
self, task: TaskTemplate[P, Union[R, Run]] | LazyEntity, *args: P.args, **kwargs: P.kwargs
|
|
366
|
+
) -> Union[R, Run]:
|
|
327
367
|
"""
|
|
328
368
|
Run an async `@env.task` or `TaskTemplate` instance. The existing async context will be used.
|
|
329
369
|
|
|
@@ -345,13 +385,18 @@ class _Runner:
|
|
|
345
385
|
:param kwargs: Keyword arguments to pass to the Task
|
|
346
386
|
:return: Run instance or the result of the task
|
|
347
387
|
"""
|
|
388
|
+
from flyte.remote._task import LazyEntity
|
|
389
|
+
|
|
390
|
+
if isinstance(task, LazyEntity) and self._mode != "remote":
|
|
391
|
+
raise ValueError("Remote task can only be run in remote mode.")
|
|
348
392
|
if self._mode == "remote":
|
|
349
393
|
return await self._run_remote(task, *args, **kwargs)
|
|
394
|
+
task = cast(TaskTemplate, task)
|
|
350
395
|
if self._mode == "hybrid":
|
|
351
396
|
return await self._run_hybrid(task, *args, **kwargs)
|
|
352
397
|
|
|
353
398
|
# TODO We could use this for remote as well and users could simply pass flyte:// or s3:// or file://
|
|
354
|
-
|
|
399
|
+
with internal_ctx().new_raw_data_path(
|
|
355
400
|
raw_data_path=RawDataPath.from_local_folder(local_folder=self._raw_data_path)
|
|
356
401
|
):
|
|
357
402
|
return await self._run_local(task, *args, **kwargs)
|
flyte/_task.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import asyncio
|
|
3
4
|
import weakref
|
|
4
5
|
from dataclasses import dataclass, field, replace
|
|
5
6
|
from functools import cached_property
|
|
@@ -216,6 +217,51 @@ class TaskTemplate(Generic[P, R]):
|
|
|
216
217
|
def native_interface(self) -> NativeInterface:
|
|
217
218
|
return self.interface
|
|
218
219
|
|
|
220
|
+
async def aio(self, *args: P.args, **kwargs: P.kwargs) -> Coroutine[Any, Any, R] | R:
|
|
221
|
+
"""
|
|
222
|
+
The aio function allows executing "sync" tasks, in an async context. This helps with migrating v1 defined sync
|
|
223
|
+
tasks to be used within an asyncio parent task.
|
|
224
|
+
This function will also re-raise exceptions from the underlying task.
|
|
225
|
+
|
|
226
|
+
Example:
|
|
227
|
+
```python
|
|
228
|
+
@env.task
|
|
229
|
+
def my_legacy_task(x: int) -> int:
|
|
230
|
+
return x
|
|
231
|
+
|
|
232
|
+
@env.task
|
|
233
|
+
async def my_new_parent_task(n: int) -> List[int]:
|
|
234
|
+
collect = []
|
|
235
|
+
for x in range(n):
|
|
236
|
+
collect.append(my_legacy_task.aio(x))
|
|
237
|
+
return asyncio.gather(*collect)
|
|
238
|
+
```
|
|
239
|
+
:param args:
|
|
240
|
+
:param kwargs:
|
|
241
|
+
:return:
|
|
242
|
+
"""
|
|
243
|
+
|
|
244
|
+
ctx = internal_ctx()
|
|
245
|
+
if ctx.is_task_context():
|
|
246
|
+
from ._internal.controllers import get_controller
|
|
247
|
+
|
|
248
|
+
# If we are in a task context, that implies we are executing a Run.
|
|
249
|
+
# In this scenario, we should submit the task to the controller.
|
|
250
|
+
controller = get_controller()
|
|
251
|
+
if controller:
|
|
252
|
+
if self._call_as_synchronous:
|
|
253
|
+
fut = controller.submit_sync(self, *args, **kwargs)
|
|
254
|
+
asyncio_future = asyncio.wrap_future(fut) # Wrap the future to make it awaitable
|
|
255
|
+
return await asyncio_future
|
|
256
|
+
else:
|
|
257
|
+
return await controller.submit(self, *args, **kwargs)
|
|
258
|
+
else:
|
|
259
|
+
raise RuntimeSystemError("BadContext", "Controller is not initialized.")
|
|
260
|
+
else:
|
|
261
|
+
# Local execute, just stay out of the way, but because .aio is used, we want to return an awaitable,
|
|
262
|
+
# even for synchronous tasks. This is to support migration.
|
|
263
|
+
return self.forward(*args, **kwargs)
|
|
264
|
+
|
|
219
265
|
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> Coroutine[Any, Any, R] | R:
|
|
220
266
|
"""
|
|
221
267
|
This is the entrypoint for an async function task at runtime. It will be called during an execution.
|
|
@@ -235,15 +281,18 @@ class TaskTemplate(Generic[P, R]):
|
|
|
235
281
|
from ._internal.controllers import get_controller
|
|
236
282
|
|
|
237
283
|
controller = get_controller()
|
|
238
|
-
if controller:
|
|
239
|
-
if self._call_as_synchronous:
|
|
240
|
-
return controller.submit_sync(self, *args, **kwargs)
|
|
241
|
-
else:
|
|
242
|
-
return controller.submit(self, *args, **kwargs)
|
|
243
|
-
else:
|
|
284
|
+
if not controller:
|
|
244
285
|
raise RuntimeSystemError("BadContext", "Controller is not initialized.")
|
|
245
286
|
|
|
246
|
-
|
|
287
|
+
if self._call_as_synchronous:
|
|
288
|
+
fut = controller.submit_sync(self, *args, **kwargs)
|
|
289
|
+
x = fut.result(None)
|
|
290
|
+
return x
|
|
291
|
+
else:
|
|
292
|
+
return controller.submit(self, *args, **kwargs)
|
|
293
|
+
else:
|
|
294
|
+
# If not in task context, purely function run, stay out of the way
|
|
295
|
+
return self.forward(*args, **kwargs)
|
|
247
296
|
except RuntimeSystemError:
|
|
248
297
|
raise
|
|
249
298
|
except RuntimeUserError:
|
flyte/_utils/helpers.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import string
|
|
3
3
|
import typing
|
|
4
|
+
from contextlib import contextmanager
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
|
|
6
7
|
|
|
@@ -106,3 +107,17 @@ def get_cwd_editable_install() -> typing.Optional[Path]:
|
|
|
106
107
|
return install # note we want the install folder, not the parent
|
|
107
108
|
|
|
108
109
|
return None
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@contextmanager
|
|
113
|
+
def _selector_policy():
|
|
114
|
+
import asyncio
|
|
115
|
+
|
|
116
|
+
original_policy = asyncio.get_event_loop_policy()
|
|
117
|
+
try:
|
|
118
|
+
if os.name == "nt" and hasattr(asyncio, "WindowsSelectorEventLoopPolicy"):
|
|
119
|
+
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
|
120
|
+
|
|
121
|
+
yield
|
|
122
|
+
finally:
|
|
123
|
+
asyncio.set_event_loop_policy(original_policy)
|
flyte/_version.py
CHANGED
|
@@ -17,5 +17,5 @@ __version__: str
|
|
|
17
17
|
__version_tuple__: VERSION_TUPLE
|
|
18
18
|
version_tuple: VERSION_TUPLE
|
|
19
19
|
|
|
20
|
-
__version__ = version = '0.2.
|
|
21
|
-
__version_tuple__ = version_tuple = (0, 2, 0, '
|
|
20
|
+
__version__ = version = '0.2.0b10'
|
|
21
|
+
__version_tuple__ = version_tuple = (0, 2, 0, 'b10')
|
flyte/cli/__init__.py
CHANGED
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
"""
|
|
2
|
-
# CLI for Flyte
|
|
3
|
-
|
|
4
|
-
The flyte cli follows a simple verb based structure, where the top-level commands are verbs that describe the action
|
|
5
|
-
to be taken, and the subcommands are nouns that describe the object of the action.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
1
|
from flyte.cli.main import main
|
|
9
2
|
|
|
10
3
|
__all__ = ["main"]
|
flyte/cli/_abort.py
CHANGED
flyte/cli/_common.py
CHANGED
|
@@ -32,7 +32,7 @@ PROJECT_OPTION = click.Option(
|
|
|
32
32
|
required=False,
|
|
33
33
|
type=str,
|
|
34
34
|
default=None,
|
|
35
|
-
help="Project to
|
|
35
|
+
help="Project to which this command applies.",
|
|
36
36
|
show_default=True,
|
|
37
37
|
)
|
|
38
38
|
|
|
@@ -41,7 +41,7 @@ DOMAIN_OPTION = click.Option(
|
|
|
41
41
|
required=False,
|
|
42
42
|
type=str,
|
|
43
43
|
default=None,
|
|
44
|
-
help="Domain to
|
|
44
|
+
help="Domain to which this command applies.",
|
|
45
45
|
show_default=True,
|
|
46
46
|
)
|
|
47
47
|
|
|
@@ -51,14 +51,14 @@ DRY_RUN_OPTION = click.Option(
|
|
|
51
51
|
type=bool,
|
|
52
52
|
is_flag=True,
|
|
53
53
|
default=False,
|
|
54
|
-
help="Dry run
|
|
54
|
+
help="Dry run. Do not actually call the backend service.",
|
|
55
55
|
show_default=True,
|
|
56
56
|
)
|
|
57
57
|
|
|
58
58
|
|
|
59
59
|
def _common_options() -> List[click.Option]:
|
|
60
60
|
"""
|
|
61
|
-
Common options
|
|
61
|
+
Common options that will be added to all commands and groups that inherit from CommandBase or GroupBase.
|
|
62
62
|
"""
|
|
63
63
|
return [PROJECT_OPTION, DOMAIN_OPTION]
|
|
64
64
|
|
|
@@ -105,13 +105,13 @@ class CLIConfig:
|
|
|
105
105
|
updated_config = self.config.with_params(platform_cfg, task_cfg)
|
|
106
106
|
|
|
107
107
|
logger.debug(f"Initializing CLI with config: {updated_config}")
|
|
108
|
-
flyte.
|
|
108
|
+
flyte.init_from_config(updated_config)
|
|
109
109
|
|
|
110
110
|
|
|
111
111
|
class InvokeBaseMixin:
|
|
112
112
|
"""
|
|
113
|
-
Mixin to catch grpc.RpcError, flyte.RpcError, other errors and other exceptions
|
|
114
|
-
|
|
113
|
+
Mixin to catch grpc.RpcError, flyte.RpcError, other errors and other exceptions
|
|
114
|
+
and raise them as gclick.ClickException.
|
|
115
115
|
"""
|
|
116
116
|
|
|
117
117
|
def invoke(self, ctx):
|
flyte/cli/_create.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
|
-
from typing import get_args
|
|
2
|
+
from typing import Any, Dict, get_args
|
|
3
3
|
|
|
4
4
|
import rich_click as click
|
|
5
5
|
|
|
@@ -10,7 +10,7 @@ from flyte.remote._secret import SecretTypes
|
|
|
10
10
|
@click.group(name="create")
|
|
11
11
|
def create():
|
|
12
12
|
"""
|
|
13
|
-
|
|
13
|
+
Create resources in a Flyte deployment.
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
16
|
|
|
@@ -32,22 +32,26 @@ def secret(
|
|
|
32
32
|
domain: str | None = None,
|
|
33
33
|
):
|
|
34
34
|
"""
|
|
35
|
-
Create a new secret
|
|
35
|
+
Create a new secret. The name of the secret is required. For example:
|
|
36
36
|
|
|
37
|
-
Examples:
|
|
38
37
|
```bash
|
|
39
|
-
flyte create secret my_secret --value my_value
|
|
38
|
+
$ flyte create secret my_secret --value my_value
|
|
40
39
|
```
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
|
|
41
|
+
If `--from-file` is specified, the value will be read from the file instead of being provided directly:
|
|
42
|
+
|
|
43
43
|
```bash
|
|
44
|
-
flyte create secret my_secret --from-file /path/to/secret_file
|
|
44
|
+
$ flyte create secret my_secret --from-file /path/to/secret_file
|
|
45
45
|
```
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
|
|
47
|
+
The `--type` option can be used to create specific types of secrets.
|
|
48
|
+
Either `regular` or `image_pull` can be specified.
|
|
49
|
+
Secrets intended to access container images should be specified as `image_pull`.
|
|
50
|
+
Other secrets should be specified as `regular`.
|
|
51
|
+
If no type is specified, `regular` is assumed.
|
|
52
|
+
|
|
49
53
|
```bash
|
|
50
|
-
flyte create secret my_secret --type image_pull
|
|
54
|
+
$ flyte create secret my_secret --type image_pull
|
|
51
55
|
```
|
|
52
56
|
"""
|
|
53
57
|
from flyte.remote import Secret
|
|
@@ -61,28 +65,28 @@ def secret(
|
|
|
61
65
|
|
|
62
66
|
@create.command(cls=common.CommandBase)
|
|
63
67
|
@click.option("--endpoint", type=str, help="Endpoint of the Flyte backend.")
|
|
64
|
-
@click.option("--insecure", is_flag=True, help="Use insecure connection to the Flyte backend.")
|
|
68
|
+
@click.option("--insecure", is_flag=True, help="Use an insecure connection to the Flyte backend.")
|
|
65
69
|
@click.option(
|
|
66
70
|
"--org",
|
|
67
71
|
type=str,
|
|
68
72
|
required=False,
|
|
69
|
-
help="Organization to use
|
|
73
|
+
help="Organization to use. This will override the organization in the configuration file.",
|
|
70
74
|
)
|
|
71
75
|
@click.option(
|
|
72
76
|
"-o",
|
|
73
77
|
"--output",
|
|
74
78
|
type=click.Path(exists=False, writable=True),
|
|
75
79
|
default=Path.cwd() / "config.yaml",
|
|
76
|
-
help="Path to the output
|
|
80
|
+
help="Path to the output directory where the configuration will be saved. Defaults to current directory.",
|
|
77
81
|
show_default=True,
|
|
78
82
|
)
|
|
79
83
|
@click.option(
|
|
80
84
|
"--force",
|
|
81
85
|
is_flag=True,
|
|
82
86
|
default=False,
|
|
83
|
-
help="Force overwrite the
|
|
87
|
+
help="Force overwrite of the configuration file if it already exists.",
|
|
84
88
|
show_default=True,
|
|
85
|
-
prompt="Are you sure you want to overwrite the
|
|
89
|
+
prompt="Are you sure you want to overwrite the configuration file?",
|
|
86
90
|
confirmation_prompt=True,
|
|
87
91
|
)
|
|
88
92
|
def config(
|
|
@@ -95,7 +99,7 @@ def config(
|
|
|
95
99
|
force: bool = False,
|
|
96
100
|
):
|
|
97
101
|
"""
|
|
98
|
-
|
|
102
|
+
Creates a configuration file for Flyte CLI.
|
|
99
103
|
If the `--output` option is not specified, it will create a file named `config.yaml` in the current directory.
|
|
100
104
|
If the file already exists, it will raise an error unless the `--force` option is used.
|
|
101
105
|
"""
|
|
@@ -104,18 +108,29 @@ def config(
|
|
|
104
108
|
if output.exists() and not force:
|
|
105
109
|
raise click.BadParameter(f"Output file {output} already exists. Use --force to overwrite.")
|
|
106
110
|
|
|
111
|
+
admin: Dict[str, Any] = {}
|
|
112
|
+
if endpoint:
|
|
113
|
+
admin["endpoint"] = endpoint
|
|
114
|
+
if insecure:
|
|
115
|
+
admin["insecure"] = insecure
|
|
116
|
+
|
|
117
|
+
task: Dict[str, str] = {}
|
|
118
|
+
if org:
|
|
119
|
+
task["org"] = org
|
|
120
|
+
if project:
|
|
121
|
+
task["project"] = project
|
|
122
|
+
if domain:
|
|
123
|
+
task["domain"] = domain
|
|
124
|
+
|
|
125
|
+
if not admin and not task:
|
|
126
|
+
raise click.BadParameter("At least one of --endpoint or --org must be provided.")
|
|
127
|
+
|
|
107
128
|
with open(output, "w") as f:
|
|
108
|
-
d = {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
"task": {
|
|
114
|
-
"org": org,
|
|
115
|
-
"project": project,
|
|
116
|
-
"domain": domain,
|
|
117
|
-
},
|
|
118
|
-
}
|
|
129
|
+
d: Dict[str, Any] = {}
|
|
130
|
+
if admin:
|
|
131
|
+
d["admin"] = admin
|
|
132
|
+
if task:
|
|
133
|
+
d["task"] = task
|
|
119
134
|
yaml.dump(d, f)
|
|
120
135
|
|
|
121
136
|
click.echo(f"Config file created at {output}")
|
flyte/cli/_delete.py
CHANGED
|
@@ -6,7 +6,7 @@ import flyte.cli._common as common
|
|
|
6
6
|
@click.group(name="delete")
|
|
7
7
|
def delete():
|
|
8
8
|
"""
|
|
9
|
-
|
|
9
|
+
Remove resources from a Flyte deployment.
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
12
|
|
|
@@ -15,7 +15,7 @@ def delete():
|
|
|
15
15
|
@click.pass_obj
|
|
16
16
|
def secret(cfg: common.CLIConfig, name: str, project: str | None = None, domain: str | None = None):
|
|
17
17
|
"""
|
|
18
|
-
Delete a secret
|
|
18
|
+
Delete a secret. The name of the secret is required.
|
|
19
19
|
"""
|
|
20
20
|
from flyte.remote import Secret
|
|
21
21
|
|
flyte/cli/_deploy.py
CHANGED
|
@@ -61,7 +61,7 @@ class DeployArguments:
|
|
|
61
61
|
@classmethod
|
|
62
62
|
def options(cls) -> List[click.Option]:
|
|
63
63
|
"""
|
|
64
|
-
Return the set of base parameters added to every
|
|
64
|
+
Return the set of base parameters added to every flyte run workflow subcommand.
|
|
65
65
|
"""
|
|
66
66
|
return [common.get_option_from_metadata(f.metadata) for f in fields(cls) if f.metadata]
|
|
67
67
|
|
|
@@ -87,7 +87,7 @@ class DeployEnvCommand(click.Command):
|
|
|
87
87
|
|
|
88
88
|
class EnvPerFileGroup(common.ObjectsPerFileGroup):
|
|
89
89
|
"""
|
|
90
|
-
Group that creates a command for each task in the current directory that is not __init__.py
|
|
90
|
+
Group that creates a command for each task in the current directory that is not `__init__.py`.
|
|
91
91
|
"""
|
|
92
92
|
|
|
93
93
|
def __init__(self, filename: Path, deploy_args: DeployArguments, *args, **kwargs):
|
|
@@ -111,7 +111,7 @@ class EnvPerFileGroup(common.ObjectsPerFileGroup):
|
|
|
111
111
|
|
|
112
112
|
class EnvFiles(common.FileGroup):
|
|
113
113
|
"""
|
|
114
|
-
Group that creates a command for each file in the current directory that is not __init__.py
|
|
114
|
+
Group that creates a command for each file in the current directory that is not `__init__.py`.
|
|
115
115
|
"""
|
|
116
116
|
|
|
117
117
|
common_options_enabled = False
|