flyte 2.0.0b32__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 +108 -0
- flyte/_bin/__init__.py +0 -0
- flyte/_bin/debug.py +38 -0
- flyte/_bin/runtime.py +195 -0
- flyte/_bin/serve.py +178 -0
- flyte/_build.py +26 -0
- flyte/_cache/__init__.py +12 -0
- flyte/_cache/cache.py +147 -0
- flyte/_cache/defaults.py +9 -0
- flyte/_cache/local_cache.py +216 -0
- flyte/_cache/policy_function_body.py +42 -0
- flyte/_code_bundle/__init__.py +8 -0
- flyte/_code_bundle/_ignore.py +121 -0
- flyte/_code_bundle/_packaging.py +218 -0
- flyte/_code_bundle/_utils.py +347 -0
- flyte/_code_bundle/bundle.py +266 -0
- flyte/_constants.py +1 -0
- flyte/_context.py +155 -0
- flyte/_custom_context.py +73 -0
- flyte/_debug/__init__.py +0 -0
- flyte/_debug/constants.py +38 -0
- flyte/_debug/utils.py +17 -0
- flyte/_debug/vscode.py +307 -0
- flyte/_deploy.py +408 -0
- flyte/_deployer.py +109 -0
- flyte/_doc.py +29 -0
- flyte/_docstring.py +32 -0
- flyte/_environment.py +122 -0
- flyte/_excepthook.py +37 -0
- flyte/_group.py +32 -0
- flyte/_hash.py +8 -0
- flyte/_image.py +1055 -0
- flyte/_initialize.py +628 -0
- flyte/_interface.py +119 -0
- flyte/_internal/__init__.py +3 -0
- flyte/_internal/controllers/__init__.py +129 -0
- flyte/_internal/controllers/_local_controller.py +239 -0
- flyte/_internal/controllers/_trace.py +48 -0
- flyte/_internal/controllers/remote/__init__.py +58 -0
- flyte/_internal/controllers/remote/_action.py +211 -0
- flyte/_internal/controllers/remote/_client.py +47 -0
- flyte/_internal/controllers/remote/_controller.py +583 -0
- flyte/_internal/controllers/remote/_core.py +465 -0
- flyte/_internal/controllers/remote/_informer.py +381 -0
- flyte/_internal/controllers/remote/_service_protocol.py +50 -0
- flyte/_internal/imagebuild/__init__.py +3 -0
- flyte/_internal/imagebuild/docker_builder.py +706 -0
- flyte/_internal/imagebuild/image_builder.py +277 -0
- flyte/_internal/imagebuild/remote_builder.py +386 -0
- flyte/_internal/imagebuild/utils.py +78 -0
- flyte/_internal/resolvers/__init__.py +0 -0
- flyte/_internal/resolvers/_task_module.py +21 -0
- flyte/_internal/resolvers/common.py +31 -0
- flyte/_internal/resolvers/default.py +28 -0
- flyte/_internal/runtime/__init__.py +0 -0
- flyte/_internal/runtime/convert.py +486 -0
- flyte/_internal/runtime/entrypoints.py +204 -0
- flyte/_internal/runtime/io.py +188 -0
- flyte/_internal/runtime/resources_serde.py +152 -0
- flyte/_internal/runtime/reuse.py +125 -0
- flyte/_internal/runtime/rusty.py +193 -0
- flyte/_internal/runtime/task_serde.py +362 -0
- flyte/_internal/runtime/taskrunner.py +209 -0
- flyte/_internal/runtime/trigger_serde.py +160 -0
- flyte/_internal/runtime/types_serde.py +54 -0
- flyte/_keyring/__init__.py +0 -0
- flyte/_keyring/file.py +115 -0
- flyte/_logging.py +300 -0
- flyte/_map.py +312 -0
- flyte/_module.py +72 -0
- flyte/_pod.py +30 -0
- flyte/_resources.py +473 -0
- flyte/_retry.py +32 -0
- flyte/_reusable_environment.py +102 -0
- flyte/_run.py +724 -0
- flyte/_secret.py +96 -0
- flyte/_task.py +550 -0
- flyte/_task_environment.py +316 -0
- flyte/_task_plugins.py +47 -0
- flyte/_timeout.py +47 -0
- flyte/_tools.py +27 -0
- flyte/_trace.py +119 -0
- flyte/_trigger.py +1000 -0
- flyte/_utils/__init__.py +30 -0
- flyte/_utils/asyn.py +121 -0
- flyte/_utils/async_cache.py +139 -0
- flyte/_utils/coro_management.py +27 -0
- flyte/_utils/docker_credentials.py +173 -0
- flyte/_utils/file_handling.py +72 -0
- flyte/_utils/helpers.py +134 -0
- flyte/_utils/lazy_module.py +54 -0
- flyte/_utils/module_loader.py +104 -0
- flyte/_utils/org_discovery.py +57 -0
- flyte/_utils/uv_script_parser.py +49 -0
- flyte/_version.py +34 -0
- flyte/app/__init__.py +22 -0
- flyte/app/_app_environment.py +157 -0
- flyte/app/_deploy.py +125 -0
- flyte/app/_input.py +160 -0
- flyte/app/_runtime/__init__.py +3 -0
- flyte/app/_runtime/app_serde.py +347 -0
- flyte/app/_types.py +101 -0
- flyte/app/extras/__init__.py +3 -0
- flyte/app/extras/_fastapi.py +151 -0
- flyte/cli/__init__.py +12 -0
- flyte/cli/_abort.py +28 -0
- flyte/cli/_build.py +114 -0
- flyte/cli/_common.py +468 -0
- flyte/cli/_create.py +371 -0
- flyte/cli/_delete.py +45 -0
- flyte/cli/_deploy.py +293 -0
- flyte/cli/_gen.py +176 -0
- flyte/cli/_get.py +370 -0
- flyte/cli/_option.py +33 -0
- flyte/cli/_params.py +554 -0
- flyte/cli/_plugins.py +209 -0
- flyte/cli/_run.py +597 -0
- flyte/cli/_serve.py +64 -0
- flyte/cli/_update.py +37 -0
- flyte/cli/_user.py +17 -0
- flyte/cli/main.py +221 -0
- flyte/config/__init__.py +3 -0
- flyte/config/_config.py +248 -0
- flyte/config/_internal.py +73 -0
- flyte/config/_reader.py +225 -0
- flyte/connectors/__init__.py +11 -0
- flyte/connectors/_connector.py +270 -0
- flyte/connectors/_server.py +197 -0
- flyte/connectors/utils.py +135 -0
- flyte/errors.py +243 -0
- flyte/extend.py +19 -0
- flyte/extras/__init__.py +5 -0
- flyte/extras/_container.py +286 -0
- flyte/git/__init__.py +3 -0
- flyte/git/_config.py +21 -0
- flyte/io/__init__.py +29 -0
- flyte/io/_dataframe/__init__.py +131 -0
- flyte/io/_dataframe/basic_dfs.py +223 -0
- flyte/io/_dataframe/dataframe.py +1026 -0
- flyte/io/_dir.py +910 -0
- flyte/io/_file.py +914 -0
- flyte/io/_hashing_io.py +342 -0
- flyte/models.py +479 -0
- flyte/py.typed +0 -0
- flyte/remote/__init__.py +35 -0
- flyte/remote/_action.py +738 -0
- flyte/remote/_app.py +57 -0
- flyte/remote/_client/__init__.py +0 -0
- flyte/remote/_client/_protocols.py +189 -0
- flyte/remote/_client/auth/__init__.py +12 -0
- flyte/remote/_client/auth/_auth_utils.py +14 -0
- flyte/remote/_client/auth/_authenticators/__init__.py +0 -0
- flyte/remote/_client/auth/_authenticators/base.py +403 -0
- flyte/remote/_client/auth/_authenticators/client_credentials.py +73 -0
- flyte/remote/_client/auth/_authenticators/device_code.py +117 -0
- flyte/remote/_client/auth/_authenticators/external_command.py +79 -0
- flyte/remote/_client/auth/_authenticators/factory.py +200 -0
- flyte/remote/_client/auth/_authenticators/pkce.py +516 -0
- flyte/remote/_client/auth/_channel.py +213 -0
- flyte/remote/_client/auth/_client_config.py +85 -0
- flyte/remote/_client/auth/_default_html.py +32 -0
- flyte/remote/_client/auth/_grpc_utils/__init__.py +0 -0
- flyte/remote/_client/auth/_grpc_utils/auth_interceptor.py +288 -0
- flyte/remote/_client/auth/_grpc_utils/default_metadata_interceptor.py +151 -0
- flyte/remote/_client/auth/_keyring.py +152 -0
- flyte/remote/_client/auth/_token_client.py +260 -0
- flyte/remote/_client/auth/errors.py +16 -0
- flyte/remote/_client/controlplane.py +128 -0
- flyte/remote/_common.py +30 -0
- flyte/remote/_console.py +19 -0
- flyte/remote/_data.py +161 -0
- flyte/remote/_logs.py +185 -0
- flyte/remote/_project.py +88 -0
- flyte/remote/_run.py +386 -0
- flyte/remote/_secret.py +142 -0
- flyte/remote/_task.py +527 -0
- flyte/remote/_trigger.py +306 -0
- flyte/remote/_user.py +33 -0
- flyte/report/__init__.py +3 -0
- flyte/report/_report.py +182 -0
- flyte/report/_template.html +124 -0
- flyte/storage/__init__.py +36 -0
- flyte/storage/_config.py +237 -0
- flyte/storage/_parallel_reader.py +274 -0
- flyte/storage/_remote_fs.py +34 -0
- flyte/storage/_storage.py +456 -0
- flyte/storage/_utils.py +5 -0
- flyte/syncify/__init__.py +56 -0
- flyte/syncify/_api.py +375 -0
- flyte/types/__init__.py +52 -0
- flyte/types/_interface.py +40 -0
- flyte/types/_pickle.py +145 -0
- flyte/types/_renderer.py +162 -0
- flyte/types/_string_literals.py +119 -0
- flyte/types/_type_engine.py +2254 -0
- flyte/types/_utils.py +80 -0
- flyte-2.0.0b32.data/scripts/debug.py +38 -0
- flyte-2.0.0b32.data/scripts/runtime.py +195 -0
- flyte-2.0.0b32.dist-info/METADATA +351 -0
- flyte-2.0.0b32.dist-info/RECORD +204 -0
- flyte-2.0.0b32.dist-info/WHEEL +5 -0
- flyte-2.0.0b32.dist-info/entry_points.txt +7 -0
- flyte-2.0.0b32.dist-info/licenses/LICENSE +201 -0
- flyte-2.0.0b32.dist-info/top_level.txt +1 -0
flyte/types/_utils.py
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import importlib
|
|
4
|
+
import typing
|
|
5
|
+
|
|
6
|
+
from flyteidl2.core.types_pb2 import EnumType, LiteralType, UnionType
|
|
7
|
+
|
|
8
|
+
T = typing.TypeVar("T")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def literal_types_match(downstream: LiteralType, upstream: LiteralType) -> bool:
|
|
12
|
+
"""
|
|
13
|
+
Returns if two LiteralTypes are the same.
|
|
14
|
+
Takes into account arbitrary ordering of enums and unions, otherwise just an equivalence check.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
# If the types are exactly the same, return True
|
|
18
|
+
if downstream == upstream:
|
|
19
|
+
return True
|
|
20
|
+
|
|
21
|
+
if downstream.collection_type:
|
|
22
|
+
if not upstream.collection_type:
|
|
23
|
+
return False
|
|
24
|
+
return literal_types_match(downstream.collection_type, upstream.collection_type)
|
|
25
|
+
|
|
26
|
+
if downstream.map_value_type:
|
|
27
|
+
if not upstream.map_value_type:
|
|
28
|
+
return False
|
|
29
|
+
return literal_types_match(downstream.map_value_type, upstream.map_value_type)
|
|
30
|
+
|
|
31
|
+
# Handle enum types
|
|
32
|
+
if downstream.enum_type and upstream.enum_type:
|
|
33
|
+
return _enum_types_match(downstream.enum_type, upstream.enum_type)
|
|
34
|
+
|
|
35
|
+
# Handle union types
|
|
36
|
+
if downstream.union_type and upstream.union_type:
|
|
37
|
+
return _union_types_match(downstream.union_type, upstream.union_type)
|
|
38
|
+
|
|
39
|
+
# If none of the above conditions are met, the types are not castable
|
|
40
|
+
return False
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _enum_types_match(downstream: EnumType, upstream: EnumType) -> bool:
|
|
44
|
+
return set(upstream.values) == set(downstream.values)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _union_types_match(downstream: UnionType, upstream: UnionType) -> bool:
|
|
48
|
+
if len(downstream.variants) != len(upstream.variants):
|
|
49
|
+
return False
|
|
50
|
+
|
|
51
|
+
down_sorted = sorted(downstream.variants, key=lambda x: str(x))
|
|
52
|
+
up_sorted = sorted(upstream.variants, key=lambda x: str(x))
|
|
53
|
+
|
|
54
|
+
for downstream_variant, upstream_variant in zip(down_sorted, up_sorted):
|
|
55
|
+
if not literal_types_match(downstream_variant, upstream_variant):
|
|
56
|
+
return False
|
|
57
|
+
|
|
58
|
+
return True
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def load_type_from_tag(tag: str) -> typing.Type[T]:
|
|
62
|
+
"""
|
|
63
|
+
Helper function for proto buf compatibility only. Used in type transformer
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
if "." not in tag:
|
|
67
|
+
raise ValueError(
|
|
68
|
+
f"Protobuf tag must include at least one '.' to delineate package and object name got {tag}",
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
module, name = tag.rsplit(".", 1)
|
|
72
|
+
try:
|
|
73
|
+
pb_module = importlib.import_module(module)
|
|
74
|
+
except ImportError:
|
|
75
|
+
raise ValueError(f"Could not resolve the protobuf definition @ {module}. Is the protobuf library installed?")
|
|
76
|
+
|
|
77
|
+
if not hasattr(pb_module, name):
|
|
78
|
+
raise ValueError(f"Could not find the protobuf named: {name} @ {module}.")
|
|
79
|
+
|
|
80
|
+
return getattr(pb_module, name)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
@click.group()
|
|
5
|
+
def _debug():
|
|
6
|
+
"""Debug commands for Flyte."""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@_debug.command("resume")
|
|
10
|
+
@click.option("--pid", "-m", type=int, required=True, help="PID of the vscode server.")
|
|
11
|
+
def resume(pid):
|
|
12
|
+
"""
|
|
13
|
+
Resume a Flyte task for debugging purposes.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
pid (int): PID of the vscode server.
|
|
17
|
+
"""
|
|
18
|
+
import os
|
|
19
|
+
import signal
|
|
20
|
+
|
|
21
|
+
print("Terminating server and resuming task.")
|
|
22
|
+
answer = (
|
|
23
|
+
input(
|
|
24
|
+
"This operation will kill the server. All unsaved data will be lost,"
|
|
25
|
+
" and you will no longer be able to connect to it. Do you really want to terminate? (Y/N): "
|
|
26
|
+
)
|
|
27
|
+
.strip()
|
|
28
|
+
.upper()
|
|
29
|
+
)
|
|
30
|
+
if answer == "Y":
|
|
31
|
+
os.kill(pid, signal.SIGTERM)
|
|
32
|
+
print("The server has been terminated and the task has been resumed.")
|
|
33
|
+
else:
|
|
34
|
+
print("Operation canceled.")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
if __name__ == "__main__":
|
|
38
|
+
_debug()
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Flyte runtime module, this is the entrypoint script for the Flyte runtime.
|
|
3
|
+
|
|
4
|
+
Caution: Startup time for this module is very important, as it is the entrypoint for the Flyte runtime.
|
|
5
|
+
Refrain from importing any modules here. If you need to import any modules, do it inside the main function.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
import os
|
|
10
|
+
import sys
|
|
11
|
+
from typing import List
|
|
12
|
+
|
|
13
|
+
import click
|
|
14
|
+
|
|
15
|
+
from flyte.models import PathRewrite
|
|
16
|
+
|
|
17
|
+
# Todo: work with pvditt to make these the names
|
|
18
|
+
# ACTION_NAME = "_U_ACTION_NAME"
|
|
19
|
+
# RUN_NAME = "_U_RUN_NAME"
|
|
20
|
+
# PROJECT_NAME = "_U_PROJECT_NAME"
|
|
21
|
+
# DOMAIN_NAME = "_U_DOMAIN_NAME"
|
|
22
|
+
# ORG_NAME = "_U_ORG_NAME"
|
|
23
|
+
|
|
24
|
+
ACTION_NAME = "ACTION_NAME"
|
|
25
|
+
RUN_NAME = "RUN_NAME"
|
|
26
|
+
PROJECT_NAME = "FLYTE_INTERNAL_EXECUTION_PROJECT"
|
|
27
|
+
DOMAIN_NAME = "FLYTE_INTERNAL_EXECUTION_DOMAIN"
|
|
28
|
+
ORG_NAME = "_U_ORG_NAME"
|
|
29
|
+
ENDPOINT_OVERRIDE = "_U_EP_OVERRIDE"
|
|
30
|
+
INSECURE_SKIP_VERIFY_OVERRIDE = "_U_INSECURE_SKIP_VERIFY"
|
|
31
|
+
RUN_OUTPUT_BASE_DIR = "_U_RUN_BASE"
|
|
32
|
+
FLYTE_ENABLE_VSCODE_KEY = "_F_E_VS"
|
|
33
|
+
|
|
34
|
+
_UNION_EAGER_API_KEY_ENV_VAR = "_UNION_EAGER_API_KEY"
|
|
35
|
+
_F_PATH_REWRITE = "_F_PATH_REWRITE"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@click.group()
|
|
39
|
+
def _pass_through():
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@_pass_through.command("a0")
|
|
44
|
+
@click.option("--inputs", "-i", required=True)
|
|
45
|
+
@click.option("--outputs-path", "-o", required=True)
|
|
46
|
+
@click.option("--version", "-v", required=True)
|
|
47
|
+
@click.option("--run-base-dir", envvar=RUN_OUTPUT_BASE_DIR, required=True)
|
|
48
|
+
@click.option("--raw-data-path", "-r", required=False)
|
|
49
|
+
@click.option("--checkpoint-path", "-c", required=False)
|
|
50
|
+
@click.option("--prev-checkpoint", "-p", required=False)
|
|
51
|
+
@click.option("--name", envvar=ACTION_NAME, required=False)
|
|
52
|
+
@click.option("--run-name", envvar=RUN_NAME, required=False)
|
|
53
|
+
@click.option("--project", envvar=PROJECT_NAME, required=False)
|
|
54
|
+
@click.option("--domain", envvar=DOMAIN_NAME, required=False)
|
|
55
|
+
@click.option("--org", envvar=ORG_NAME, required=False)
|
|
56
|
+
@click.option("--debug", envvar=FLYTE_ENABLE_VSCODE_KEY, type=click.BOOL, required=False)
|
|
57
|
+
@click.option("--interactive-mode", type=click.BOOL, required=False)
|
|
58
|
+
@click.option("--image-cache", required=False)
|
|
59
|
+
@click.option("--tgz", required=False)
|
|
60
|
+
@click.option("--pkl", required=False)
|
|
61
|
+
@click.option("--dest", required=False)
|
|
62
|
+
@click.option("--resolver", required=False)
|
|
63
|
+
@click.argument(
|
|
64
|
+
"resolver-args",
|
|
65
|
+
type=click.UNPROCESSED,
|
|
66
|
+
nargs=-1,
|
|
67
|
+
)
|
|
68
|
+
@click.pass_context
|
|
69
|
+
def main(
|
|
70
|
+
ctx: click.Context,
|
|
71
|
+
run_name: str,
|
|
72
|
+
name: str,
|
|
73
|
+
project: str,
|
|
74
|
+
domain: str,
|
|
75
|
+
org: str,
|
|
76
|
+
debug: bool,
|
|
77
|
+
interactive_mode: bool,
|
|
78
|
+
image_cache: str,
|
|
79
|
+
version: str,
|
|
80
|
+
inputs: str,
|
|
81
|
+
run_base_dir: str,
|
|
82
|
+
outputs_path: str,
|
|
83
|
+
raw_data_path: str,
|
|
84
|
+
checkpoint_path: str,
|
|
85
|
+
prev_checkpoint: str,
|
|
86
|
+
tgz: str,
|
|
87
|
+
pkl: str,
|
|
88
|
+
dest: str,
|
|
89
|
+
resolver: str,
|
|
90
|
+
resolver_args: List[str],
|
|
91
|
+
):
|
|
92
|
+
sys.path.insert(0, ".")
|
|
93
|
+
|
|
94
|
+
import faulthandler
|
|
95
|
+
import signal
|
|
96
|
+
|
|
97
|
+
import flyte
|
|
98
|
+
import flyte._utils as utils
|
|
99
|
+
import flyte.errors
|
|
100
|
+
import flyte.storage as storage
|
|
101
|
+
from flyte._initialize import init_in_cluster
|
|
102
|
+
from flyte._internal.controllers import create_controller
|
|
103
|
+
from flyte._internal.imagebuild.image_builder import ImageCache
|
|
104
|
+
from flyte._internal.runtime.entrypoints import load_and_run_task
|
|
105
|
+
from flyte._logging import logger
|
|
106
|
+
from flyte.models import ActionID, Checkpoints, CodeBundle, RawDataPath
|
|
107
|
+
|
|
108
|
+
logger.info("Registering faulthandler for SIGUSR1")
|
|
109
|
+
faulthandler.register(signal.SIGUSR1)
|
|
110
|
+
|
|
111
|
+
logger.info(f"Initializing flyte runtime - version {flyte.__version__}")
|
|
112
|
+
assert org, "Org is required for now"
|
|
113
|
+
assert project, "Project is required"
|
|
114
|
+
assert domain, "Domain is required"
|
|
115
|
+
assert run_name, f"Run name is required {run_name}"
|
|
116
|
+
assert name, f"Action name is required {name}"
|
|
117
|
+
|
|
118
|
+
if run_name.startswith("{{"):
|
|
119
|
+
run_name = os.getenv("RUN_NAME", "")
|
|
120
|
+
if name.startswith("{{"):
|
|
121
|
+
name = os.getenv("ACTION_NAME", "")
|
|
122
|
+
|
|
123
|
+
logger.warning(f"Flyte runtime started for action {name} with run name {run_name}")
|
|
124
|
+
|
|
125
|
+
if debug and name == "a0":
|
|
126
|
+
from flyte._debug.vscode import _start_vscode_server
|
|
127
|
+
|
|
128
|
+
asyncio.run(_start_vscode_server(ctx))
|
|
129
|
+
|
|
130
|
+
controller_kwargs = init_in_cluster(org=org, project=project, domain=domain)
|
|
131
|
+
bundle = None
|
|
132
|
+
if tgz or pkl:
|
|
133
|
+
bundle = CodeBundle(tgz=tgz, pkl=pkl, destination=dest, computed_version=version)
|
|
134
|
+
# Controller is created with the same kwargs as init, so that it can be used to run tasks
|
|
135
|
+
controller = create_controller(ct="remote", **controller_kwargs)
|
|
136
|
+
|
|
137
|
+
ic = ImageCache.from_transport(image_cache) if image_cache else None
|
|
138
|
+
|
|
139
|
+
path_rewrite_cfg = os.getenv(_F_PATH_REWRITE, None)
|
|
140
|
+
path_rewrite = None
|
|
141
|
+
if path_rewrite_cfg:
|
|
142
|
+
potential_path_rewrite = PathRewrite.from_str(path_rewrite_cfg)
|
|
143
|
+
if storage.exists_sync(potential_path_rewrite.new_prefix):
|
|
144
|
+
path_rewrite = potential_path_rewrite
|
|
145
|
+
logger.info(f"Path rewrite configured for {path_rewrite.new_prefix}")
|
|
146
|
+
else:
|
|
147
|
+
logger.error(
|
|
148
|
+
f"Path rewrite failed for path {potential_path_rewrite.new_prefix}, "
|
|
149
|
+
f"not found, reverting to original path {potential_path_rewrite.old_prefix}"
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
# Create a coroutine to load the task and run it
|
|
153
|
+
task_coroutine = load_and_run_task(
|
|
154
|
+
resolver=resolver,
|
|
155
|
+
resolver_args=resolver_args,
|
|
156
|
+
action=ActionID(name=name, run_name=run_name, project=project, domain=domain, org=org),
|
|
157
|
+
raw_data_path=RawDataPath(path=raw_data_path, path_rewrite=path_rewrite),
|
|
158
|
+
checkpoints=Checkpoints(checkpoint_path, prev_checkpoint),
|
|
159
|
+
code_bundle=bundle,
|
|
160
|
+
input_path=inputs,
|
|
161
|
+
output_path=outputs_path,
|
|
162
|
+
run_base_dir=run_base_dir,
|
|
163
|
+
version=version,
|
|
164
|
+
controller=controller,
|
|
165
|
+
image_cache=ic,
|
|
166
|
+
interactive_mode=interactive_mode or debug,
|
|
167
|
+
)
|
|
168
|
+
# Create a coroutine to watch for errors
|
|
169
|
+
controller_failure = controller.watch_for_errors()
|
|
170
|
+
|
|
171
|
+
# Run both coroutines concurrently and wait for first to finish and cancel the other
|
|
172
|
+
async def _run_and_stop():
|
|
173
|
+
loop = asyncio.get_event_loop()
|
|
174
|
+
loop.set_exception_handler(flyte.errors.silence_grpc_polling_error)
|
|
175
|
+
try:
|
|
176
|
+
await utils.run_coros(controller_failure, task_coroutine)
|
|
177
|
+
await controller.stop()
|
|
178
|
+
except flyte.errors.RuntimeSystemError as e:
|
|
179
|
+
logger.error(f"Runtime system error: {e}")
|
|
180
|
+
from flyte._internal.runtime.convert import convert_from_native_to_error
|
|
181
|
+
from flyte._internal.runtime.io import upload_error
|
|
182
|
+
|
|
183
|
+
logger.error(f"Flyte runtime failed for action {name} with run name {run_name}, error: {e}")
|
|
184
|
+
err = convert_from_native_to_error(e)
|
|
185
|
+
path = await upload_error(err.err, outputs_path)
|
|
186
|
+
logger.error(f"Run {run_name} Action {name} failed with error: {err}. Uploaded error to {path}")
|
|
187
|
+
await controller.stop()
|
|
188
|
+
raise
|
|
189
|
+
|
|
190
|
+
asyncio.run(_run_and_stop())
|
|
191
|
+
logger.warning(f"Flyte runtime completed for action {name} with run name {run_name}")
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
if __name__ == "__main__":
|
|
195
|
+
_pass_through()
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: flyte
|
|
3
|
+
Version: 2.0.0b32
|
|
4
|
+
Summary: Add your description here
|
|
5
|
+
Author-email: Ketan Umare <kumare3@users.noreply.github.com>
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Dist: aiofiles>=24.1.0
|
|
10
|
+
Requires-Dist: click>=8.2.1
|
|
11
|
+
Requires-Dist: flyteidl<2.0.0,>=1.15.4b0
|
|
12
|
+
Requires-Dist: cloudpickle>=3.1.1
|
|
13
|
+
Requires-Dist: fsspec>=2025.3.0
|
|
14
|
+
Requires-Dist: grpcio>=1.71.0
|
|
15
|
+
Requires-Dist: obstore>=0.7.3
|
|
16
|
+
Requires-Dist: protobuf>=6.30.1
|
|
17
|
+
Requires-Dist: pydantic>=2.10.6
|
|
18
|
+
Requires-Dist: pyyaml>=6.0.2
|
|
19
|
+
Requires-Dist: rich-click==1.8.9
|
|
20
|
+
Requires-Dist: httpx<1.0.0,>=0.28.1
|
|
21
|
+
Requires-Dist: keyring>=25.6.0
|
|
22
|
+
Requires-Dist: msgpack>=1.1.0
|
|
23
|
+
Requires-Dist: toml>=0.10.2
|
|
24
|
+
Requires-Dist: async-lru>=2.0.5
|
|
25
|
+
Requires-Dist: mashumaro
|
|
26
|
+
Requires-Dist: dataclasses_json
|
|
27
|
+
Requires-Dist: aiolimiter>=1.2.1
|
|
28
|
+
Requires-Dist: flyteidl2==2.0.0a14
|
|
29
|
+
Provides-Extra: aiosqlite
|
|
30
|
+
Requires-Dist: aiosqlite>=0.21.0; extra == "aiosqlite"
|
|
31
|
+
Provides-Extra: connector
|
|
32
|
+
Requires-Dist: grpcio-health-checking; extra == "connector"
|
|
33
|
+
Requires-Dist: httpx; extra == "connector"
|
|
34
|
+
Requires-Dist: prometheus-client; extra == "connector"
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
|
|
37
|
+
# Flyte 2 SDK ๐
|
|
38
|
+
|
|
39
|
+
**Type-safe, distributed orchestration of agents, ML pipelines, and more โ in pure Python with async/await or sync!**
|
|
40
|
+
|
|
41
|
+
[](https://pypi.org/project/flyte/)
|
|
42
|
+
[](https://pypi.org/project/flyte/)
|
|
43
|
+
[](LICENSE)
|
|
44
|
+
|
|
45
|
+
> โก **Pure Python workflows** โข ๐ **Async-first parallelism** โข ๐ ๏ธ **Zero DSL constraints** โข ๐ **Sub-task observability**
|
|
46
|
+
|
|
47
|
+
## ๐ Ecosystem & Resources
|
|
48
|
+
|
|
49
|
+
- **๐ Documentation**: [Docs Link](https://www.union.ai/docs/v2/flyte/user-guide/)
|
|
50
|
+
- **โถ๏ธ Getting Started**: [Docs Link](https://www.union.ai/docs/v2/flyte/user-guide/getting-started/)
|
|
51
|
+
- **๐ฌ Community**: [Slack](https://slack.flyte.org/) | [GitHub Discussions](https://github.com/flyteorg/flyte/discussions)
|
|
52
|
+
- **๐ Examples**: [GitHub Examples](https://github.com/flyteorg/flyte-sdk/tree/main/examples)
|
|
53
|
+
- **๐ Issues**: [Bug Reports](https://github.com/flyteorg/flyte/issues)
|
|
54
|
+
|
|
55
|
+
## What is Flyte 2?
|
|
56
|
+
|
|
57
|
+
Flyte 2 represents a fundamental shift from constrained domain-specific languages to **pure Python workflows**. Write data pipelines, ML training jobs, and distributed compute exactly like you write Pythonโbecause it *is* Python.
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
import flyte
|
|
61
|
+
|
|
62
|
+
env = flyte.TaskEnvironment("hello_world")
|
|
63
|
+
|
|
64
|
+
@env.task
|
|
65
|
+
async def process_data(data: list[str]) -> list[str]:
|
|
66
|
+
# Use any Python construct: loops, conditionals, try/except
|
|
67
|
+
results = []
|
|
68
|
+
for item in data:
|
|
69
|
+
if len(item) > 5:
|
|
70
|
+
results.append(await transform_item(item))
|
|
71
|
+
return results
|
|
72
|
+
|
|
73
|
+
@env.task
|
|
74
|
+
async def transform_item(item: str) -> str:
|
|
75
|
+
return f"processed: {item.upper()}"
|
|
76
|
+
|
|
77
|
+
if __name__ == "__main__":
|
|
78
|
+
flyte.init()
|
|
79
|
+
result = flyte.run(process_data, data=["hello", "world", "flyte"])
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## ๐ Why Flyte 2?
|
|
83
|
+
|
|
84
|
+
| Feature Highlight | Flyte 1 | Flyte 2 |
|
|
85
|
+
|-| ------- | ------- |
|
|
86
|
+
| **No More Workflow DSL** | โ `@workflow` decorators with Python subset limitations | โ
**Pure Python**: loops, conditionals, error handling, dynamic structures |
|
|
87
|
+
| **Async-First Parallelism** | โ Custom `map()` functions and workflow-specific parallel constructs | โ
**Native `asyncio`**: `await asyncio.gather()` for distributed parallel execution |
|
|
88
|
+
| **Fine-Grained Observability** | โ Task-level logging only | โ
**Function-level tracing** with `@flyte.trace` for sub-task checkpoints |
|
|
89
|
+
|
|
90
|
+
## ๐ Quick Start
|
|
91
|
+
|
|
92
|
+
### Installation
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
# Install uv package manager
|
|
96
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
97
|
+
|
|
98
|
+
# Create virtual environment
|
|
99
|
+
uv venv && source .venv/bin/activate
|
|
100
|
+
|
|
101
|
+
# Install Flyte 2 (beta)
|
|
102
|
+
uv pip install --prerelease=allow flyte
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Your First Workflow
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
# hello.py
|
|
109
|
+
# /// script
|
|
110
|
+
# requires-python = ">=3.10"
|
|
111
|
+
# dependencies = ["flyte>=2.0.0b0"]
|
|
112
|
+
# ///
|
|
113
|
+
|
|
114
|
+
import flyte
|
|
115
|
+
|
|
116
|
+
env = flyte.TaskEnvironment(
|
|
117
|
+
name="hello_world",
|
|
118
|
+
resources=flyte.Resources(memory="250Mi")
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
@env.task
|
|
122
|
+
def calculate(x: int) -> int:
|
|
123
|
+
return x * 2 + 5
|
|
124
|
+
|
|
125
|
+
@env.task
|
|
126
|
+
async def main(numbers: list[int]) -> float:
|
|
127
|
+
# Parallel execution across distributed containers
|
|
128
|
+
results = await asyncio.gather(*[
|
|
129
|
+
calculate.aio(num) for num in numbers
|
|
130
|
+
])
|
|
131
|
+
return sum(results) / len(results)
|
|
132
|
+
|
|
133
|
+
if __name__ == "__main__":
|
|
134
|
+
flyte.init_from_config("config.yaml")
|
|
135
|
+
run = flyte.run(main, numbers=list(range(10)))
|
|
136
|
+
print(f"Result: {run.result}")
|
|
137
|
+
print(f"View at: {run.url}")
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
# Run locally, execute remotely
|
|
142
|
+
uv run --prerelease=allow hello.py
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## ๐๏ธ Core Concepts
|
|
146
|
+
|
|
147
|
+
### **TaskEnvironments**: Container Configuration Made Simple
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
# Group tasks with shared configuration
|
|
151
|
+
env = flyte.TaskEnvironment(
|
|
152
|
+
name="ml_pipeline",
|
|
153
|
+
image=flyte.Image.from_debian_base().with_pip_packages(
|
|
154
|
+
"torch", "pandas", "scikit-learn"
|
|
155
|
+
),
|
|
156
|
+
resources=flyte.Resources(cpu=4, memory="8Gi", gpu=1),
|
|
157
|
+
reusable=flyte.ReusePolicy(replicas=3, idle_ttl=300)
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
@env.task
|
|
161
|
+
def train_model(data: flyte.File) -> flyte.File:
|
|
162
|
+
# Runs in configured container with GPU access
|
|
163
|
+
pass
|
|
164
|
+
|
|
165
|
+
@env.task
|
|
166
|
+
def evaluate_model(model: flyte.File, test_data: flyte.File) -> dict:
|
|
167
|
+
# Same container configuration, different instance
|
|
168
|
+
pass
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### **Pure Python Workflows**: No More DSL Constraints
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
@env.task
|
|
175
|
+
async def dynamic_pipeline(config: dict) -> list[str]:
|
|
176
|
+
results = []
|
|
177
|
+
|
|
178
|
+
# โ
Use any Python construct
|
|
179
|
+
for dataset in config["datasets"]:
|
|
180
|
+
try:
|
|
181
|
+
# โ
Native error handling
|
|
182
|
+
if dataset["type"] == "batch":
|
|
183
|
+
result = await process_batch(dataset)
|
|
184
|
+
else:
|
|
185
|
+
result = await process_stream(dataset)
|
|
186
|
+
results.append(result)
|
|
187
|
+
except ValidationError as e:
|
|
188
|
+
# โ
Custom error recovery
|
|
189
|
+
result = await handle_error(dataset, e)
|
|
190
|
+
results.append(result)
|
|
191
|
+
|
|
192
|
+
return results
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### **Async Parallelism**: Distributed by Default
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
@env.task
|
|
199
|
+
async def parallel_training(hyperparams: list[dict]) -> dict:
|
|
200
|
+
# Each model trains on separate infrastructure
|
|
201
|
+
models = await asyncio.gather(*[
|
|
202
|
+
train_model.aio(params) for params in hyperparams
|
|
203
|
+
])
|
|
204
|
+
|
|
205
|
+
# Evaluate all models in parallel
|
|
206
|
+
evaluations = await asyncio.gather(*[
|
|
207
|
+
evaluate_model.aio(model) for model in models
|
|
208
|
+
])
|
|
209
|
+
|
|
210
|
+
# Find best model
|
|
211
|
+
best_idx = max(range(len(evaluations)),
|
|
212
|
+
key=lambda i: evaluations[i]["accuracy"])
|
|
213
|
+
return {"best_model": models[best_idx], "accuracy": evaluations[best_idx]}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## ๐ฏ Advanced Features
|
|
217
|
+
|
|
218
|
+
### **Sub-Task Observability with Tracing**
|
|
219
|
+
|
|
220
|
+
```python
|
|
221
|
+
@flyte.trace
|
|
222
|
+
async def expensive_computation(data: str) -> str:
|
|
223
|
+
# Function-level checkpointing - recoverable on failure
|
|
224
|
+
result = await call_external_api(data)
|
|
225
|
+
return process_result(result)
|
|
226
|
+
|
|
227
|
+
@env.task(cache=flyte.Cache(behavior="auto"))
|
|
228
|
+
async def main_task(inputs: list[str]) -> list[str]:
|
|
229
|
+
results = []
|
|
230
|
+
for inp in inputs:
|
|
231
|
+
# If task fails here, it resumes from the last successful trace
|
|
232
|
+
result = await expensive_computation(inp)
|
|
233
|
+
results.append(result)
|
|
234
|
+
return results
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### **Remote Task Execution**
|
|
238
|
+
|
|
239
|
+
```python
|
|
240
|
+
import flyte.remote
|
|
241
|
+
|
|
242
|
+
# Reference tasks deployed elsewhere
|
|
243
|
+
torch_task = flyte.remote.Task.get("torch_env.train_model", auto_version="latest")
|
|
244
|
+
spark_task = flyte.remote.Task.get("spark_env.process_data", auto_version="latest")
|
|
245
|
+
|
|
246
|
+
@env.task
|
|
247
|
+
async def orchestrator(raw_data: flyte.File) -> flyte.File:
|
|
248
|
+
# Execute Spark job on big data cluster
|
|
249
|
+
processed = await spark_task(raw_data)
|
|
250
|
+
|
|
251
|
+
# Execute PyTorch training on GPU cluster
|
|
252
|
+
model = await torch_task(processed)
|
|
253
|
+
|
|
254
|
+
return model
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
## ๐ Native Jupyter Integration
|
|
258
|
+
|
|
259
|
+
Run and monitor workflows directly from notebooks:
|
|
260
|
+
|
|
261
|
+
```python
|
|
262
|
+
# In Jupyter cell
|
|
263
|
+
import flyte
|
|
264
|
+
|
|
265
|
+
flyte.init_from_config()
|
|
266
|
+
run = flyte.run(my_workflow, data=large_dataset)
|
|
267
|
+
|
|
268
|
+
# Stream logs in real-time
|
|
269
|
+
run.logs.stream()
|
|
270
|
+
|
|
271
|
+
# Get outputs when complete
|
|
272
|
+
results = run.wait()
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## ๐ง Configuration & Deployment
|
|
276
|
+
|
|
277
|
+
### Configuration File
|
|
278
|
+
|
|
279
|
+
```yaml
|
|
280
|
+
# config.yaml
|
|
281
|
+
endpoint: https://my-flyte-instance.com
|
|
282
|
+
project: ml-team
|
|
283
|
+
domain: production
|
|
284
|
+
image:
|
|
285
|
+
builder: local
|
|
286
|
+
registry: ghcr.io/my-org
|
|
287
|
+
auth:
|
|
288
|
+
type: oauth2
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Deploy and Run
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
# Deploy tasks to remote cluster
|
|
295
|
+
flyte deploy my_workflow.py
|
|
296
|
+
|
|
297
|
+
# Run deployed workflow
|
|
298
|
+
flyte run my_workflow --input-file params.json
|
|
299
|
+
|
|
300
|
+
# Monitor execution
|
|
301
|
+
flyte logs <execution-id>
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
## Migration from Flyte 1
|
|
305
|
+
|
|
306
|
+
| Flyte 1 | Flyte 2 |
|
|
307
|
+
|---------|---------|
|
|
308
|
+
| `@workflow` + `@task` | `@env.task` only |
|
|
309
|
+
| `flytekit.map()` | `await asyncio.gather()` |
|
|
310
|
+
| `@dynamic` workflows | Regular `@env.task` with loops |
|
|
311
|
+
| `flytekit.conditional()` | Python `if/else` |
|
|
312
|
+
| `LaunchPlan` schedules | `@env.task(on_schedule=...)` |
|
|
313
|
+
| Workflow failure handlers | Python `try/except` |
|
|
314
|
+
|
|
315
|
+
## ๐ค Contributing
|
|
316
|
+
|
|
317
|
+
We welcome contributions! Whether it's:
|
|
318
|
+
|
|
319
|
+
- ๐ **Bug fixes**
|
|
320
|
+
- โจ **New features**
|
|
321
|
+
- ๐ **Documentation improvements**
|
|
322
|
+
- ๐งช **Testing enhancements**
|
|
323
|
+
|
|
324
|
+
### Setup & Iteration Cycle
|
|
325
|
+
To get started, make sure you start from a new virtual environment and install this package in editable mode with any of the supported Python versions, from 3.10 to 3.13.
|
|
326
|
+
|
|
327
|
+
```bash
|
|
328
|
+
uv venv --python 3.13
|
|
329
|
+
uv pip install -e .
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
Besides from picking up local code changes, installing the package in editable mode
|
|
333
|
+
also changes the definition of the default `Image()` object to use a locally
|
|
334
|
+
build wheel. You will need to build said wheel by yourself though, with the `make dist` target.
|
|
335
|
+
|
|
336
|
+
```bash
|
|
337
|
+
make dist
|
|
338
|
+
python maint_tools/build_default_image.py
|
|
339
|
+
```
|
|
340
|
+
You'll need to have a local docker daemon running for this. The build script does nothing
|
|
341
|
+
more than invoke the local image builder, which will create a buildx builder named `flytex` if not present. Note that only members of the `Flyte Maintainers` group has
|
|
342
|
+
access to push to the default registry. If you don't have access, please make sure to
|
|
343
|
+
specify the registry and name to the build script.
|
|
344
|
+
|
|
345
|
+
```bash
|
|
346
|
+
python maint_tools/build_default_image.py --registry ghcr.io/my-org --name my-flyte-image
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
## ๐ License
|
|
350
|
+
|
|
351
|
+
Flyte 2 is licensed under the [Apache 2.0 License](LICENSE).
|