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/_datastructures.py
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import inspect
|
|
4
|
+
import os
|
|
5
|
+
import pathlib
|
|
6
|
+
import tempfile
|
|
7
|
+
from dataclasses import dataclass, field, replace
|
|
8
|
+
from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Tuple, Type
|
|
9
|
+
|
|
10
|
+
from union._docstring import Docstring
|
|
11
|
+
from union._interface import extract_return_annotation
|
|
12
|
+
from union._logging import logger
|
|
13
|
+
from union._utils.helpers import base62_encode
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from union._internal.imagebuild.image_builder import ImageCache
|
|
17
|
+
from union.report import Report
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def generate_random_name() -> str:
|
|
21
|
+
"""
|
|
22
|
+
Generate a random name for the task. This is used to create unique names for tasks.
|
|
23
|
+
TODO we can use unique-namer in the future, for now its just guids
|
|
24
|
+
"""
|
|
25
|
+
from uuid import uuid4
|
|
26
|
+
|
|
27
|
+
return str(uuid4()) # Placeholder for actual random name generation logic
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass(frozen=True, kw_only=True)
|
|
31
|
+
class ActionID:
|
|
32
|
+
"""
|
|
33
|
+
A class representing the ID of an Action, nested within a Run. This is used to identify a specific action on a task.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
name: str
|
|
37
|
+
run_name: str | None = None
|
|
38
|
+
project: str | None = None
|
|
39
|
+
domain: str | None = None
|
|
40
|
+
org: str | None = None
|
|
41
|
+
|
|
42
|
+
def __post_init__(self):
|
|
43
|
+
if self.run_name is None:
|
|
44
|
+
object.__setattr__(self, "run_name", self.name)
|
|
45
|
+
|
|
46
|
+
@classmethod
|
|
47
|
+
def create_random(cls):
|
|
48
|
+
name = generate_random_name()
|
|
49
|
+
return cls(name=name, run_name=name)
|
|
50
|
+
|
|
51
|
+
def new_sub_action(self, name: str | None = None) -> ActionID:
|
|
52
|
+
"""
|
|
53
|
+
Create a new sub-run with the given name. If name is None, a random name will be generated.
|
|
54
|
+
"""
|
|
55
|
+
if name is None:
|
|
56
|
+
name = generate_random_name()
|
|
57
|
+
return replace(self, name=name)
|
|
58
|
+
|
|
59
|
+
def new_sub_action_from(self, input_hash: str, group: str | None) -> ActionID:
|
|
60
|
+
"""Make a deterministic name"""
|
|
61
|
+
import hashlib
|
|
62
|
+
|
|
63
|
+
components = f"{self.run_name}-{self.name}-{input_hash}" + (f"-{group}" if group else "")
|
|
64
|
+
# has the components into something deterministic
|
|
65
|
+
bytes_digest = hashlib.md5(components.encode()).digest()
|
|
66
|
+
new_name = base62_encode(bytes_digest)
|
|
67
|
+
return self.new_sub_action(new_name)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@dataclass(frozen=True, kw_only=True)
|
|
71
|
+
class RawDataPath:
|
|
72
|
+
"""
|
|
73
|
+
A class representing the raw data path for a task. This is used to store the raw data for the task execution and
|
|
74
|
+
also get mutations on the path.
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
path: str
|
|
78
|
+
|
|
79
|
+
@classmethod
|
|
80
|
+
def from_local_folder(cls, local_folder: str | pathlib.Path | None = None) -> RawDataPath:
|
|
81
|
+
"""
|
|
82
|
+
Create a new context attribute object, with local path given. Will be created if it doesn't exist.
|
|
83
|
+
:return: Path to the temporary directory
|
|
84
|
+
"""
|
|
85
|
+
match local_folder:
|
|
86
|
+
case pathlib.Path():
|
|
87
|
+
local_folder.mkdir(parents=True, exist_ok=True)
|
|
88
|
+
return RawDataPath(path=str(local_folder))
|
|
89
|
+
case None:
|
|
90
|
+
# Create a temporary directory for data storage
|
|
91
|
+
p = tempfile.mkdtemp()
|
|
92
|
+
logger.debug(f"Creating temporary directory for data storage: {p}")
|
|
93
|
+
return RawDataPath(path=p)
|
|
94
|
+
case str():
|
|
95
|
+
return RawDataPath(path=local_folder)
|
|
96
|
+
case _:
|
|
97
|
+
raise ValueError(f"Invalid local path {local_folder}")
|
|
98
|
+
|
|
99
|
+
def get_random_remote_path(self, file_name: Optional[str] = None) -> str:
|
|
100
|
+
"""
|
|
101
|
+
Returns a random path for uploading a file/directory to.
|
|
102
|
+
|
|
103
|
+
:param file_name: If given, will be joined after a randomly generated portion.
|
|
104
|
+
:return:
|
|
105
|
+
"""
|
|
106
|
+
import random
|
|
107
|
+
from uuid import UUID
|
|
108
|
+
|
|
109
|
+
import fsspec
|
|
110
|
+
from fsspec.utils import get_protocol
|
|
111
|
+
|
|
112
|
+
random_string = UUID(int=random.getrandbits(128)).hex
|
|
113
|
+
file_prefix = self.path
|
|
114
|
+
|
|
115
|
+
protocol = get_protocol(file_prefix)
|
|
116
|
+
if "file" in protocol:
|
|
117
|
+
local_path = pathlib.Path(file_prefix) / random_string
|
|
118
|
+
if file_name:
|
|
119
|
+
# Only if file name is given do we create the parent, because it may be needed as a folder otherwise
|
|
120
|
+
local_path = local_path / file_name
|
|
121
|
+
if not local_path.exists():
|
|
122
|
+
local_path.parent.mkdir(exist_ok=True, parents=True)
|
|
123
|
+
local_path.touch()
|
|
124
|
+
return str(local_path.absolute())
|
|
125
|
+
|
|
126
|
+
fs = fsspec.filesystem(protocol)
|
|
127
|
+
if file_prefix.endswith(fs.sep):
|
|
128
|
+
file_prefix = file_prefix[:-1]
|
|
129
|
+
remote_path = fs.sep.join([file_prefix, random_string])
|
|
130
|
+
if file_name:
|
|
131
|
+
remote_path = fs.sep.join([remote_path, file_name])
|
|
132
|
+
return remote_path
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
@dataclass(frozen=True)
|
|
136
|
+
class GroupData:
|
|
137
|
+
name: str
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@dataclass(frozen=True, kw_only=True)
|
|
141
|
+
class TaskContext:
|
|
142
|
+
"""
|
|
143
|
+
A context class to hold the current task executions context.
|
|
144
|
+
This can be used to access various contextual parameters in the task execution by the user.
|
|
145
|
+
|
|
146
|
+
:param action: The action ID of the current execution. This is always set, within a run.
|
|
147
|
+
:param version: The version of the executed task. This is set when the task is executed by an action and will be
|
|
148
|
+
set on all sub-actions.
|
|
149
|
+
"""
|
|
150
|
+
|
|
151
|
+
action: ActionID
|
|
152
|
+
version: str
|
|
153
|
+
raw_data_path: RawDataPath
|
|
154
|
+
output_path: str
|
|
155
|
+
report: Report | None = None # This field is available if reports are enabled
|
|
156
|
+
group_data: GroupData | None = None
|
|
157
|
+
checkpoints: Checkpoints | None = None
|
|
158
|
+
code_bundle: CodeBundle | None = None
|
|
159
|
+
compiled_image_cache: ImageCache | None = None
|
|
160
|
+
data: Dict[str, Any] = field(default_factory=dict)
|
|
161
|
+
|
|
162
|
+
def replace(self, **kwargs) -> TaskContext:
|
|
163
|
+
if "data" in kwargs:
|
|
164
|
+
rec_data = kwargs.pop("data")
|
|
165
|
+
if rec_data is None:
|
|
166
|
+
return replace(self, **kwargs)
|
|
167
|
+
data = {}
|
|
168
|
+
if self.data is not None:
|
|
169
|
+
data = self.data.copy()
|
|
170
|
+
data.update(rec_data)
|
|
171
|
+
kwargs.update({"data": data})
|
|
172
|
+
return replace(self, **kwargs)
|
|
173
|
+
|
|
174
|
+
def __getitem__(self, key: str) -> Optional[Any]:
|
|
175
|
+
return self.data.get(key)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
@dataclass(frozen=True, kw_only=True)
|
|
179
|
+
class CodeBundle:
|
|
180
|
+
"""
|
|
181
|
+
A class representing a code bundle for a task. This is used to package the code and the inflation path.
|
|
182
|
+
The code bundle computes the version of the code using the hash of the code.
|
|
183
|
+
|
|
184
|
+
:param computed_version: The version of the code bundle. This is the hash of the code.
|
|
185
|
+
:param destination: The destination path for the code bundle to be inflated to.
|
|
186
|
+
:param tgz: Optional path to the tgz file.
|
|
187
|
+
:param pkl: Optional path to the pkl file.
|
|
188
|
+
:param downloaded_path: The path to the downloaded code bundle. This is only available during runtime, when
|
|
189
|
+
the code bundle has been downloaded and inflated.
|
|
190
|
+
"""
|
|
191
|
+
|
|
192
|
+
computed_version: str
|
|
193
|
+
destination: str = "."
|
|
194
|
+
tgz: str | None = None
|
|
195
|
+
pkl: str | None = None
|
|
196
|
+
downloaded_path: pathlib.Path | None = None
|
|
197
|
+
|
|
198
|
+
# runtime_dependencies: Tuple[str, ...] = field(default_factory=tuple) In the future if we want we could add this
|
|
199
|
+
# but this messes up actors, spark etc
|
|
200
|
+
|
|
201
|
+
def __post_init__(self):
|
|
202
|
+
if self.tgz is None and self.pkl is None:
|
|
203
|
+
raise ValueError("Either tgz or pkl must be provided")
|
|
204
|
+
|
|
205
|
+
def with_downloaded_path(self, path: pathlib.Path) -> CodeBundle:
|
|
206
|
+
"""
|
|
207
|
+
Create a new CodeBundle with the given downloaded path.
|
|
208
|
+
"""
|
|
209
|
+
return replace(self, downloaded_path=path)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
@dataclass(frozen=True)
|
|
213
|
+
class Checkpoints:
|
|
214
|
+
"""
|
|
215
|
+
A class representing the checkpoints for a task. This is used to store the checkpoints for the task execution.
|
|
216
|
+
"""
|
|
217
|
+
|
|
218
|
+
prev_checkpoint_path: str | None
|
|
219
|
+
checkpoint_path: str | None
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
@dataclass(frozen=True)
|
|
223
|
+
class NativeInterface:
|
|
224
|
+
"""
|
|
225
|
+
A class representing the native interface for a task. This is used to interact with the task and its execution
|
|
226
|
+
context.
|
|
227
|
+
"""
|
|
228
|
+
|
|
229
|
+
inputs: Dict[str, Tuple[Type, Any]]
|
|
230
|
+
outputs: Dict[str, Type]
|
|
231
|
+
docstring: Optional[Docstring] = field(default=(None,))
|
|
232
|
+
|
|
233
|
+
@classmethod
|
|
234
|
+
def from_callable(cls, func: Callable) -> NativeInterface:
|
|
235
|
+
"""
|
|
236
|
+
Extract the native interface from the given function. This is used to create a native interface for the task.
|
|
237
|
+
"""
|
|
238
|
+
sig = inspect.signature(func)
|
|
239
|
+
|
|
240
|
+
# Extract parameter details (name, type, default value)
|
|
241
|
+
param_info = {name: (param.annotation, param.default) for name, param in sig.parameters.items()}
|
|
242
|
+
|
|
243
|
+
# Get return type
|
|
244
|
+
outputs = extract_return_annotation(sig.return_annotation)
|
|
245
|
+
return cls(inputs=param_info, outputs=outputs)
|
|
246
|
+
|
|
247
|
+
def convert_to_kwargs(self, *args, **kwargs) -> Dict[str, Any]:
|
|
248
|
+
"""
|
|
249
|
+
Convert the given arguments to keyword arguments based on the native interface. This is used to convert the
|
|
250
|
+
arguments to the correct types for the task execution.
|
|
251
|
+
"""
|
|
252
|
+
# Convert positional arguments to keyword arguments
|
|
253
|
+
if len(args) > len(self.inputs):
|
|
254
|
+
raise ValueError(f"Too many positional arguments provided, inputs {self.inputs.keys()}, args {len(args)}")
|
|
255
|
+
for arg, input_name in zip(args, self.inputs.keys()):
|
|
256
|
+
kwargs[input_name] = arg
|
|
257
|
+
return kwargs
|
|
258
|
+
|
|
259
|
+
def get_input_types(self) -> Dict[str, Type]:
|
|
260
|
+
"""
|
|
261
|
+
Get the input types for the task. This is used to get the types of the inputs for the task execution.
|
|
262
|
+
"""
|
|
263
|
+
return {k: v[0] for k, v in self.inputs.items()}
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
@dataclass
|
|
267
|
+
class SerializationContext:
|
|
268
|
+
"""
|
|
269
|
+
This object holds serialization time contextual information, that can be used when serializing the task and
|
|
270
|
+
various parameters of a tasktemplate. This is only available when the task is being serialized and can be
|
|
271
|
+
during a deployment or runtime.
|
|
272
|
+
|
|
273
|
+
:param version: The version of the task
|
|
274
|
+
:param code_bundle: The code bundle for the task. This is used to package the code and the inflation path.
|
|
275
|
+
:param input_path: The path to the inputs for the task. This is used to determine where the inputs will be located
|
|
276
|
+
:param output_path: The path to the outputs for the task. This is used to determine where the outputs will be
|
|
277
|
+
located
|
|
278
|
+
"""
|
|
279
|
+
|
|
280
|
+
version: str
|
|
281
|
+
project: str | None = None
|
|
282
|
+
domain: str | None = None
|
|
283
|
+
org: str | None = None
|
|
284
|
+
code_bundle: Optional[CodeBundle] = None
|
|
285
|
+
input_path: str = "{{.input}}"
|
|
286
|
+
output_path: str = "{{.outputPrefix}}"
|
|
287
|
+
_entrypoint_path: str = field(default="_bin/runtime.py", init=False)
|
|
288
|
+
image_cache: ImageCache | None = None
|
|
289
|
+
|
|
290
|
+
def get_entrypoint_path(self, interpreter_path: str) -> str:
|
|
291
|
+
"""
|
|
292
|
+
Get the entrypoint path for the task. This is used to determine the entrypoint for the task execution.
|
|
293
|
+
:param interpreter_path: The path to the interpreter (python)
|
|
294
|
+
"""
|
|
295
|
+
return os.path.join(os.path.dirname(os.path.dirname(interpreter_path)), self._entrypoint_path)
|
union/_deploy.py
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import TYPE_CHECKING, Dict, List, Optional, Tuple
|
|
6
|
+
|
|
7
|
+
import rich.repr
|
|
8
|
+
|
|
9
|
+
from ._api_commons import syncer
|
|
10
|
+
from ._datastructures import SerializationContext
|
|
11
|
+
from ._environment import Environment
|
|
12
|
+
from ._image import Image
|
|
13
|
+
from ._initialize import get_client, get_common_config, requires_client, requires_initialization
|
|
14
|
+
from ._logging import logger
|
|
15
|
+
from ._task import TaskTemplate
|
|
16
|
+
from ._task_environment import TaskEnvironment
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from union._protos.workflow import task_definition_pb2
|
|
20
|
+
|
|
21
|
+
from ._code_bundle import CopyFiles
|
|
22
|
+
from ._internal.imagebuild.image_builder import ImageCache
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@rich.repr.auto
|
|
26
|
+
@dataclass
|
|
27
|
+
class DeploymentPlan:
|
|
28
|
+
envs: Dict[str, Environment]
|
|
29
|
+
version: Optional[str] = None
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@rich.repr.auto
|
|
33
|
+
@dataclass
|
|
34
|
+
class Deployment:
|
|
35
|
+
envs: Dict[str, Environment]
|
|
36
|
+
deployed_tasks: List[task_definition_pb2.TaskSpec] | None = None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@requires_client
|
|
40
|
+
async def _deploy_task(
|
|
41
|
+
task: TaskTemplate, serialization_context: SerializationContext, dryrun: bool = False
|
|
42
|
+
) -> task_definition_pb2.TaskSpec:
|
|
43
|
+
"""
|
|
44
|
+
Deploy the given task.
|
|
45
|
+
"""
|
|
46
|
+
from ._internal.runtime.task_serde import translate_task_to_wire
|
|
47
|
+
from ._protos.workflow import task_definition_pb2, task_service_pb2
|
|
48
|
+
|
|
49
|
+
image_uri = task.image.uri if isinstance(task.image, Image) else task.image
|
|
50
|
+
|
|
51
|
+
spec = translate_task_to_wire(task, serialization_context)
|
|
52
|
+
if dryrun:
|
|
53
|
+
return spec
|
|
54
|
+
logger.info(f"Deploying task {task.name}, with image {image_uri} and {serialization_context.version=} spec: {spec}")
|
|
55
|
+
cfg = get_common_config()
|
|
56
|
+
task_id = task_definition_pb2.TaskIdentifier(
|
|
57
|
+
org=cfg.org,
|
|
58
|
+
project=cfg.project,
|
|
59
|
+
domain=cfg.domain,
|
|
60
|
+
version=serialization_context.version,
|
|
61
|
+
name=spec.task_template.id.name,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
resp = await get_client().task_service.DeployTask(task_service_pb2.DeployTaskRequest(task_id=task_id, spec=spec))
|
|
65
|
+
logger.info(f"Deployed task {task.name}, {resp}")
|
|
66
|
+
return spec
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
async def _build_image_bg(env_name: str, image: Image) -> Tuple[str, str]:
|
|
70
|
+
"""
|
|
71
|
+
Build the image in the background and return the environment name and the built image.
|
|
72
|
+
"""
|
|
73
|
+
from ._build import build
|
|
74
|
+
|
|
75
|
+
logger.info(f"Building image {image.name} for environment {env_name}")
|
|
76
|
+
return env_name, await build.aio(image)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
async def build_images(deployment: DeploymentPlan) -> ImageCache:
|
|
80
|
+
"""
|
|
81
|
+
Build the images for the given deployment plan and update the environment with the built image.
|
|
82
|
+
"""
|
|
83
|
+
from ._internal.imagebuild.image_builder import ImageCache
|
|
84
|
+
|
|
85
|
+
images = []
|
|
86
|
+
image_identifier_map = {}
|
|
87
|
+
for env_name, env in deployment.envs.items():
|
|
88
|
+
if not isinstance(env.image, str):
|
|
89
|
+
logger.info(f"Building Image for environment {env_name}, image: {env.image}")
|
|
90
|
+
images.append(_build_image_bg(env_name, env.image))
|
|
91
|
+
|
|
92
|
+
elif env.image == "auto" and "auto" not in image_identifier_map:
|
|
93
|
+
auto_image = Image.auto()
|
|
94
|
+
image_identifier_map["auto"] = auto_image.uri
|
|
95
|
+
final_images = await asyncio.gather(*images)
|
|
96
|
+
|
|
97
|
+
for env_name, image_uri in final_images:
|
|
98
|
+
logger.info(f"Built Image for environment {env_name}, image: {image_uri}")
|
|
99
|
+
env = deployment.envs[env_name]
|
|
100
|
+
image_identifier_map[env.image.identifier] = env.image.uri
|
|
101
|
+
|
|
102
|
+
return ImageCache(image_lookup=image_identifier_map)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@requires_initialization
|
|
106
|
+
async def apply(deployment: DeploymentPlan, copy_style: CopyFiles, dryrun: bool = False) -> Deployment:
|
|
107
|
+
from ._code_bundle import build_code_bundle
|
|
108
|
+
|
|
109
|
+
cfg = get_common_config()
|
|
110
|
+
image_cache = await build_images(deployment)
|
|
111
|
+
if copy_style == "none":
|
|
112
|
+
code_bundle = None
|
|
113
|
+
assert deployment.version is not None, "Version must be set when copy_style is none"
|
|
114
|
+
else:
|
|
115
|
+
code_bundle = await build_code_bundle(from_dir=cfg.root_dir, dryrun=dryrun, copy_style=copy_style)
|
|
116
|
+
deployment.version = code_bundle.computed_version
|
|
117
|
+
|
|
118
|
+
sc = SerializationContext(
|
|
119
|
+
code_bundle=code_bundle,
|
|
120
|
+
version=deployment.version or code_bundle.computed_version,
|
|
121
|
+
image_cache=image_cache,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
tasks = []
|
|
125
|
+
for env_name, env in deployment.envs.items():
|
|
126
|
+
logger.info(f"Deploying environment {env_name}")
|
|
127
|
+
if isinstance(env, TaskEnvironment):
|
|
128
|
+
for task in env.tasks.values():
|
|
129
|
+
tasks.append(_deploy_task(task, dryrun=dryrun, serialization_context=sc))
|
|
130
|
+
return Deployment(envs=deployment.envs, deployed_tasks=await asyncio.gather(*tasks))
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _recursive_discover(
|
|
134
|
+
planned_envs: Dict[str, Environment], envs: Environment | List[Environment]
|
|
135
|
+
) -> Dict[str, Environment]:
|
|
136
|
+
"""
|
|
137
|
+
Recursively deploy the environment and its dependencies, if not already deployed (present in env_tasks) and
|
|
138
|
+
return the updated env_tasks.
|
|
139
|
+
"""
|
|
140
|
+
if isinstance(envs, Environment):
|
|
141
|
+
envs = [envs]
|
|
142
|
+
for env in envs:
|
|
143
|
+
# Skip if the environment is already planned
|
|
144
|
+
if env.name in planned_envs:
|
|
145
|
+
continue
|
|
146
|
+
# Recursively discover dependent environments
|
|
147
|
+
for dependent_env in env.env_dep_hints:
|
|
148
|
+
_recursive_discover(planned_envs, dependent_env)
|
|
149
|
+
# Add the environment to the existing envs
|
|
150
|
+
planned_envs[env.name] = env
|
|
151
|
+
return planned_envs
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def plan_deploy(*envs: Environment, version: Optional[str] = None) -> DeploymentPlan:
|
|
155
|
+
if envs is None:
|
|
156
|
+
return DeploymentPlan({})
|
|
157
|
+
planned_envs = _recursive_discover({}, *envs)
|
|
158
|
+
return DeploymentPlan(planned_envs, version=version)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
@syncer.wrap
|
|
162
|
+
async def deploy(
|
|
163
|
+
*envs: Environment,
|
|
164
|
+
dryrun: bool = False,
|
|
165
|
+
version: str | None = None,
|
|
166
|
+
interactive_mode: bool | None = None,
|
|
167
|
+
copy_style: CopyFiles = "loaded_modules",
|
|
168
|
+
) -> Deployment:
|
|
169
|
+
"""
|
|
170
|
+
Deploy the given environment or list of environments.
|
|
171
|
+
:param envs: Environment or list of environments to deploy.
|
|
172
|
+
:param dryrun: dryrun mode, if True, the deployment will not be applied to the control plane.
|
|
173
|
+
:param version: version of the deployment, if None, the version will be computed from the code bundle.
|
|
174
|
+
TODO: Support for interactive_mode
|
|
175
|
+
:param interactive_mode: Optional, can be forced to True or False.
|
|
176
|
+
If not provided, it will be set based on the current environment. For example Jupyter notebooks are considered
|
|
177
|
+
interactive mode, while scripts are not. This is used to determine how the code bundle is created.
|
|
178
|
+
:param copy_style: Copy style to use when running the task
|
|
179
|
+
|
|
180
|
+
:return: Deployment object containing the deployed environments and tasks.
|
|
181
|
+
"""
|
|
182
|
+
if interactive_mode:
|
|
183
|
+
raise NotImplementedError("Interactive mode not yet implemented for deployment")
|
|
184
|
+
deployment = plan_deploy(*envs, version=version)
|
|
185
|
+
return await apply(deployment, copy_style=copy_style, dryrun=dryrun)
|
union/_doc.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Callable
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass
|
|
7
|
+
class Documentation:
|
|
8
|
+
"""
|
|
9
|
+
This class is used to store the documentation of a task.
|
|
10
|
+
|
|
11
|
+
It can be set explicitly or extracted from the docstring of the task.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
description: str
|
|
15
|
+
|
|
16
|
+
def __help__str__(self):
|
|
17
|
+
return self.description
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def extract_docstring(func: Callable) -> Documentation:
|
|
21
|
+
"""
|
|
22
|
+
Extracts the description from a docstring.
|
|
23
|
+
"""
|
|
24
|
+
if not func:
|
|
25
|
+
return Documentation(description="")
|
|
26
|
+
docstring = inspect.getdoc(func)
|
|
27
|
+
if not docstring:
|
|
28
|
+
return Documentation(description="")
|
|
29
|
+
return Documentation(description=docstring)
|
union/_docstring.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from typing import Callable, Dict, Optional
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Docstring(object):
|
|
5
|
+
def __init__(self, docstring: Optional[str] = None, callable_: Optional[Callable] = None):
|
|
6
|
+
if docstring is not None:
|
|
7
|
+
# from docstring_parser import parse
|
|
8
|
+
self._parsed_docstring = "" # parse(docstring)
|
|
9
|
+
else:
|
|
10
|
+
self._parsed_docstring = "" # parse(callable_.__doc__)
|
|
11
|
+
|
|
12
|
+
@property
|
|
13
|
+
def input_descriptions(self) -> Dict[str, str]:
|
|
14
|
+
return {p.arg_name: p.description for p in self._parsed_docstring.params}
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def output_descriptions(self) -> Dict[str, str]:
|
|
18
|
+
return {p.return_name: p.description for p in self._parsed_docstring.many_returns}
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def short_description(self) -> Optional[str]:
|
|
22
|
+
return self._parsed_docstring.short_description
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def long_description(self) -> Optional[str]:
|
|
26
|
+
return self._parsed_docstring.long_description
|
union/_environment.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from typing import TYPE_CHECKING, Dict, List, Literal, Optional, Union
|
|
5
|
+
|
|
6
|
+
import rich.repr
|
|
7
|
+
|
|
8
|
+
from union._secret import SecretRequest
|
|
9
|
+
|
|
10
|
+
from ._image import Image
|
|
11
|
+
from ._resources import Resources
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from kubernetes.client import V1PodTemplate
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@rich.repr.auto
|
|
18
|
+
@dataclass(init=True, repr=True)
|
|
19
|
+
class Environment:
|
|
20
|
+
"""
|
|
21
|
+
:param name: Name of the environment
|
|
22
|
+
:param image: Docker image to use for the environment. If set to "auto", will use the default image.
|
|
23
|
+
:param resources: Resources to allocate for the environment.
|
|
24
|
+
:param env: Environment variables to set for the environment.
|
|
25
|
+
:param secrets: Secrets to inject into the environment.
|
|
26
|
+
:param env_dep_hints: Environment dependencies to hint, so when you deploy the environment, the dependencies are
|
|
27
|
+
also deployed. This is useful when you have a set of environments that depend on each other.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
name: str
|
|
31
|
+
env_dep_hints: List[Environment] = field(default_factory=list)
|
|
32
|
+
pod_template: Optional[Union[str, "V1PodTemplate"]] = None
|
|
33
|
+
description: Optional[str] = None
|
|
34
|
+
secrets: Optional[SecretRequest] = None
|
|
35
|
+
env: Optional[Dict[str, str]] = None
|
|
36
|
+
resources: Optional[Resources] = None
|
|
37
|
+
image: Union[str, Image, Literal["auto"]] = "auto"
|
|
38
|
+
|
|
39
|
+
def add_dependency(self, *env: Environment):
|
|
40
|
+
"""
|
|
41
|
+
Add a dependency to the environment.
|
|
42
|
+
"""
|
|
43
|
+
self.env_dep_hints.extend(env)
|
union/_group.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from contextlib import contextmanager
|
|
2
|
+
|
|
3
|
+
from ._context import internal_ctx
|
|
4
|
+
from ._datastructures import GroupData
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@contextmanager
|
|
8
|
+
def group(name: str):
|
|
9
|
+
"""
|
|
10
|
+
Create a new group with the given name. The method is intended to be used as a context manager.
|
|
11
|
+
|
|
12
|
+
Example:
|
|
13
|
+
```python
|
|
14
|
+
@task
|
|
15
|
+
async def my_task():
|
|
16
|
+
...
|
|
17
|
+
with group("my_group"):
|
|
18
|
+
t1(x,y) # tasks in this block will be grouped under "my_group"
|
|
19
|
+
...
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
:param name: The name of the group
|
|
23
|
+
"""
|
|
24
|
+
ctx = internal_ctx()
|
|
25
|
+
if ctx.data.task_context is None:
|
|
26
|
+
yield
|
|
27
|
+
return
|
|
28
|
+
tctx = ctx.data.task_context
|
|
29
|
+
new_tctx = tctx.replace(group_data=GroupData(name))
|
|
30
|
+
with ctx.replace_task_context(new_tctx):
|
|
31
|
+
yield
|
union/_hash.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from typing import Callable, Generic, TypeVar
|
|
2
|
+
|
|
3
|
+
T = TypeVar("T")
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class HashOnReferenceMixin(object):
|
|
7
|
+
def __hash__(self):
|
|
8
|
+
return hash(id(self))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class HashMethod(Generic[T]):
|
|
12
|
+
"""
|
|
13
|
+
Flyte-specific object used to wrap the hash function for a specific type
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, function: Callable[[T], str]):
|
|
17
|
+
self._function = function
|
|
18
|
+
|
|
19
|
+
def calculate(self, obj: T) -> str:
|
|
20
|
+
"""
|
|
21
|
+
Calculate hash for `obj`.
|
|
22
|
+
"""
|
|
23
|
+
return self._function(obj)
|