flyte 0.0.1b3__py3-none-any.whl → 0.2.0b0__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/_cli/_common.py +12 -0
- {union → flyte}/_cli/_params.py +106 -147
- flyte/_cli/_run.py +24 -2
- flyte/_cli/main.py +28 -2
- flyte/_image.py +1 -2
- flyte/_initialize.py +24 -15
- flyte/_internal/runtime/convert.py +6 -0
- flyte/_run.py +0 -1
- flyte/_version.py +2 -2
- flyte/config/__init__.py +168 -0
- flyte/config/_config.py +196 -0
- flyte/config/_internal.py +64 -0
- flyte/remote/_console.py +1 -1
- flyte/types/_type_engine.py +4 -3
- {flyte-0.0.1b3.dist-info → flyte-0.2.0b0.dist-info}/METADATA +1 -1
- flyte-0.2.0b0.dist-info/RECORD +204 -0
- flyte-0.0.1b3.dist-info/RECORD +0 -390
- union/__init__.py +0 -54
- union/_api_commons.py +0 -3
- union/_bin/__init__.py +0 -0
- union/_bin/runtime.py +0 -113
- union/_build.py +0 -25
- union/_cache/__init__.py +0 -12
- union/_cache/cache.py +0 -141
- union/_cache/defaults.py +0 -9
- union/_cache/policy_function_body.py +0 -42
- union/_cli/__init__.py +0 -0
- union/_cli/_common.py +0 -263
- union/_cli/_create.py +0 -40
- union/_cli/_delete.py +0 -23
- union/_cli/_deploy.py +0 -120
- union/_cli/_get.py +0 -162
- union/_cli/_run.py +0 -150
- union/_cli/main.py +0 -72
- union/_code_bundle/__init__.py +0 -8
- union/_code_bundle/_ignore.py +0 -113
- union/_code_bundle/_packaging.py +0 -187
- union/_code_bundle/_utils.py +0 -342
- union/_code_bundle/bundle.py +0 -176
- union/_context.py +0 -146
- union/_datastructures.py +0 -295
- union/_deploy.py +0 -185
- union/_doc.py +0 -29
- union/_docstring.py +0 -26
- union/_environment.py +0 -43
- union/_group.py +0 -31
- union/_hash.py +0 -23
- union/_image.py +0 -760
- union/_initialize.py +0 -585
- union/_interface.py +0 -84
- union/_internal/__init__.py +0 -3
- union/_internal/controllers/__init__.py +0 -77
- union/_internal/controllers/_local_controller.py +0 -77
- union/_internal/controllers/pbhash.py +0 -39
- union/_internal/controllers/remote/__init__.py +0 -40
- union/_internal/controllers/remote/_action.py +0 -131
- union/_internal/controllers/remote/_client.py +0 -43
- union/_internal/controllers/remote/_controller.py +0 -169
- union/_internal/controllers/remote/_core.py +0 -341
- union/_internal/controllers/remote/_informer.py +0 -260
- union/_internal/controllers/remote/_service_protocol.py +0 -44
- union/_internal/imagebuild/__init__.py +0 -11
- union/_internal/imagebuild/docker_builder.py +0 -416
- union/_internal/imagebuild/image_builder.py +0 -243
- union/_internal/imagebuild/remote_builder.py +0 -0
- union/_internal/resolvers/__init__.py +0 -0
- union/_internal/resolvers/_task_module.py +0 -31
- union/_internal/resolvers/common.py +0 -24
- union/_internal/resolvers/default.py +0 -27
- union/_internal/runtime/__init__.py +0 -0
- union/_internal/runtime/convert.py +0 -163
- union/_internal/runtime/entrypoints.py +0 -121
- union/_internal/runtime/io.py +0 -136
- union/_internal/runtime/resources_serde.py +0 -134
- union/_internal/runtime/task_serde.py +0 -202
- union/_internal/runtime/taskrunner.py +0 -179
- union/_internal/runtime/types_serde.py +0 -53
- union/_logging.py +0 -124
- union/_protos/__init__.py +0 -0
- union/_protos/common/authorization_pb2.py +0 -66
- union/_protos/common/authorization_pb2.pyi +0 -106
- union/_protos/common/authorization_pb2_grpc.py +0 -4
- union/_protos/common/identifier_pb2.py +0 -71
- union/_protos/common/identifier_pb2.pyi +0 -82
- union/_protos/common/identifier_pb2_grpc.py +0 -4
- union/_protos/common/identity_pb2.py +0 -48
- union/_protos/common/identity_pb2.pyi +0 -72
- union/_protos/common/identity_pb2_grpc.py +0 -4
- union/_protos/common/list_pb2.py +0 -36
- union/_protos/common/list_pb2.pyi +0 -69
- union/_protos/common/list_pb2_grpc.py +0 -4
- union/_protos/common/policy_pb2.py +0 -37
- union/_protos/common/policy_pb2.pyi +0 -27
- union/_protos/common/policy_pb2_grpc.py +0 -4
- union/_protos/common/role_pb2.py +0 -37
- union/_protos/common/role_pb2.pyi +0 -51
- union/_protos/common/role_pb2_grpc.py +0 -4
- union/_protos/common/runtime_version_pb2.py +0 -28
- union/_protos/common/runtime_version_pb2.pyi +0 -24
- union/_protos/common/runtime_version_pb2_grpc.py +0 -4
- union/_protos/logs/dataplane/payload_pb2.py +0 -96
- union/_protos/logs/dataplane/payload_pb2.pyi +0 -168
- union/_protos/logs/dataplane/payload_pb2_grpc.py +0 -4
- union/_protos/secret/definition_pb2.py +0 -49
- union/_protos/secret/definition_pb2.pyi +0 -93
- union/_protos/secret/definition_pb2_grpc.py +0 -4
- union/_protos/secret/payload_pb2.py +0 -62
- union/_protos/secret/payload_pb2.pyi +0 -94
- union/_protos/secret/payload_pb2_grpc.py +0 -4
- union/_protos/secret/secret_pb2.py +0 -38
- union/_protos/secret/secret_pb2.pyi +0 -6
- union/_protos/secret/secret_pb2_grpc.py +0 -198
- union/_protos/validate/validate/validate_pb2.py +0 -76
- union/_protos/workflow/node_execution_service_pb2.py +0 -26
- union/_protos/workflow/node_execution_service_pb2.pyi +0 -4
- union/_protos/workflow/node_execution_service_pb2_grpc.py +0 -32
- union/_protos/workflow/queue_service_pb2.py +0 -75
- union/_protos/workflow/queue_service_pb2.pyi +0 -103
- union/_protos/workflow/queue_service_pb2_grpc.py +0 -172
- union/_protos/workflow/run_definition_pb2.py +0 -100
- union/_protos/workflow/run_definition_pb2.pyi +0 -256
- union/_protos/workflow/run_definition_pb2_grpc.py +0 -4
- union/_protos/workflow/run_logs_service_pb2.py +0 -41
- union/_protos/workflow/run_logs_service_pb2.pyi +0 -28
- union/_protos/workflow/run_logs_service_pb2_grpc.py +0 -69
- union/_protos/workflow/run_service_pb2.py +0 -133
- union/_protos/workflow/run_service_pb2.pyi +0 -173
- union/_protos/workflow/run_service_pb2_grpc.py +0 -412
- union/_protos/workflow/state_service_pb2.py +0 -58
- union/_protos/workflow/state_service_pb2.pyi +0 -69
- union/_protos/workflow/state_service_pb2_grpc.py +0 -138
- union/_protos/workflow/task_definition_pb2.py +0 -72
- union/_protos/workflow/task_definition_pb2.pyi +0 -65
- union/_protos/workflow/task_definition_pb2_grpc.py +0 -4
- union/_protos/workflow/task_service_pb2.py +0 -44
- union/_protos/workflow/task_service_pb2.pyi +0 -31
- union/_protos/workflow/task_service_pb2_grpc.py +0 -104
- union/_resources.py +0 -226
- union/_retry.py +0 -32
- union/_reusable_environment.py +0 -25
- union/_run.py +0 -374
- union/_secret.py +0 -61
- union/_task.py +0 -354
- union/_task_environment.py +0 -186
- union/_timeout.py +0 -47
- union/_tools.py +0 -27
- union/_utils/__init__.py +0 -11
- union/_utils/asyn.py +0 -119
- union/_utils/file_handling.py +0 -71
- union/_utils/helpers.py +0 -46
- union/_utils/lazy_module.py +0 -54
- union/_utils/uv_script_parser.py +0 -49
- union/_version.py +0 -21
- union/connectors/__init__.py +0 -0
- union/errors.py +0 -128
- union/extras/__init__.py +0 -5
- union/extras/_container.py +0 -263
- union/io/__init__.py +0 -11
- union/io/_dataframe.py +0 -0
- union/io/_dir.py +0 -425
- union/io/_file.py +0 -418
- union/io/pickle/__init__.py +0 -0
- union/io/pickle/transformer.py +0 -117
- union/io/structured_dataset/__init__.py +0 -122
- union/io/structured_dataset/basic_dfs.py +0 -219
- union/io/structured_dataset/structured_dataset.py +0 -1057
- union/py.typed +0 -0
- union/remote/__init__.py +0 -23
- union/remote/_client/__init__.py +0 -0
- union/remote/_client/_protocols.py +0 -129
- union/remote/_client/auth/__init__.py +0 -12
- union/remote/_client/auth/_authenticators/__init__.py +0 -0
- union/remote/_client/auth/_authenticators/base.py +0 -391
- union/remote/_client/auth/_authenticators/client_credentials.py +0 -73
- union/remote/_client/auth/_authenticators/device_code.py +0 -120
- union/remote/_client/auth/_authenticators/external_command.py +0 -77
- union/remote/_client/auth/_authenticators/factory.py +0 -200
- union/remote/_client/auth/_authenticators/pkce.py +0 -515
- union/remote/_client/auth/_channel.py +0 -184
- union/remote/_client/auth/_client_config.py +0 -83
- union/remote/_client/auth/_default_html.py +0 -32
- union/remote/_client/auth/_grpc_utils/__init__.py +0 -0
- union/remote/_client/auth/_grpc_utils/auth_interceptor.py +0 -204
- union/remote/_client/auth/_grpc_utils/default_metadata_interceptor.py +0 -144
- union/remote/_client/auth/_keyring.py +0 -154
- union/remote/_client/auth/_token_client.py +0 -258
- union/remote/_client/auth/errors.py +0 -16
- union/remote/_client/controlplane.py +0 -86
- union/remote/_data.py +0 -149
- union/remote/_logs.py +0 -74
- union/remote/_project.py +0 -86
- union/remote/_run.py +0 -820
- union/remote/_secret.py +0 -132
- union/remote/_task.py +0 -193
- union/report/__init__.py +0 -3
- union/report/_report.py +0 -178
- union/report/_template.html +0 -124
- union/storage/__init__.py +0 -24
- union/storage/_remote_fs.py +0 -34
- union/storage/_storage.py +0 -247
- union/storage/_utils.py +0 -5
- union/types/__init__.py +0 -11
- union/types/_renderer.py +0 -162
- union/types/_string_literals.py +0 -120
- union/types/_type_engine.py +0 -2131
- union/types/_utils.py +0 -80
- {flyte-0.0.1b3.dist-info → flyte-0.2.0b0.dist-info}/WHEEL +0 -0
- {flyte-0.0.1b3.dist-info → flyte-0.2.0b0.dist-info}/entry_points.txt +0 -0
- {flyte-0.0.1b3.dist-info → flyte-0.2.0b0.dist-info}/top_level.txt +0 -0
flyte/_cli/_common.py
CHANGED
|
@@ -12,10 +12,13 @@ from typing import Any, Dict, Iterable, List, Optional
|
|
|
12
12
|
|
|
13
13
|
import rich.box
|
|
14
14
|
import rich_click as click
|
|
15
|
+
from rich.console import Console
|
|
15
16
|
from rich.panel import Panel
|
|
16
17
|
from rich.table import Table
|
|
18
|
+
from rich.traceback import Traceback
|
|
17
19
|
|
|
18
20
|
import flyte.errors
|
|
21
|
+
from flyte.config import Config
|
|
19
22
|
|
|
20
23
|
PREFERRED_BORDER_COLOR = "dim cyan"
|
|
21
24
|
PREFERRED_ACCENT_COLOR = "bold #FFD700"
|
|
@@ -70,6 +73,7 @@ class CLIConfig:
|
|
|
70
73
|
endpoint: str | None = None
|
|
71
74
|
insecure: bool = False
|
|
72
75
|
org_override: str | None = None
|
|
76
|
+
config: Config | None = None
|
|
73
77
|
|
|
74
78
|
def replace(self, **kwargs) -> CLIConfig:
|
|
75
79
|
"""
|
|
@@ -87,6 +91,7 @@ class CLIConfig:
|
|
|
87
91
|
project=project,
|
|
88
92
|
domain=domain,
|
|
89
93
|
log_level=self.log_level,
|
|
94
|
+
config=self.config,
|
|
90
95
|
)
|
|
91
96
|
|
|
92
97
|
|
|
@@ -111,7 +116,14 @@ class InvokeBaseMixin:
|
|
|
111
116
|
raise click.ClickException(f"RPC error invoking command: {e!s}") from e
|
|
112
117
|
except flyte.errors.InitializationError:
|
|
113
118
|
raise click.ClickException("Initialize the CLI with a remote configuration. For example, pass --endpoint")
|
|
119
|
+
except click.exceptions.Exit as e:
|
|
120
|
+
# This is a normal exit, do nothing
|
|
121
|
+
raise e
|
|
114
122
|
except Exception as e:
|
|
123
|
+
if ctx.obj and ctx.obj.log_level and ctx.obj.log_level <= logging.DEBUG:
|
|
124
|
+
# If the user has requested verbose output, print the full traceback
|
|
125
|
+
console = Console()
|
|
126
|
+
console.print(Traceback.from_exception(type(e), e, e.__traceback__))
|
|
115
127
|
raise click.ClickException(f"Error invoking command: {e}") from e
|
|
116
128
|
|
|
117
129
|
|
{union → flyte}/_cli/_params.py
RENAMED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import asyncio
|
|
2
1
|
import dataclasses
|
|
3
2
|
import datetime
|
|
4
3
|
import enum
|
|
@@ -7,6 +6,7 @@ import importlib.util
|
|
|
7
6
|
import json
|
|
8
7
|
import os
|
|
9
8
|
import pathlib
|
|
9
|
+
import re
|
|
10
10
|
import sys
|
|
11
11
|
import typing
|
|
12
12
|
import typing as t
|
|
@@ -14,24 +14,16 @@ from typing import get_args
|
|
|
14
14
|
|
|
15
15
|
import rich_click as click
|
|
16
16
|
import yaml
|
|
17
|
+
from click import Parameter
|
|
18
|
+
from flyteidl.core.interface_pb2 import Variable
|
|
17
19
|
from flyteidl.core.literals_pb2 import Literal
|
|
18
20
|
from flyteidl.core.types_pb2 import BlobType, LiteralType, SimpleType
|
|
21
|
+
from google.protobuf.json_format import MessageToDict
|
|
22
|
+
from mashumaro.codecs.json import JSONEncoder
|
|
19
23
|
|
|
20
|
-
from
|
|
21
|
-
from
|
|
22
|
-
from
|
|
23
|
-
from union.storage._remote_fs import RemoteFSPathResolver
|
|
24
|
-
from union.types import TypeEngine
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
# ---------------------------------------------------
|
|
28
|
-
# TODO replace these
|
|
29
|
-
class ArtifactQuery:
|
|
30
|
-
pass
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def is_remote(v: str) -> bool:
|
|
34
|
-
return False
|
|
24
|
+
from flyte._logging import logger
|
|
25
|
+
from flyte.io import Dir, File
|
|
26
|
+
from flyte.io.pickle.transformer import FlytePickleTransformer
|
|
35
27
|
|
|
36
28
|
|
|
37
29
|
class StructuredDataset:
|
|
@@ -43,26 +35,6 @@ class StructuredDataset:
|
|
|
43
35
|
# ---------------------------------------------------
|
|
44
36
|
|
|
45
37
|
|
|
46
|
-
def is_pydantic_basemodel(python_type: typing.Type) -> bool:
|
|
47
|
-
"""
|
|
48
|
-
Checks if the python type is a pydantic BaseModel
|
|
49
|
-
"""
|
|
50
|
-
try:
|
|
51
|
-
import pydantic # noqa: F401
|
|
52
|
-
except ImportError:
|
|
53
|
-
return False
|
|
54
|
-
else:
|
|
55
|
-
try:
|
|
56
|
-
from pydantic import BaseModel as BaseModelV2
|
|
57
|
-
from pydantic.v1 import BaseModel as BaseModelV1
|
|
58
|
-
|
|
59
|
-
return issubclass(python_type, BaseModelV1) or issubclass(python_type, BaseModelV2)
|
|
60
|
-
except ImportError:
|
|
61
|
-
from pydantic import BaseModel
|
|
62
|
-
|
|
63
|
-
return issubclass(python_type, BaseModel)
|
|
64
|
-
|
|
65
|
-
|
|
66
38
|
def key_value_callback(_: typing.Any, param: str, values: typing.List[str]) -> typing.Optional[typing.Dict[str, str]]:
|
|
67
39
|
"""
|
|
68
40
|
Callback for click to parse key-value pairs.
|
|
@@ -100,17 +72,13 @@ class DirParamType(click.ParamType):
|
|
|
100
72
|
def convert(
|
|
101
73
|
self, value: typing.Any, param: typing.Optional[click.Parameter], ctx: typing.Optional[click.Context]
|
|
102
74
|
) -> typing.Any:
|
|
103
|
-
|
|
104
|
-
return value
|
|
75
|
+
from flyte.storage import is_remote
|
|
105
76
|
|
|
106
|
-
# set remote_directory to false if running pyflyte run locally. This makes sure that the original
|
|
107
|
-
# directory is used and not a random one.
|
|
108
|
-
remote_directory = None if getattr(ctx.obj, "is_remote", False) else False
|
|
109
77
|
if not is_remote(value):
|
|
110
78
|
p = pathlib.Path(value)
|
|
111
79
|
if not p.exists() or not p.is_dir():
|
|
112
80
|
raise click.BadParameter(f"parameter should be a valid flytedirectory path, {value}")
|
|
113
|
-
return Dir(path=value
|
|
81
|
+
return Dir(path=value)
|
|
114
82
|
|
|
115
83
|
|
|
116
84
|
class StructuredDatasetParamType(click.ParamType):
|
|
@@ -123,8 +91,6 @@ class StructuredDatasetParamType(click.ParamType):
|
|
|
123
91
|
def convert(
|
|
124
92
|
self, value: typing.Any, param: typing.Optional[click.Parameter], ctx: typing.Optional[click.Context]
|
|
125
93
|
) -> typing.Any:
|
|
126
|
-
if isinstance(value, ArtifactQuery):
|
|
127
|
-
return value
|
|
128
94
|
if isinstance(value, str):
|
|
129
95
|
return StructuredDataset(uri=value)
|
|
130
96
|
elif isinstance(value, StructuredDataset):
|
|
@@ -138,22 +104,19 @@ class FileParamType(click.ParamType):
|
|
|
138
104
|
def convert(
|
|
139
105
|
self, value: typing.Any, param: typing.Optional[click.Parameter], ctx: typing.Optional[click.Context]
|
|
140
106
|
) -> typing.Any:
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
# set remote_directory to false if running pyflyte run locally. This makes sure that the original
|
|
144
|
-
# file is used and not a random one.
|
|
145
|
-
remote_path = None if getattr(ctx.obj, "is_remote", False) else False
|
|
107
|
+
from flyte.storage import is_remote
|
|
108
|
+
|
|
146
109
|
if not is_remote(value):
|
|
147
110
|
p = pathlib.Path(value)
|
|
148
111
|
if not p.exists() or not p.is_file():
|
|
149
112
|
raise click.BadParameter(f"parameter should be a valid file path, {value}")
|
|
150
|
-
return File(path=value
|
|
113
|
+
return File(path=value)
|
|
151
114
|
|
|
152
115
|
|
|
153
116
|
class PickleParamType(click.ParamType):
|
|
154
117
|
name = "pickle"
|
|
155
118
|
|
|
156
|
-
def get_metavar(self, param:
|
|
119
|
+
def get_metavar(self, param: "Parameter", *args) -> t.Optional[str]:
|
|
157
120
|
return "Python Object <Module>:<Object>"
|
|
158
121
|
|
|
159
122
|
def convert(
|
|
@@ -163,7 +126,7 @@ class PickleParamType(click.ParamType):
|
|
|
163
126
|
return value
|
|
164
127
|
parts = value.split(":")
|
|
165
128
|
if len(parts) != 2:
|
|
166
|
-
if ctx and ctx.obj and ctx.obj.
|
|
129
|
+
if ctx and ctx.obj and ctx.obj.log_level >= 10: # DEBUG level
|
|
167
130
|
click.echo(f"Did not receive a string in the expected format <MODULE>:<VAR>, falling back to: {value}")
|
|
168
131
|
return value
|
|
169
132
|
try:
|
|
@@ -185,9 +148,6 @@ class JSONIteratorParamType(click.ParamType):
|
|
|
185
148
|
return value
|
|
186
149
|
|
|
187
150
|
|
|
188
|
-
import re
|
|
189
|
-
|
|
190
|
-
|
|
191
151
|
def parse_iso8601_duration(iso_duration: str) -> datetime.timedelta:
|
|
192
152
|
pattern = re.compile(
|
|
193
153
|
r"^P" # Starts with 'P'
|
|
@@ -211,10 +171,10 @@ def parse_human_durations(text: str) -> list[datetime.timedelta]:
|
|
|
211
171
|
durations = []
|
|
212
172
|
|
|
213
173
|
for part in raw_parts:
|
|
214
|
-
|
|
174
|
+
new_part = part.strip().lower()
|
|
215
175
|
|
|
216
176
|
# Match 1:24 or :45
|
|
217
|
-
m_colon = re.match(r"^(?:(\d+):)?(\d+)$",
|
|
177
|
+
m_colon = re.match(r"^(?:(\d+):)?(\d+)$", new_part)
|
|
218
178
|
if m_colon:
|
|
219
179
|
minutes = int(m_colon.group(1)) if m_colon.group(1) else 0
|
|
220
180
|
seconds = int(m_colon.group(2))
|
|
@@ -222,7 +182,7 @@ def parse_human_durations(text: str) -> list[datetime.timedelta]:
|
|
|
222
182
|
continue
|
|
223
183
|
|
|
224
184
|
# Match "10 days", "1 minute", etc.
|
|
225
|
-
m_units = re.match(r"^(\d+)\s*(day|hour|minute|second)s?$",
|
|
185
|
+
m_units = re.match(r"^(\d+)\s*(day|hour|minute|second)s?$", new_part)
|
|
226
186
|
if m_units:
|
|
227
187
|
value = int(m_units.group(1))
|
|
228
188
|
unit = m_units.group(2)
|
|
@@ -270,9 +230,6 @@ class DateTimeType(click.DateTime):
|
|
|
270
230
|
def convert(
|
|
271
231
|
self, value: typing.Any, param: typing.Optional[click.Parameter], ctx: typing.Optional[click.Context]
|
|
272
232
|
) -> typing.Any:
|
|
273
|
-
if isinstance(value, ArtifactQuery):
|
|
274
|
-
return value
|
|
275
|
-
|
|
276
233
|
if isinstance(value, str) and " " in value:
|
|
277
234
|
import re
|
|
278
235
|
|
|
@@ -303,8 +260,6 @@ class DurationParamType(click.ParamType):
|
|
|
303
260
|
def convert(
|
|
304
261
|
self, value: typing.Any, param: typing.Optional[click.Parameter], ctx: typing.Optional[click.Context]
|
|
305
262
|
) -> typing.Any:
|
|
306
|
-
if isinstance(value, ArtifactQuery):
|
|
307
|
-
return value
|
|
308
263
|
if value is None:
|
|
309
264
|
raise click.BadParameter("None value cannot be converted to a Duration type.")
|
|
310
265
|
return parse_duration(value)
|
|
@@ -318,8 +273,6 @@ class EnumParamType(click.Choice):
|
|
|
318
273
|
def convert(
|
|
319
274
|
self, value: typing.Any, param: typing.Optional[click.Parameter], ctx: typing.Optional[click.Context]
|
|
320
275
|
) -> enum.Enum:
|
|
321
|
-
if isinstance(value, ArtifactQuery):
|
|
322
|
-
return value
|
|
323
276
|
if isinstance(value, self._enum_type):
|
|
324
277
|
return value
|
|
325
278
|
return self._enum_type(super().convert(value, param, ctx))
|
|
@@ -333,10 +286,7 @@ class UnionParamType(click.ParamType):
|
|
|
333
286
|
def __init__(self, types: typing.List[click.ParamType]):
|
|
334
287
|
super().__init__()
|
|
335
288
|
self._types = self._sort_precedence(types)
|
|
336
|
-
|
|
337
|
-
@property
|
|
338
|
-
def name(self) -> str:
|
|
339
|
-
return "|".join([t.name for t in self._types])
|
|
289
|
+
self.name = "|".join([t.name for t in self._types])
|
|
340
290
|
|
|
341
291
|
@staticmethod
|
|
342
292
|
def _sort_precedence(tp: typing.List[click.ParamType]) -> typing.List[click.ParamType]:
|
|
@@ -350,7 +300,7 @@ class UnionParamType(click.ParamType):
|
|
|
350
300
|
str_types.append(p)
|
|
351
301
|
else:
|
|
352
302
|
others.append(p)
|
|
353
|
-
return others + str_types + unprocessed
|
|
303
|
+
return others + str_types + unprocessed # type: ignore
|
|
354
304
|
|
|
355
305
|
def convert(
|
|
356
306
|
self, value: typing.Any, param: typing.Optional[click.Parameter], ctx: typing.Optional[click.Context]
|
|
@@ -359,8 +309,6 @@ class UnionParamType(click.ParamType):
|
|
|
359
309
|
Important to implement NoneType / Optional.
|
|
360
310
|
Also could we just determine the click types from the python types
|
|
361
311
|
"""
|
|
362
|
-
if isinstance(value, ArtifactQuery):
|
|
363
|
-
return value
|
|
364
312
|
for p in self._types:
|
|
365
313
|
try:
|
|
366
314
|
return p.convert(value, param, ctx)
|
|
@@ -386,7 +334,7 @@ class JsonParamType(click.ParamType):
|
|
|
386
334
|
# We failed to load the json, so we'll try to load it as a file
|
|
387
335
|
if os.path.exists(value):
|
|
388
336
|
# if the value is a yaml file, we'll try to load it as yaml
|
|
389
|
-
if value.endswith(".yaml"
|
|
337
|
+
if value.endswith((".yaml", "yml")):
|
|
390
338
|
with open(value, "r") as f:
|
|
391
339
|
return yaml.safe_load(f)
|
|
392
340
|
with open(value, "r") as f:
|
|
@@ -398,8 +346,6 @@ class JsonParamType(click.ParamType):
|
|
|
398
346
|
def convert(
|
|
399
347
|
self, value: typing.Any, param: typing.Optional[click.Parameter], ctx: typing.Optional[click.Context]
|
|
400
348
|
) -> typing.Any:
|
|
401
|
-
if isinstance(value, ArtifactQuery):
|
|
402
|
-
return value
|
|
403
349
|
if value is None:
|
|
404
350
|
raise click.BadParameter("None value cannot be converted to a Json type.")
|
|
405
351
|
|
|
@@ -407,68 +353,38 @@ class JsonParamType(click.ParamType):
|
|
|
407
353
|
|
|
408
354
|
# We compare the origin type because the json parsed value for list or dict is always a list or dict without
|
|
409
355
|
# the covariant type information.
|
|
410
|
-
if type(parsed_value)
|
|
356
|
+
if type(parsed_value) is typing.get_origin(self._python_type) or type(parsed_value) is self._python_type:
|
|
411
357
|
# Indexing the return value of get_args will raise an error for native dict and list types.
|
|
412
358
|
# We don't support native list/dict types with nested dataclasses.
|
|
413
359
|
if get_args(self._python_type) == ():
|
|
414
360
|
return parsed_value
|
|
415
361
|
elif isinstance(parsed_value, list) and dataclasses.is_dataclass(get_args(self._python_type)[0]):
|
|
416
362
|
j = JsonParamType(get_args(self._python_type)[0])
|
|
417
|
-
|
|
363
|
+
# turn object back into json string
|
|
364
|
+
return [j.convert(json.dumps(v), param, ctx) for v in parsed_value]
|
|
418
365
|
elif isinstance(parsed_value, dict) and dataclasses.is_dataclass(get_args(self._python_type)[1]):
|
|
419
366
|
j = JsonParamType(get_args(self._python_type)[1])
|
|
420
|
-
|
|
367
|
+
# turn object back into json string
|
|
368
|
+
return {k: j.convert(json.dumps(v), param, ctx) for k, v in parsed_value.items()}
|
|
421
369
|
|
|
422
370
|
return parsed_value
|
|
423
371
|
|
|
424
|
-
|
|
425
|
-
"""
|
|
426
|
-
This function supports backward compatibility for the Pydantic v1 plugin.
|
|
427
|
-
If the class is a Pydantic BaseModel, it attempts to parse JSON input using
|
|
428
|
-
the appropriate version of Pydantic (v1 or v2).
|
|
429
|
-
"""
|
|
430
|
-
try:
|
|
431
|
-
if importlib.util.find_spec("pydantic.v1") is not None:
|
|
432
|
-
from pydantic import BaseModel as BaseModelV2
|
|
433
|
-
|
|
434
|
-
if issubclass(self._python_type, BaseModelV2):
|
|
435
|
-
return self._python_type.model_validate_json(
|
|
436
|
-
json.dumps(parsed_value), strict=False, context={"deserialize": True}
|
|
437
|
-
)
|
|
438
|
-
except ImportError:
|
|
439
|
-
pass
|
|
440
|
-
|
|
441
|
-
# The behavior of the Pydantic v1 plugin.
|
|
442
|
-
return self._python_type.parse_raw(json.dumps(parsed_value))
|
|
443
|
-
return None
|
|
372
|
+
from pydantic import BaseModel
|
|
444
373
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
Modifies the literal object recursively to replace the URIs with the native paths.
|
|
449
|
-
"""
|
|
450
|
-
if lit.collection:
|
|
451
|
-
for l in lit.collection.literals:
|
|
452
|
-
modify_literal_uris(l)
|
|
453
|
-
elif lit.map:
|
|
454
|
-
for k, v in lit.map.literals.items():
|
|
455
|
-
modify_literal_uris(v)
|
|
456
|
-
elif lit.scalar:
|
|
457
|
-
if lit.scalar.blob and lit.scalar.blob.uri and lit.scalar.blob.uri.startswith(RemoteFSPathResolver.protocol):
|
|
458
|
-
lit.scalar.blob._uri = RemoteFSPathResolver.resolve_remote_path(lit.scalar.blob.uri)
|
|
459
|
-
elif lit.scalar.union:
|
|
460
|
-
modify_literal_uris(lit.scalar.union.value)
|
|
461
|
-
elif (
|
|
462
|
-
lit.scalar.structured_dataset
|
|
463
|
-
and lit.scalar.structured_dataset.uri
|
|
464
|
-
and lit.scalar.structured_dataset.uri.startswith(RemoteFSPathResolver.protocol)
|
|
465
|
-
):
|
|
466
|
-
lit.scalar.structured_dataset._uri = RemoteFSPathResolver.resolve_remote_path(
|
|
467
|
-
lit.scalar.structured_dataset.uri
|
|
374
|
+
if issubclass(self._python_type, BaseModel):
|
|
375
|
+
return typing.cast(BaseModel, self._python_type).model_validate_json(
|
|
376
|
+
json.dumps(parsed_value), strict=False, context={"deserialize": True}
|
|
468
377
|
)
|
|
378
|
+
elif dataclasses.is_dataclass(self._python_type):
|
|
379
|
+
from mashumaro.codecs.json import JSONDecoder
|
|
469
380
|
|
|
381
|
+
decoder = JSONDecoder(self._python_type)
|
|
382
|
+
return decoder.decode(value)
|
|
470
383
|
|
|
471
|
-
|
|
384
|
+
return parsed_value
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
SIMPLE_TYPE_CONVERTER = {
|
|
472
388
|
SimpleType.FLOAT: click.FLOAT,
|
|
473
389
|
SimpleType.INTEGER: click.INT,
|
|
474
390
|
SimpleType.STRING: click.STRING,
|
|
@@ -482,7 +398,7 @@ def literal_type_to_click_type(lt: LiteralType, python_type: typing.Type) -> cli
|
|
|
482
398
|
"""
|
|
483
399
|
Converts a Flyte LiteralType given a python_type to a click.ParamType
|
|
484
400
|
"""
|
|
485
|
-
if lt.simple:
|
|
401
|
+
if lt.HasField("simple"):
|
|
486
402
|
if lt.simple == SimpleType.STRUCT:
|
|
487
403
|
ct = JsonParamType(python_type)
|
|
488
404
|
ct.name = f"JSON object {python_type.__name__}"
|
|
@@ -491,38 +407,38 @@ def literal_type_to_click_type(lt: LiteralType, python_type: typing.Type) -> cli
|
|
|
491
407
|
return SIMPLE_TYPE_CONVERTER[lt.simple]
|
|
492
408
|
raise NotImplementedError(f"Type {lt.simple} is not supported in pyflyte run")
|
|
493
409
|
|
|
494
|
-
if lt.
|
|
495
|
-
return EnumParamType(python_type) # type: ignore
|
|
496
|
-
|
|
497
|
-
if lt.structured_dataset_type:
|
|
410
|
+
if lt.HasField("structured_dataset_type"):
|
|
498
411
|
return StructuredDatasetParamType()
|
|
499
412
|
|
|
500
|
-
if lt.collection_type or lt.map_value_type:
|
|
413
|
+
if lt.HasField("collection_type") or lt.HasField("map_value_type"):
|
|
501
414
|
ct = JsonParamType(python_type)
|
|
502
|
-
if lt.collection_type:
|
|
415
|
+
if lt.HasField("collection_type"):
|
|
503
416
|
ct.name = "json list"
|
|
504
417
|
else:
|
|
505
418
|
ct.name = "json dictionary"
|
|
506
419
|
return ct
|
|
507
420
|
|
|
508
|
-
if lt.blob:
|
|
421
|
+
if lt.HasField("blob"):
|
|
509
422
|
if lt.blob.dimensionality == BlobType.BlobDimensionality.SINGLE:
|
|
510
423
|
if lt.blob.format == FlytePickleTransformer.PYTHON_PICKLE_FORMAT:
|
|
511
424
|
return PickleParamType()
|
|
425
|
+
# TODO: Add JSONIteratorTransformer
|
|
512
426
|
# elif lt.blob.format == JSONIteratorTransformer.JSON_ITERATOR_FORMAT:
|
|
513
427
|
# return JSONIteratorParamType()
|
|
514
428
|
return FileParamType()
|
|
515
429
|
return DirParamType()
|
|
516
430
|
|
|
517
|
-
if lt.union_type:
|
|
431
|
+
if lt.HasField("union_type"):
|
|
518
432
|
cts = []
|
|
519
433
|
for i in range(len(lt.union_type.variants)):
|
|
520
434
|
variant = lt.union_type.variants[i]
|
|
521
435
|
variant_python_type = typing.get_args(python_type)[i]
|
|
522
|
-
|
|
523
|
-
cts.append(ct)
|
|
436
|
+
cts.append(literal_type_to_click_type(variant, variant_python_type))
|
|
524
437
|
return UnionParamType(cts)
|
|
525
438
|
|
|
439
|
+
if lt.HasField("enum_type"):
|
|
440
|
+
return EnumParamType(python_type) # type: ignore
|
|
441
|
+
|
|
526
442
|
return click.UNPROCESSED
|
|
527
443
|
|
|
528
444
|
|
|
@@ -533,9 +449,7 @@ class FlyteLiteralConverter(object):
|
|
|
533
449
|
self,
|
|
534
450
|
literal_type: LiteralType,
|
|
535
451
|
python_type: typing.Type,
|
|
536
|
-
is_remote: bool,
|
|
537
452
|
):
|
|
538
|
-
self._is_remote = is_remote
|
|
539
453
|
self._literal_type = literal_type
|
|
540
454
|
self._python_type = python_type
|
|
541
455
|
self._click_type = literal_type_to_click_type(literal_type, python_type)
|
|
@@ -551,25 +465,15 @@ class FlyteLiteralConverter(object):
|
|
|
551
465
|
self, ctx: click.Context, param: typing.Optional[click.Parameter], value: typing.Any
|
|
552
466
|
) -> typing.Union[Literal, typing.Any]:
|
|
553
467
|
"""
|
|
554
|
-
Convert the value to a
|
|
468
|
+
Convert the value to a python native type. This is used by click to convert the input.
|
|
555
469
|
"""
|
|
556
|
-
if isinstance(value, ArtifactQuery):
|
|
557
|
-
return value
|
|
558
470
|
try:
|
|
559
471
|
# If the expected Python type is datetime.date, adjust the value to date
|
|
560
472
|
if self._python_type is datetime.date:
|
|
561
473
|
# Click produces datetime, so converting to date to avoid type mismatch error
|
|
562
474
|
value = value.date()
|
|
563
|
-
# If the input matches the default value in the launch plan, serialization can be skipped.
|
|
564
|
-
if param and value == param.default:
|
|
565
|
-
return None
|
|
566
475
|
|
|
567
|
-
|
|
568
|
-
if not self._is_remote:
|
|
569
|
-
return value
|
|
570
|
-
|
|
571
|
-
lit = asyncio.run(TypeEngine.to_literal(value, self._python_type, self._literal_type))
|
|
572
|
-
return lit
|
|
476
|
+
return value
|
|
573
477
|
except click.BadParameter:
|
|
574
478
|
raise
|
|
575
479
|
except Exception as e:
|
|
@@ -577,3 +481,58 @@ class FlyteLiteralConverter(object):
|
|
|
577
481
|
f"Failed to convert param: {param if param else 'NA'}, value: {value} to type: {self._python_type}."
|
|
578
482
|
f" Reason {e}"
|
|
579
483
|
) from e
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
def to_click_option(
|
|
487
|
+
input_name: str,
|
|
488
|
+
literal_var: Variable,
|
|
489
|
+
python_type: typing.Type,
|
|
490
|
+
default_val: typing.Any,
|
|
491
|
+
) -> click.Option:
|
|
492
|
+
"""
|
|
493
|
+
This handles converting workflow input types to supported click parameters with callbacks to initialize
|
|
494
|
+
the input values to their expected types.
|
|
495
|
+
"""
|
|
496
|
+
from flyteidl.core.types_pb2 import SimpleType
|
|
497
|
+
|
|
498
|
+
if input_name != input_name.lower():
|
|
499
|
+
# Click does not support uppercase option names: https://github.com/pallets/click/issues/837
|
|
500
|
+
raise ValueError(f"Workflow input name must be lowercase: {input_name!r}")
|
|
501
|
+
|
|
502
|
+
literal_converter = FlyteLiteralConverter(
|
|
503
|
+
literal_type=literal_var.type,
|
|
504
|
+
python_type=python_type,
|
|
505
|
+
)
|
|
506
|
+
|
|
507
|
+
if literal_converter.is_bool() and not default_val:
|
|
508
|
+
default_val = False
|
|
509
|
+
|
|
510
|
+
description_extra = ""
|
|
511
|
+
if literal_var.type.simple == SimpleType.STRUCT:
|
|
512
|
+
if default_val:
|
|
513
|
+
# pydantic v2
|
|
514
|
+
if hasattr(default_val, "model_dump_json"):
|
|
515
|
+
default_val = default_val.model_dump_json()
|
|
516
|
+
else:
|
|
517
|
+
encoder = JSONEncoder(python_type)
|
|
518
|
+
default_val = encoder.encode(default_val)
|
|
519
|
+
if literal_var.type.metadata:
|
|
520
|
+
description_extra = f": {MessageToDict(literal_var.type.metadata)}"
|
|
521
|
+
|
|
522
|
+
# If a query has been specified, the input is never strictly required at this layer
|
|
523
|
+
required = False if default_val is not None else True
|
|
524
|
+
is_flag: typing.Optional[bool] = None
|
|
525
|
+
if literal_converter.is_bool():
|
|
526
|
+
required = False
|
|
527
|
+
is_flag = True
|
|
528
|
+
|
|
529
|
+
return click.Option(
|
|
530
|
+
param_decls=[f"--{input_name}"],
|
|
531
|
+
type=literal_converter.click_type,
|
|
532
|
+
is_flag=is_flag,
|
|
533
|
+
default=default_val,
|
|
534
|
+
show_default=True,
|
|
535
|
+
required=required,
|
|
536
|
+
help=literal_var.description + description_extra,
|
|
537
|
+
callback=literal_converter.convert,
|
|
538
|
+
)
|
flyte/_cli/_run.py
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import inspect
|
|
3
4
|
from dataclasses import dataclass, field, fields
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
from types import ModuleType
|
|
6
7
|
from typing import Any, Dict, List, cast
|
|
7
8
|
|
|
8
9
|
import click
|
|
9
|
-
from click import Context
|
|
10
|
+
from click import Context, Parameter
|
|
10
11
|
from rich.console import Console
|
|
11
12
|
from typing_extensions import get_args
|
|
12
13
|
|
|
@@ -17,6 +18,7 @@ from .._task import TaskTemplate
|
|
|
17
18
|
from ..remote import Run
|
|
18
19
|
from . import _common as common
|
|
19
20
|
from ._common import CLIConfig
|
|
21
|
+
from ._params import to_click_option
|
|
20
22
|
|
|
21
23
|
|
|
22
24
|
@dataclass
|
|
@@ -77,7 +79,7 @@ class RunTaskCommand(click.Command):
|
|
|
77
79
|
copy_style=self.run_args.copy_style,
|
|
78
80
|
version=self.run_args.copy_style,
|
|
79
81
|
mode="local" if self.run_args.local else "remote",
|
|
80
|
-
).run(self.obj)
|
|
82
|
+
).run(self.obj, **ctx.params)
|
|
81
83
|
if isinstance(r, Run) and r.action is not None:
|
|
82
84
|
console = Console()
|
|
83
85
|
console.print(
|
|
@@ -89,6 +91,26 @@ class RunTaskCommand(click.Command):
|
|
|
89
91
|
)
|
|
90
92
|
)
|
|
91
93
|
|
|
94
|
+
def get_params(self, ctx: Context) -> List[Parameter]:
|
|
95
|
+
# Note this function may be called multiple times by click.
|
|
96
|
+
task = self.obj
|
|
97
|
+
from .._internal.runtime.types_serde import transform_native_to_typed_interface
|
|
98
|
+
|
|
99
|
+
interface = transform_native_to_typed_interface(task.native_interface)
|
|
100
|
+
if interface is None:
|
|
101
|
+
return super().get_params(ctx)
|
|
102
|
+
inputs_interface = task.native_interface.inputs
|
|
103
|
+
|
|
104
|
+
params: List[Parameter] = []
|
|
105
|
+
for name, var in interface.inputs.variables.items():
|
|
106
|
+
default_val = None
|
|
107
|
+
if inputs_interface[name][1] is not inspect._empty:
|
|
108
|
+
default_val = inputs_interface[name][1]
|
|
109
|
+
params.append(to_click_option(name, var, inputs_interface[name][0], default_val))
|
|
110
|
+
|
|
111
|
+
self.params = params
|
|
112
|
+
return super().get_params(ctx)
|
|
113
|
+
|
|
92
114
|
|
|
93
115
|
class TaskPerFileGroup(common.ObjectsPerFileGroup):
|
|
94
116
|
"""
|
flyte/_cli/main.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import rich_click as click
|
|
2
2
|
|
|
3
|
+
from ..config import Config
|
|
3
4
|
from ._common import CLIConfig
|
|
4
5
|
from ._create import create
|
|
5
6
|
from ._deploy import deploy
|
|
@@ -57,13 +58,38 @@ def _verbosity_to_loglevel(verbosity: int) -> int | None:
|
|
|
57
58
|
required=False,
|
|
58
59
|
help="Override for org",
|
|
59
60
|
)
|
|
61
|
+
@click.option(
|
|
62
|
+
"-c",
|
|
63
|
+
"--config",
|
|
64
|
+
"config_file",
|
|
65
|
+
required=False,
|
|
66
|
+
type=click.Path(exists=True),
|
|
67
|
+
help="Path to config file (YAML format) to use for the CLI. If not specified,"
|
|
68
|
+
" the default config file will be used.",
|
|
69
|
+
)
|
|
60
70
|
@click.pass_context
|
|
61
|
-
def main(
|
|
71
|
+
def main(
|
|
72
|
+
ctx: click.Context,
|
|
73
|
+
endpoint: str | None,
|
|
74
|
+
insecure: bool,
|
|
75
|
+
verbose: int,
|
|
76
|
+
org_override: str | None,
|
|
77
|
+
config_file: str | None,
|
|
78
|
+
):
|
|
62
79
|
"""
|
|
63
80
|
v2 cli. Root command, please use one of the subcommands.
|
|
64
81
|
"""
|
|
65
82
|
log_level = _verbosity_to_loglevel(verbose)
|
|
66
|
-
|
|
83
|
+
|
|
84
|
+
config = Config.auto(config_file=config_file)
|
|
85
|
+
|
|
86
|
+
ctx.obj = CLIConfig(
|
|
87
|
+
log_level=log_level,
|
|
88
|
+
endpoint=endpoint or config.platform.endpoint,
|
|
89
|
+
insecure=insecure or config.platform.insecure,
|
|
90
|
+
org_override=org_override or config.task.org,
|
|
91
|
+
config=config,
|
|
92
|
+
)
|
|
67
93
|
|
|
68
94
|
|
|
69
95
|
main.add_command(run)
|
flyte/_image.py
CHANGED
|
@@ -342,8 +342,7 @@ class Image:
|
|
|
342
342
|
"""Internal hacky function to see if the current install is editable or not."""
|
|
343
343
|
curr = Path(__file__)
|
|
344
344
|
pyproject = curr.parent.parent.parent / "pyproject.toml"
|
|
345
|
-
|
|
346
|
-
return pyproject.exists() and dist_folder
|
|
345
|
+
return pyproject.exists()
|
|
347
346
|
|
|
348
347
|
@classmethod
|
|
349
348
|
def from_uv_debian(
|