flyte 0.0.1b0__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 +62 -0
- flyte/_api_commons.py +3 -0
- flyte/_bin/__init__.py +0 -0
- flyte/_bin/runtime.py +126 -0
- flyte/_build.py +25 -0
- flyte/_cache/__init__.py +12 -0
- flyte/_cache/cache.py +146 -0
- flyte/_cache/defaults.py +9 -0
- flyte/_cache/policy_function_body.py +42 -0
- flyte/_cli/__init__.py +0 -0
- flyte/_cli/_common.py +287 -0
- flyte/_cli/_create.py +42 -0
- flyte/_cli/_delete.py +23 -0
- flyte/_cli/_deploy.py +140 -0
- flyte/_cli/_get.py +235 -0
- flyte/_cli/_run.py +152 -0
- flyte/_cli/main.py +72 -0
- flyte/_code_bundle/__init__.py +8 -0
- flyte/_code_bundle/_ignore.py +113 -0
- flyte/_code_bundle/_packaging.py +187 -0
- flyte/_code_bundle/_utils.py +339 -0
- flyte/_code_bundle/bundle.py +178 -0
- flyte/_context.py +146 -0
- flyte/_datastructures.py +342 -0
- flyte/_deploy.py +202 -0
- flyte/_doc.py +29 -0
- flyte/_docstring.py +32 -0
- flyte/_environment.py +43 -0
- flyte/_group.py +31 -0
- flyte/_hash.py +23 -0
- flyte/_image.py +760 -0
- flyte/_initialize.py +634 -0
- flyte/_interface.py +84 -0
- flyte/_internal/__init__.py +3 -0
- flyte/_internal/controllers/__init__.py +115 -0
- flyte/_internal/controllers/_local_controller.py +118 -0
- flyte/_internal/controllers/_trace.py +40 -0
- flyte/_internal/controllers/pbhash.py +39 -0
- flyte/_internal/controllers/remote/__init__.py +40 -0
- flyte/_internal/controllers/remote/_action.py +141 -0
- flyte/_internal/controllers/remote/_client.py +43 -0
- flyte/_internal/controllers/remote/_controller.py +361 -0
- flyte/_internal/controllers/remote/_core.py +402 -0
- flyte/_internal/controllers/remote/_informer.py +361 -0
- flyte/_internal/controllers/remote/_service_protocol.py +50 -0
- flyte/_internal/imagebuild/__init__.py +11 -0
- flyte/_internal/imagebuild/docker_builder.py +416 -0
- flyte/_internal/imagebuild/image_builder.py +241 -0
- flyte/_internal/imagebuild/remote_builder.py +0 -0
- flyte/_internal/resolvers/__init__.py +0 -0
- flyte/_internal/resolvers/_task_module.py +54 -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 +199 -0
- flyte/_internal/runtime/entrypoints.py +135 -0
- flyte/_internal/runtime/io.py +136 -0
- flyte/_internal/runtime/resources_serde.py +138 -0
- flyte/_internal/runtime/task_serde.py +210 -0
- flyte/_internal/runtime/taskrunner.py +190 -0
- flyte/_internal/runtime/types_serde.py +54 -0
- flyte/_logging.py +124 -0
- flyte/_protos/__init__.py +0 -0
- flyte/_protos/common/authorization_pb2.py +66 -0
- flyte/_protos/common/authorization_pb2.pyi +108 -0
- flyte/_protos/common/authorization_pb2_grpc.py +4 -0
- flyte/_protos/common/identifier_pb2.py +71 -0
- flyte/_protos/common/identifier_pb2.pyi +82 -0
- flyte/_protos/common/identifier_pb2_grpc.py +4 -0
- flyte/_protos/common/identity_pb2.py +48 -0
- flyte/_protos/common/identity_pb2.pyi +72 -0
- flyte/_protos/common/identity_pb2_grpc.py +4 -0
- flyte/_protos/common/list_pb2.py +36 -0
- flyte/_protos/common/list_pb2.pyi +69 -0
- flyte/_protos/common/list_pb2_grpc.py +4 -0
- flyte/_protos/common/policy_pb2.py +37 -0
- flyte/_protos/common/policy_pb2.pyi +27 -0
- flyte/_protos/common/policy_pb2_grpc.py +4 -0
- flyte/_protos/common/role_pb2.py +37 -0
- flyte/_protos/common/role_pb2.pyi +53 -0
- flyte/_protos/common/role_pb2_grpc.py +4 -0
- flyte/_protos/common/runtime_version_pb2.py +28 -0
- flyte/_protos/common/runtime_version_pb2.pyi +24 -0
- flyte/_protos/common/runtime_version_pb2_grpc.py +4 -0
- flyte/_protos/logs/dataplane/payload_pb2.py +96 -0
- flyte/_protos/logs/dataplane/payload_pb2.pyi +168 -0
- flyte/_protos/logs/dataplane/payload_pb2_grpc.py +4 -0
- flyte/_protos/secret/definition_pb2.py +49 -0
- flyte/_protos/secret/definition_pb2.pyi +93 -0
- flyte/_protos/secret/definition_pb2_grpc.py +4 -0
- flyte/_protos/secret/payload_pb2.py +62 -0
- flyte/_protos/secret/payload_pb2.pyi +94 -0
- flyte/_protos/secret/payload_pb2_grpc.py +4 -0
- flyte/_protos/secret/secret_pb2.py +38 -0
- flyte/_protos/secret/secret_pb2.pyi +6 -0
- flyte/_protos/secret/secret_pb2_grpc.py +198 -0
- flyte/_protos/secret/secret_pb2_grpc_grpc.py +198 -0
- flyte/_protos/validate/validate/validate_pb2.py +76 -0
- flyte/_protos/workflow/node_execution_service_pb2.py +26 -0
- flyte/_protos/workflow/node_execution_service_pb2.pyi +4 -0
- flyte/_protos/workflow/node_execution_service_pb2_grpc.py +32 -0
- flyte/_protos/workflow/queue_service_pb2.py +106 -0
- flyte/_protos/workflow/queue_service_pb2.pyi +141 -0
- flyte/_protos/workflow/queue_service_pb2_grpc.py +172 -0
- flyte/_protos/workflow/run_definition_pb2.py +128 -0
- flyte/_protos/workflow/run_definition_pb2.pyi +310 -0
- flyte/_protos/workflow/run_definition_pb2_grpc.py +4 -0
- flyte/_protos/workflow/run_logs_service_pb2.py +41 -0
- flyte/_protos/workflow/run_logs_service_pb2.pyi +28 -0
- flyte/_protos/workflow/run_logs_service_pb2_grpc.py +69 -0
- flyte/_protos/workflow/run_service_pb2.py +133 -0
- flyte/_protos/workflow/run_service_pb2.pyi +175 -0
- flyte/_protos/workflow/run_service_pb2_grpc.py +412 -0
- flyte/_protos/workflow/state_service_pb2.py +58 -0
- flyte/_protos/workflow/state_service_pb2.pyi +71 -0
- flyte/_protos/workflow/state_service_pb2_grpc.py +138 -0
- flyte/_protos/workflow/task_definition_pb2.py +72 -0
- flyte/_protos/workflow/task_definition_pb2.pyi +65 -0
- flyte/_protos/workflow/task_definition_pb2_grpc.py +4 -0
- flyte/_protos/workflow/task_service_pb2.py +44 -0
- flyte/_protos/workflow/task_service_pb2.pyi +31 -0
- flyte/_protos/workflow/task_service_pb2_grpc.py +104 -0
- flyte/_resources.py +226 -0
- flyte/_retry.py +32 -0
- flyte/_reusable_environment.py +25 -0
- flyte/_run.py +411 -0
- flyte/_secret.py +61 -0
- flyte/_task.py +367 -0
- flyte/_task_environment.py +200 -0
- flyte/_timeout.py +47 -0
- flyte/_tools.py +27 -0
- flyte/_trace.py +128 -0
- flyte/_utils/__init__.py +20 -0
- flyte/_utils/asyn.py +119 -0
- flyte/_utils/coro_management.py +25 -0
- flyte/_utils/file_handling.py +72 -0
- flyte/_utils/helpers.py +108 -0
- flyte/_utils/lazy_module.py +54 -0
- flyte/_utils/uv_script_parser.py +49 -0
- flyte/_version.py +21 -0
- flyte/connectors/__init__.py +0 -0
- flyte/errors.py +143 -0
- flyte/extras/__init__.py +5 -0
- flyte/extras/_container.py +273 -0
- flyte/io/__init__.py +11 -0
- flyte/io/_dataframe.py +0 -0
- flyte/io/_dir.py +448 -0
- flyte/io/_file.py +468 -0
- flyte/io/pickle/__init__.py +0 -0
- flyte/io/pickle/transformer.py +117 -0
- flyte/io/structured_dataset/__init__.py +129 -0
- flyte/io/structured_dataset/basic_dfs.py +219 -0
- flyte/io/structured_dataset/structured_dataset.py +1061 -0
- flyte/py.typed +0 -0
- flyte/remote/__init__.py +25 -0
- flyte/remote/_client/__init__.py +0 -0
- flyte/remote/_client/_protocols.py +131 -0
- flyte/remote/_client/auth/__init__.py +12 -0
- flyte/remote/_client/auth/_authenticators/__init__.py +0 -0
- flyte/remote/_client/auth/_authenticators/base.py +397 -0
- flyte/remote/_client/auth/_authenticators/client_credentials.py +73 -0
- flyte/remote/_client/auth/_authenticators/device_code.py +118 -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 +184 -0
- flyte/remote/_client/auth/_client_config.py +83 -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 +143 -0
- flyte/remote/_client/auth/_token_client.py +260 -0
- flyte/remote/_client/auth/errors.py +16 -0
- flyte/remote/_client/controlplane.py +95 -0
- flyte/remote/_console.py +18 -0
- flyte/remote/_data.py +155 -0
- flyte/remote/_logs.py +116 -0
- flyte/remote/_project.py +86 -0
- flyte/remote/_run.py +873 -0
- flyte/remote/_secret.py +132 -0
- flyte/remote/_task.py +227 -0
- flyte/report/__init__.py +3 -0
- flyte/report/_report.py +178 -0
- flyte/report/_template.html +124 -0
- flyte/storage/__init__.py +24 -0
- flyte/storage/_remote_fs.py +34 -0
- flyte/storage/_storage.py +251 -0
- flyte/storage/_utils.py +5 -0
- flyte/types/__init__.py +13 -0
- flyte/types/_interface.py +25 -0
- flyte/types/_renderer.py +162 -0
- flyte/types/_string_literals.py +120 -0
- flyte/types/_type_engine.py +2210 -0
- flyte/types/_utils.py +80 -0
- flyte-0.0.1b0.dist-info/METADATA +179 -0
- flyte-0.0.1b0.dist-info/RECORD +390 -0
- flyte-0.0.1b0.dist-info/WHEEL +5 -0
- flyte-0.0.1b0.dist-info/entry_points.txt +3 -0
- flyte-0.0.1b0.dist-info/top_level.txt +1 -0
- union/__init__.py +54 -0
- union/_api_commons.py +3 -0
- union/_bin/__init__.py +0 -0
- union/_bin/runtime.py +113 -0
- union/_build.py +25 -0
- union/_cache/__init__.py +12 -0
- union/_cache/cache.py +141 -0
- union/_cache/defaults.py +9 -0
- union/_cache/policy_function_body.py +42 -0
- union/_cli/__init__.py +0 -0
- union/_cli/_common.py +263 -0
- union/_cli/_create.py +40 -0
- union/_cli/_delete.py +23 -0
- union/_cli/_deploy.py +120 -0
- union/_cli/_get.py +162 -0
- union/_cli/_params.py +579 -0
- union/_cli/_run.py +150 -0
- union/_cli/main.py +72 -0
- union/_code_bundle/__init__.py +8 -0
- union/_code_bundle/_ignore.py +113 -0
- union/_code_bundle/_packaging.py +187 -0
- union/_code_bundle/_utils.py +342 -0
- union/_code_bundle/bundle.py +176 -0
- union/_context.py +146 -0
- union/_datastructures.py +295 -0
- union/_deploy.py +185 -0
- union/_doc.py +29 -0
- union/_docstring.py +26 -0
- union/_environment.py +43 -0
- union/_group.py +31 -0
- union/_hash.py +23 -0
- union/_image.py +760 -0
- union/_initialize.py +585 -0
- union/_interface.py +84 -0
- union/_internal/__init__.py +3 -0
- union/_internal/controllers/__init__.py +77 -0
- union/_internal/controllers/_local_controller.py +77 -0
- union/_internal/controllers/pbhash.py +39 -0
- union/_internal/controllers/remote/__init__.py +40 -0
- union/_internal/controllers/remote/_action.py +131 -0
- union/_internal/controllers/remote/_client.py +43 -0
- union/_internal/controllers/remote/_controller.py +169 -0
- union/_internal/controllers/remote/_core.py +341 -0
- union/_internal/controllers/remote/_informer.py +260 -0
- union/_internal/controllers/remote/_service_protocol.py +44 -0
- union/_internal/imagebuild/__init__.py +11 -0
- union/_internal/imagebuild/docker_builder.py +416 -0
- union/_internal/imagebuild/image_builder.py +243 -0
- union/_internal/imagebuild/remote_builder.py +0 -0
- union/_internal/resolvers/__init__.py +0 -0
- union/_internal/resolvers/_task_module.py +31 -0
- union/_internal/resolvers/common.py +24 -0
- union/_internal/resolvers/default.py +27 -0
- union/_internal/runtime/__init__.py +0 -0
- union/_internal/runtime/convert.py +163 -0
- union/_internal/runtime/entrypoints.py +121 -0
- union/_internal/runtime/io.py +136 -0
- union/_internal/runtime/resources_serde.py +134 -0
- union/_internal/runtime/task_serde.py +202 -0
- union/_internal/runtime/taskrunner.py +179 -0
- union/_internal/runtime/types_serde.py +53 -0
- union/_logging.py +124 -0
- union/_protos/__init__.py +0 -0
- union/_protos/common/authorization_pb2.py +66 -0
- union/_protos/common/authorization_pb2.pyi +106 -0
- union/_protos/common/authorization_pb2_grpc.py +4 -0
- union/_protos/common/identifier_pb2.py +71 -0
- union/_protos/common/identifier_pb2.pyi +82 -0
- union/_protos/common/identifier_pb2_grpc.py +4 -0
- union/_protos/common/identity_pb2.py +48 -0
- union/_protos/common/identity_pb2.pyi +72 -0
- union/_protos/common/identity_pb2_grpc.py +4 -0
- union/_protos/common/list_pb2.py +36 -0
- union/_protos/common/list_pb2.pyi +69 -0
- union/_protos/common/list_pb2_grpc.py +4 -0
- union/_protos/common/policy_pb2.py +37 -0
- union/_protos/common/policy_pb2.pyi +27 -0
- union/_protos/common/policy_pb2_grpc.py +4 -0
- union/_protos/common/role_pb2.py +37 -0
- union/_protos/common/role_pb2.pyi +51 -0
- union/_protos/common/role_pb2_grpc.py +4 -0
- union/_protos/common/runtime_version_pb2.py +28 -0
- union/_protos/common/runtime_version_pb2.pyi +24 -0
- union/_protos/common/runtime_version_pb2_grpc.py +4 -0
- union/_protos/logs/dataplane/payload_pb2.py +96 -0
- union/_protos/logs/dataplane/payload_pb2.pyi +168 -0
- union/_protos/logs/dataplane/payload_pb2_grpc.py +4 -0
- union/_protos/secret/definition_pb2.py +49 -0
- union/_protos/secret/definition_pb2.pyi +93 -0
- union/_protos/secret/definition_pb2_grpc.py +4 -0
- union/_protos/secret/payload_pb2.py +62 -0
- union/_protos/secret/payload_pb2.pyi +94 -0
- union/_protos/secret/payload_pb2_grpc.py +4 -0
- union/_protos/secret/secret_pb2.py +38 -0
- union/_protos/secret/secret_pb2.pyi +6 -0
- union/_protos/secret/secret_pb2_grpc.py +198 -0
- union/_protos/validate/validate/validate_pb2.py +76 -0
- union/_protos/workflow/node_execution_service_pb2.py +26 -0
- union/_protos/workflow/node_execution_service_pb2.pyi +4 -0
- union/_protos/workflow/node_execution_service_pb2_grpc.py +32 -0
- union/_protos/workflow/queue_service_pb2.py +75 -0
- union/_protos/workflow/queue_service_pb2.pyi +103 -0
- union/_protos/workflow/queue_service_pb2_grpc.py +172 -0
- union/_protos/workflow/run_definition_pb2.py +100 -0
- union/_protos/workflow/run_definition_pb2.pyi +256 -0
- union/_protos/workflow/run_definition_pb2_grpc.py +4 -0
- union/_protos/workflow/run_logs_service_pb2.py +41 -0
- union/_protos/workflow/run_logs_service_pb2.pyi +28 -0
- union/_protos/workflow/run_logs_service_pb2_grpc.py +69 -0
- union/_protos/workflow/run_service_pb2.py +133 -0
- union/_protos/workflow/run_service_pb2.pyi +173 -0
- union/_protos/workflow/run_service_pb2_grpc.py +412 -0
- union/_protos/workflow/state_service_pb2.py +58 -0
- union/_protos/workflow/state_service_pb2.pyi +69 -0
- union/_protos/workflow/state_service_pb2_grpc.py +138 -0
- union/_protos/workflow/task_definition_pb2.py +72 -0
- union/_protos/workflow/task_definition_pb2.pyi +65 -0
- union/_protos/workflow/task_definition_pb2_grpc.py +4 -0
- union/_protos/workflow/task_service_pb2.py +44 -0
- union/_protos/workflow/task_service_pb2.pyi +31 -0
- union/_protos/workflow/task_service_pb2_grpc.py +104 -0
- union/_resources.py +226 -0
- union/_retry.py +32 -0
- union/_reusable_environment.py +25 -0
- union/_run.py +374 -0
- union/_secret.py +61 -0
- union/_task.py +354 -0
- union/_task_environment.py +186 -0
- union/_timeout.py +47 -0
- union/_tools.py +27 -0
- union/_utils/__init__.py +11 -0
- union/_utils/asyn.py +119 -0
- union/_utils/file_handling.py +71 -0
- union/_utils/helpers.py +46 -0
- union/_utils/lazy_module.py +54 -0
- union/_utils/uv_script_parser.py +49 -0
- union/_version.py +21 -0
- union/connectors/__init__.py +0 -0
- union/errors.py +128 -0
- union/extras/__init__.py +5 -0
- union/extras/_container.py +263 -0
- union/io/__init__.py +11 -0
- union/io/_dataframe.py +0 -0
- union/io/_dir.py +425 -0
- union/io/_file.py +418 -0
- union/io/pickle/__init__.py +0 -0
- union/io/pickle/transformer.py +117 -0
- union/io/structured_dataset/__init__.py +122 -0
- union/io/structured_dataset/basic_dfs.py +219 -0
- union/io/structured_dataset/structured_dataset.py +1057 -0
- union/py.typed +0 -0
- union/remote/__init__.py +23 -0
- union/remote/_client/__init__.py +0 -0
- union/remote/_client/_protocols.py +129 -0
- union/remote/_client/auth/__init__.py +12 -0
- union/remote/_client/auth/_authenticators/__init__.py +0 -0
- union/remote/_client/auth/_authenticators/base.py +391 -0
- union/remote/_client/auth/_authenticators/client_credentials.py +73 -0
- union/remote/_client/auth/_authenticators/device_code.py +120 -0
- union/remote/_client/auth/_authenticators/external_command.py +77 -0
- union/remote/_client/auth/_authenticators/factory.py +200 -0
- union/remote/_client/auth/_authenticators/pkce.py +515 -0
- union/remote/_client/auth/_channel.py +184 -0
- union/remote/_client/auth/_client_config.py +83 -0
- union/remote/_client/auth/_default_html.py +32 -0
- union/remote/_client/auth/_grpc_utils/__init__.py +0 -0
- union/remote/_client/auth/_grpc_utils/auth_interceptor.py +204 -0
- union/remote/_client/auth/_grpc_utils/default_metadata_interceptor.py +144 -0
- union/remote/_client/auth/_keyring.py +154 -0
- union/remote/_client/auth/_token_client.py +258 -0
- union/remote/_client/auth/errors.py +16 -0
- union/remote/_client/controlplane.py +86 -0
- union/remote/_data.py +149 -0
- union/remote/_logs.py +74 -0
- union/remote/_project.py +86 -0
- union/remote/_run.py +820 -0
- union/remote/_secret.py +132 -0
- union/remote/_task.py +193 -0
- union/report/__init__.py +3 -0
- union/report/_report.py +178 -0
- union/report/_template.html +124 -0
- union/storage/__init__.py +24 -0
- union/storage/_remote_fs.py +34 -0
- union/storage/_storage.py +247 -0
- union/storage/_utils.py +5 -0
- union/types/__init__.py +11 -0
- union/types/_renderer.py +162 -0
- union/types/_string_literals.py +120 -0
- union/types/_type_engine.py +2131 -0
- union/types/_utils.py +80 -0
union/_utils/asyn.py
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""Manages an async event loop on another thread. Developers should only require to call
|
|
2
|
+
sync to use the managed loop:
|
|
3
|
+
|
|
4
|
+
from flytekit.tools.asyn import run_sync
|
|
5
|
+
|
|
6
|
+
async def async_add(a: int, b: int) -> int:
|
|
7
|
+
return a + b
|
|
8
|
+
|
|
9
|
+
result = run_sync(async_add, a=10, b=12)
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import asyncio
|
|
13
|
+
import atexit
|
|
14
|
+
import functools
|
|
15
|
+
import os
|
|
16
|
+
import threading
|
|
17
|
+
from contextlib import contextmanager
|
|
18
|
+
from typing import Any, Awaitable, Callable, TypeVar
|
|
19
|
+
|
|
20
|
+
from typing_extensions import ParamSpec
|
|
21
|
+
|
|
22
|
+
from union._logging import logger
|
|
23
|
+
|
|
24
|
+
T = TypeVar("T")
|
|
25
|
+
|
|
26
|
+
P = ParamSpec("P")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@contextmanager
|
|
30
|
+
def _selector_policy():
|
|
31
|
+
original_policy = asyncio.get_event_loop_policy()
|
|
32
|
+
try:
|
|
33
|
+
if os.name == "nt" and hasattr(asyncio, "WindowsSelectorEventLoopPolicy"):
|
|
34
|
+
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
|
35
|
+
|
|
36
|
+
yield
|
|
37
|
+
finally:
|
|
38
|
+
asyncio.set_event_loop_policy(original_policy)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class _TaskRunner:
|
|
42
|
+
"""A task runner that runs an asyncio event loop on a background thread."""
|
|
43
|
+
|
|
44
|
+
def __init__(self) -> None:
|
|
45
|
+
self.__loop: asyncio.AbstractEventLoop | None = None
|
|
46
|
+
self.__runner_thread: threading.Thread | None = None
|
|
47
|
+
self.__lock = threading.Lock()
|
|
48
|
+
atexit.register(self._close)
|
|
49
|
+
|
|
50
|
+
def _close(self) -> None:
|
|
51
|
+
if self.__loop:
|
|
52
|
+
self.__loop.stop()
|
|
53
|
+
|
|
54
|
+
def _execute(self) -> None:
|
|
55
|
+
loop = self.__loop
|
|
56
|
+
assert loop is not None
|
|
57
|
+
try:
|
|
58
|
+
loop.run_forever()
|
|
59
|
+
finally:
|
|
60
|
+
loop.close()
|
|
61
|
+
|
|
62
|
+
def get_exc_handler(self):
|
|
63
|
+
def exc_handler(loop, context):
|
|
64
|
+
logger.error(
|
|
65
|
+
f"Taskrunner for {self.__runner_thread.name if self.__runner_thread else 'no thread'} caught"
|
|
66
|
+
f" exception in {loop}: {context}"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
return exc_handler
|
|
70
|
+
|
|
71
|
+
def run(self, coro: Any) -> Any:
|
|
72
|
+
"""Synchronously run a coroutine on a background thread."""
|
|
73
|
+
name = f"{threading.current_thread().name} : loop-runner"
|
|
74
|
+
with self.__lock:
|
|
75
|
+
if self.__loop is None:
|
|
76
|
+
with _selector_policy():
|
|
77
|
+
self.__loop = asyncio.new_event_loop()
|
|
78
|
+
|
|
79
|
+
exc_handler = self.get_exc_handler()
|
|
80
|
+
self.__loop.set_exception_handler(exc_handler)
|
|
81
|
+
self.__runner_thread = threading.Thread(target=self._execute, daemon=True, name=name)
|
|
82
|
+
self.__runner_thread.start()
|
|
83
|
+
fut = asyncio.run_coroutine_threadsafe(coro, self.__loop)
|
|
84
|
+
|
|
85
|
+
res = fut.result(None)
|
|
86
|
+
|
|
87
|
+
return res
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class _AsyncLoopManager:
|
|
91
|
+
def __init__(self):
|
|
92
|
+
self._runner_map: dict[str, _TaskRunner] = {}
|
|
93
|
+
|
|
94
|
+
def run_sync(self, coro_func: Callable[..., Awaitable[T]], *args, **kwargs) -> T:
|
|
95
|
+
"""
|
|
96
|
+
This should be called from synchronous functions to run an async function.
|
|
97
|
+
"""
|
|
98
|
+
name = threading.current_thread().name + f"PID:{os.getpid()}"
|
|
99
|
+
coro = coro_func(*args, **kwargs)
|
|
100
|
+
if name not in self._runner_map:
|
|
101
|
+
if len(self._runner_map) > 500:
|
|
102
|
+
logger.warning(
|
|
103
|
+
"More than 500 event loop runners created!!! This could be a case of runaway recursion..."
|
|
104
|
+
)
|
|
105
|
+
self._runner_map[name] = _TaskRunner()
|
|
106
|
+
return self._runner_map[name].run(coro)
|
|
107
|
+
|
|
108
|
+
def synced(self, coro_func: Callable[P, Awaitable[T]]) -> Callable[P, T]:
|
|
109
|
+
"""Make loop run coroutine until it returns. Runs in other thread"""
|
|
110
|
+
|
|
111
|
+
@functools.wraps(coro_func)
|
|
112
|
+
def wrapped(*args: Any, **kwargs: Any) -> T:
|
|
113
|
+
return self.run_sync(coro_func, *args, **kwargs)
|
|
114
|
+
|
|
115
|
+
return wrapped
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
loop_manager = _AsyncLoopManager()
|
|
119
|
+
run_sync = loop_manager.run_sync
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import hashlib
|
|
4
|
+
import os
|
|
5
|
+
import pathlib
|
|
6
|
+
import stat
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import List, Optional, Union
|
|
9
|
+
|
|
10
|
+
from union._logging import logger
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def filehash_update(path: pathlib.Path, hasher: hashlib._Hash) -> None:
|
|
14
|
+
blocksize = 65536
|
|
15
|
+
with open(path, "rb") as f:
|
|
16
|
+
bytes = f.read(blocksize)
|
|
17
|
+
while bytes:
|
|
18
|
+
hasher.update(bytes)
|
|
19
|
+
bytes = f.read(blocksize)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _pathhash_update(path: Union[os.PathLike, str], hasher: hashlib._Hash) -> None:
|
|
23
|
+
path_list = path.split(os.sep)
|
|
24
|
+
hasher.update("".join(path_list).encode("utf-8"))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def update_hasher_for_source(
|
|
28
|
+
source: Union[os.PathLike, List[os.PathLike]], hasher: hashlib._Hash, filter: Optional[callable] = None
|
|
29
|
+
) -> str:
|
|
30
|
+
"""
|
|
31
|
+
Walks the entirety of the source dir to compute a deterministic md5 hex digest of the dir contents.
|
|
32
|
+
:param os.PathLike source:
|
|
33
|
+
:param callable filter:
|
|
34
|
+
:return Text:
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def compute_digest_for_file(path: os.PathLike, rel_path: os.PathLike) -> None:
|
|
38
|
+
# Only consider files that exist (e.g. disregard symlinks that point to non-existent files)
|
|
39
|
+
if not os.path.exists(path):
|
|
40
|
+
logger.info(f"Skipping non-existent file {path}")
|
|
41
|
+
return
|
|
42
|
+
|
|
43
|
+
# Skip socket files
|
|
44
|
+
if stat.S_ISSOCK(os.stat(path).st_mode):
|
|
45
|
+
logger.info(f"Skip socket file {path}")
|
|
46
|
+
return
|
|
47
|
+
|
|
48
|
+
if filter:
|
|
49
|
+
if filter(rel_path):
|
|
50
|
+
return
|
|
51
|
+
|
|
52
|
+
filehash_update(Path(path), hasher)
|
|
53
|
+
_pathhash_update(rel_path, hasher)
|
|
54
|
+
|
|
55
|
+
def compute_digest_for_dir(source: os.PathLike) -> None:
|
|
56
|
+
for root, _, files in os.walk(source, topdown=True):
|
|
57
|
+
files.sort()
|
|
58
|
+
|
|
59
|
+
for fname in files:
|
|
60
|
+
abspath = os.path.join(root, fname)
|
|
61
|
+
relpath = os.path.relpath(abspath, source)
|
|
62
|
+
compute_digest_for_file(abspath, relpath)
|
|
63
|
+
|
|
64
|
+
if isinstance(source, list):
|
|
65
|
+
for src in source:
|
|
66
|
+
if os.path.isdir(src):
|
|
67
|
+
compute_digest_for_dir(src)
|
|
68
|
+
else:
|
|
69
|
+
compute_digest_for_file(src, os.path.basename(src))
|
|
70
|
+
else:
|
|
71
|
+
compute_digest_for_dir(source)
|
union/_utils/helpers.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import string
|
|
3
|
+
import typing
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def load_proto_from_file(pb2_type, path):
|
|
8
|
+
with open(path, "rb") as reader:
|
|
9
|
+
out = pb2_type()
|
|
10
|
+
out.ParseFromString(reader.read())
|
|
11
|
+
return out
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def write_proto_to_file(proto, path):
|
|
15
|
+
Path(os.path.dirname(path)).mkdir(parents=True, exist_ok=True)
|
|
16
|
+
with open(path, "wb") as writer:
|
|
17
|
+
writer.write(proto.SerializeToString())
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def str2bool(value: typing.Optional[str]) -> bool:
|
|
21
|
+
"""
|
|
22
|
+
Convert a string to a boolean. This is useful for parsing environment variables.
|
|
23
|
+
:param value: The string to convert to a boolean
|
|
24
|
+
:return: the boolean value
|
|
25
|
+
"""
|
|
26
|
+
if value is None:
|
|
27
|
+
return False
|
|
28
|
+
return value.lower() in ("true", "t", "1")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
BASE62_ALPHABET = string.digits + string.ascii_letters # 0-9 + A-Z + a-z (62 characters)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def base62_encode(byte_data: bytes) -> str:
|
|
35
|
+
# Convert bytes to a big integer
|
|
36
|
+
num = int.from_bytes(byte_data, byteorder="big")
|
|
37
|
+
|
|
38
|
+
# Convert integer to base62 string
|
|
39
|
+
if num == 0:
|
|
40
|
+
return BASE62_ALPHABET[0]
|
|
41
|
+
|
|
42
|
+
base62 = []
|
|
43
|
+
while num:
|
|
44
|
+
num, rem = divmod(num, 62)
|
|
45
|
+
base62.append(BASE62_ALPHABET[rem])
|
|
46
|
+
return "".join(reversed(base62))
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import importlib.util
|
|
2
|
+
import sys
|
|
3
|
+
import types
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class _LazyModule(types.ModuleType):
|
|
7
|
+
"""
|
|
8
|
+
`lazy_module` returns an instance of this class if the module is not found in the python environment.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
def __init__(self, module_name: str):
|
|
12
|
+
super().__init__(module_name)
|
|
13
|
+
self._module_name = module_name
|
|
14
|
+
|
|
15
|
+
def __getattribute__(self, attr):
|
|
16
|
+
raise ImportError(f"Module {object.__getattribute__(self, '_module_name')} is not yet installed.")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def is_imported(module_name):
|
|
20
|
+
"""
|
|
21
|
+
This function is used to check if a module has been imported by the regular import.
|
|
22
|
+
Return false if module is lazy imported and not used yet.
|
|
23
|
+
"""
|
|
24
|
+
return (
|
|
25
|
+
module_name in sys.modules
|
|
26
|
+
and object.__getattribute__(lazy_module(module_name), "__class__").__name__ != "_LazyModule"
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def lazy_module(fullname):
|
|
31
|
+
"""
|
|
32
|
+
This function is used to lazily import modules. It is used in the following way:
|
|
33
|
+
.. code-block:: python
|
|
34
|
+
from flytekit.lazy_import import lazy_module
|
|
35
|
+
sklearn = lazy_module("sklearn")
|
|
36
|
+
sklearn.svm.SVC()
|
|
37
|
+
:param Text fullname: The full name of the module to import
|
|
38
|
+
"""
|
|
39
|
+
if fullname in sys.modules:
|
|
40
|
+
return sys.modules[fullname]
|
|
41
|
+
# https://docs.python.org/3/library/importlib.html#implementing-lazy-imports
|
|
42
|
+
spec = importlib.util.find_spec(fullname)
|
|
43
|
+
if spec is None or spec.loader is None:
|
|
44
|
+
# Return a lazy module if the module is not found in the python environment,
|
|
45
|
+
# so that we can raise a proper error when the user tries to access an attribute in the module.
|
|
46
|
+
# The reason to do this is because importlib.util.LazyLoader still requires
|
|
47
|
+
# the module to be installed even if you don't use it.
|
|
48
|
+
return _LazyModule(fullname)
|
|
49
|
+
loader = importlib.util.LazyLoader(spec.loader)
|
|
50
|
+
spec.loader = loader
|
|
51
|
+
module = importlib.util.module_from_spec(spec)
|
|
52
|
+
sys.modules[fullname] = module
|
|
53
|
+
loader.exec_module(module)
|
|
54
|
+
return module
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import pathlib
|
|
2
|
+
import re
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from typing import Dict, List, Optional
|
|
5
|
+
|
|
6
|
+
import toml
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class ToolUVConfig:
|
|
11
|
+
exclude_newer: Optional[str] = None
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class UVScriptMetadata:
|
|
16
|
+
requires_python: Optional[str] = None
|
|
17
|
+
dependencies: List[str] = field(default_factory=list)
|
|
18
|
+
tool: Optional[Dict[str, ToolUVConfig]] = None
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _extract_uv_metadata_block(text: str) -> str | None:
|
|
22
|
+
pattern = re.compile(r"# /// script\s*(.*?)# ///", re.DOTALL)
|
|
23
|
+
match = pattern.search(text)
|
|
24
|
+
if not match:
|
|
25
|
+
return None
|
|
26
|
+
lines = [line.lstrip("# ").rstrip() for line in match.group(1).splitlines()]
|
|
27
|
+
return "\n".join(lines)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def parse_uv_script_file(path: pathlib.Path) -> UVScriptMetadata:
|
|
31
|
+
if not path.exists() or not path.is_file():
|
|
32
|
+
raise FileNotFoundError(f"File not found: {path}")
|
|
33
|
+
|
|
34
|
+
text = path.read_text(encoding="utf-8")
|
|
35
|
+
raw_header = _extract_uv_metadata_block(text)
|
|
36
|
+
if raw_header is None:
|
|
37
|
+
raise ValueError("No uv metadata block found")
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
data = toml.loads(raw_header)
|
|
41
|
+
except toml.TomlDecodeError as e:
|
|
42
|
+
raise ValueError(f"Invalid TOML in metadata block: {e}")
|
|
43
|
+
|
|
44
|
+
tool_data = data.get("tool", {}).get("uv", {})
|
|
45
|
+
return UVScriptMetadata(
|
|
46
|
+
requires_python=data.get("requires-python"),
|
|
47
|
+
dependencies=data.get("dependencies", []),
|
|
48
|
+
tool={"uv": ToolUVConfig(exclude_newer=tool_data.get("exclude-newer"))} if tool_data else None,
|
|
49
|
+
)
|
union/_version.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# file generated by setuptools-scm
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
|
|
4
|
+
__all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
|
|
5
|
+
|
|
6
|
+
TYPE_CHECKING = False
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from typing import Tuple
|
|
9
|
+
from typing import Union
|
|
10
|
+
|
|
11
|
+
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
|
12
|
+
else:
|
|
13
|
+
VERSION_TUPLE = object
|
|
14
|
+
|
|
15
|
+
version: str
|
|
16
|
+
__version__: str
|
|
17
|
+
__version_tuple__: VERSION_TUPLE
|
|
18
|
+
version_tuple: VERSION_TUPLE
|
|
19
|
+
|
|
20
|
+
__version__ = version = '0.1.dev160+g7ce653a.d20250515'
|
|
21
|
+
__version_tuple__ = version_tuple = (0, 1, 'dev160', 'g7ce653a.d20250515')
|
|
File without changes
|
union/errors.py
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Exceptions raised by Union.
|
|
3
|
+
|
|
4
|
+
These errors are raised when the underlying task execution fails, either because of a user error, system error or an
|
|
5
|
+
unknown error.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Literal
|
|
9
|
+
|
|
10
|
+
ErrorKind = Literal["system", "unknown", "user"]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class RuntimeError(RuntimeError):
|
|
14
|
+
"""
|
|
15
|
+
Base class for all Union runtime errors. These errors are raised when the underlying task execution fails, either
|
|
16
|
+
because of a user error, system error or an unknown error.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, code: str, kind: ErrorKind, root_cause_message: str, worker: str | None = None):
|
|
20
|
+
super().__init__(root_cause_message)
|
|
21
|
+
self.code = code
|
|
22
|
+
self.kind = kind
|
|
23
|
+
self.worker = worker
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class InitializationError(RuntimeError):
|
|
27
|
+
"""
|
|
28
|
+
This error is raised when the Union system is tried to access without being initialized.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class RuntimeSystemError(RuntimeError):
|
|
33
|
+
"""
|
|
34
|
+
This error is raised when the underlying task execution fails because of a system error. This could be a bug in the
|
|
35
|
+
Union system or a bug in the user's code.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(self, code: str, message: str, worker: str | None = None):
|
|
39
|
+
super().__init__(code, "system", message, worker)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class UnionRpcError(RuntimeSystemError):
|
|
43
|
+
"""
|
|
44
|
+
This error is raised when communication with the Union server fails.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class RuntimeUserError(RuntimeError):
|
|
49
|
+
"""
|
|
50
|
+
This error is raised when the underlying task execution fails because of an error in the user's code.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
def __init__(self, code: str, message: str, worker: str | None = None):
|
|
54
|
+
super().__init__(code, "user", message, worker)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class RuntimeUnknownError(RuntimeError):
|
|
58
|
+
"""
|
|
59
|
+
This error is raised when the underlying task execution fails because of an unknown error.
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
def __init__(self, code: str, message: str, worker: str | None = None):
|
|
63
|
+
super().__init__(code, "unknown", message, worker)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class OOMError(RuntimeUserError):
|
|
67
|
+
"""
|
|
68
|
+
This error is raised when the underlying task execution fails because of an out-of-memory error.
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class TaskInterruptedError(RuntimeUserError):
|
|
73
|
+
"""
|
|
74
|
+
This error is raised when the underlying task execution is interrupted.
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class PrimaryContainerNotFoundError(RuntimeUserError):
|
|
79
|
+
"""
|
|
80
|
+
This error is raised when the primary container is not found.
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class TaskTimeoutError(RuntimeUserError):
|
|
85
|
+
"""
|
|
86
|
+
This error is raised when the underlying task execution runs for longer than the specified timeout.
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class RetriesExhaustedError(RuntimeUserError):
|
|
91
|
+
"""
|
|
92
|
+
This error is raised when the underlying task execution fails after all retries have been exhausted.
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class InvalidImageNameError(RuntimeUserError):
|
|
97
|
+
"""
|
|
98
|
+
This error is raised when the image name is invalid.
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class ImagePullBackOffError(RuntimeUserError):
|
|
103
|
+
"""
|
|
104
|
+
This error is raised when the image cannot be pulled.
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class CustomError(RuntimeUserError):
|
|
109
|
+
"""
|
|
110
|
+
This error is raised when the user raises a custom error.
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
def __init__(self, code: str, message: str):
|
|
114
|
+
super().__init__(code, message, "user")
|
|
115
|
+
|
|
116
|
+
@classmethod
|
|
117
|
+
def from_exception(cls, e: Exception):
|
|
118
|
+
"""
|
|
119
|
+
Create a CustomError from an exception. The exception's class name is used as the error code and the exception
|
|
120
|
+
message is used as the error message.
|
|
121
|
+
"""
|
|
122
|
+
return cls(e.__class__.__name__, str(e))
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class NotInTaskContextError(RuntimeUserError):
|
|
126
|
+
"""
|
|
127
|
+
This error is raised when the user tries to access the task context outside of a task.
|
|
128
|
+
"""
|