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.

Files changed (209) hide show
  1. flyte/_cli/_common.py +12 -0
  2. {union → flyte}/_cli/_params.py +106 -147
  3. flyte/_cli/_run.py +24 -2
  4. flyte/_cli/main.py +28 -2
  5. flyte/_image.py +1 -2
  6. flyte/_initialize.py +24 -15
  7. flyte/_internal/runtime/convert.py +6 -0
  8. flyte/_run.py +0 -1
  9. flyte/_version.py +2 -2
  10. flyte/config/__init__.py +168 -0
  11. flyte/config/_config.py +196 -0
  12. flyte/config/_internal.py +64 -0
  13. flyte/remote/_console.py +1 -1
  14. flyte/types/_type_engine.py +4 -3
  15. {flyte-0.0.1b3.dist-info → flyte-0.2.0b0.dist-info}/METADATA +1 -1
  16. flyte-0.2.0b0.dist-info/RECORD +204 -0
  17. flyte-0.0.1b3.dist-info/RECORD +0 -390
  18. union/__init__.py +0 -54
  19. union/_api_commons.py +0 -3
  20. union/_bin/__init__.py +0 -0
  21. union/_bin/runtime.py +0 -113
  22. union/_build.py +0 -25
  23. union/_cache/__init__.py +0 -12
  24. union/_cache/cache.py +0 -141
  25. union/_cache/defaults.py +0 -9
  26. union/_cache/policy_function_body.py +0 -42
  27. union/_cli/__init__.py +0 -0
  28. union/_cli/_common.py +0 -263
  29. union/_cli/_create.py +0 -40
  30. union/_cli/_delete.py +0 -23
  31. union/_cli/_deploy.py +0 -120
  32. union/_cli/_get.py +0 -162
  33. union/_cli/_run.py +0 -150
  34. union/_cli/main.py +0 -72
  35. union/_code_bundle/__init__.py +0 -8
  36. union/_code_bundle/_ignore.py +0 -113
  37. union/_code_bundle/_packaging.py +0 -187
  38. union/_code_bundle/_utils.py +0 -342
  39. union/_code_bundle/bundle.py +0 -176
  40. union/_context.py +0 -146
  41. union/_datastructures.py +0 -295
  42. union/_deploy.py +0 -185
  43. union/_doc.py +0 -29
  44. union/_docstring.py +0 -26
  45. union/_environment.py +0 -43
  46. union/_group.py +0 -31
  47. union/_hash.py +0 -23
  48. union/_image.py +0 -760
  49. union/_initialize.py +0 -585
  50. union/_interface.py +0 -84
  51. union/_internal/__init__.py +0 -3
  52. union/_internal/controllers/__init__.py +0 -77
  53. union/_internal/controllers/_local_controller.py +0 -77
  54. union/_internal/controllers/pbhash.py +0 -39
  55. union/_internal/controllers/remote/__init__.py +0 -40
  56. union/_internal/controllers/remote/_action.py +0 -131
  57. union/_internal/controllers/remote/_client.py +0 -43
  58. union/_internal/controllers/remote/_controller.py +0 -169
  59. union/_internal/controllers/remote/_core.py +0 -341
  60. union/_internal/controllers/remote/_informer.py +0 -260
  61. union/_internal/controllers/remote/_service_protocol.py +0 -44
  62. union/_internal/imagebuild/__init__.py +0 -11
  63. union/_internal/imagebuild/docker_builder.py +0 -416
  64. union/_internal/imagebuild/image_builder.py +0 -243
  65. union/_internal/imagebuild/remote_builder.py +0 -0
  66. union/_internal/resolvers/__init__.py +0 -0
  67. union/_internal/resolvers/_task_module.py +0 -31
  68. union/_internal/resolvers/common.py +0 -24
  69. union/_internal/resolvers/default.py +0 -27
  70. union/_internal/runtime/__init__.py +0 -0
  71. union/_internal/runtime/convert.py +0 -163
  72. union/_internal/runtime/entrypoints.py +0 -121
  73. union/_internal/runtime/io.py +0 -136
  74. union/_internal/runtime/resources_serde.py +0 -134
  75. union/_internal/runtime/task_serde.py +0 -202
  76. union/_internal/runtime/taskrunner.py +0 -179
  77. union/_internal/runtime/types_serde.py +0 -53
  78. union/_logging.py +0 -124
  79. union/_protos/__init__.py +0 -0
  80. union/_protos/common/authorization_pb2.py +0 -66
  81. union/_protos/common/authorization_pb2.pyi +0 -106
  82. union/_protos/common/authorization_pb2_grpc.py +0 -4
  83. union/_protos/common/identifier_pb2.py +0 -71
  84. union/_protos/common/identifier_pb2.pyi +0 -82
  85. union/_protos/common/identifier_pb2_grpc.py +0 -4
  86. union/_protos/common/identity_pb2.py +0 -48
  87. union/_protos/common/identity_pb2.pyi +0 -72
  88. union/_protos/common/identity_pb2_grpc.py +0 -4
  89. union/_protos/common/list_pb2.py +0 -36
  90. union/_protos/common/list_pb2.pyi +0 -69
  91. union/_protos/common/list_pb2_grpc.py +0 -4
  92. union/_protos/common/policy_pb2.py +0 -37
  93. union/_protos/common/policy_pb2.pyi +0 -27
  94. union/_protos/common/policy_pb2_grpc.py +0 -4
  95. union/_protos/common/role_pb2.py +0 -37
  96. union/_protos/common/role_pb2.pyi +0 -51
  97. union/_protos/common/role_pb2_grpc.py +0 -4
  98. union/_protos/common/runtime_version_pb2.py +0 -28
  99. union/_protos/common/runtime_version_pb2.pyi +0 -24
  100. union/_protos/common/runtime_version_pb2_grpc.py +0 -4
  101. union/_protos/logs/dataplane/payload_pb2.py +0 -96
  102. union/_protos/logs/dataplane/payload_pb2.pyi +0 -168
  103. union/_protos/logs/dataplane/payload_pb2_grpc.py +0 -4
  104. union/_protos/secret/definition_pb2.py +0 -49
  105. union/_protos/secret/definition_pb2.pyi +0 -93
  106. union/_protos/secret/definition_pb2_grpc.py +0 -4
  107. union/_protos/secret/payload_pb2.py +0 -62
  108. union/_protos/secret/payload_pb2.pyi +0 -94
  109. union/_protos/secret/payload_pb2_grpc.py +0 -4
  110. union/_protos/secret/secret_pb2.py +0 -38
  111. union/_protos/secret/secret_pb2.pyi +0 -6
  112. union/_protos/secret/secret_pb2_grpc.py +0 -198
  113. union/_protos/validate/validate/validate_pb2.py +0 -76
  114. union/_protos/workflow/node_execution_service_pb2.py +0 -26
  115. union/_protos/workflow/node_execution_service_pb2.pyi +0 -4
  116. union/_protos/workflow/node_execution_service_pb2_grpc.py +0 -32
  117. union/_protos/workflow/queue_service_pb2.py +0 -75
  118. union/_protos/workflow/queue_service_pb2.pyi +0 -103
  119. union/_protos/workflow/queue_service_pb2_grpc.py +0 -172
  120. union/_protos/workflow/run_definition_pb2.py +0 -100
  121. union/_protos/workflow/run_definition_pb2.pyi +0 -256
  122. union/_protos/workflow/run_definition_pb2_grpc.py +0 -4
  123. union/_protos/workflow/run_logs_service_pb2.py +0 -41
  124. union/_protos/workflow/run_logs_service_pb2.pyi +0 -28
  125. union/_protos/workflow/run_logs_service_pb2_grpc.py +0 -69
  126. union/_protos/workflow/run_service_pb2.py +0 -133
  127. union/_protos/workflow/run_service_pb2.pyi +0 -173
  128. union/_protos/workflow/run_service_pb2_grpc.py +0 -412
  129. union/_protos/workflow/state_service_pb2.py +0 -58
  130. union/_protos/workflow/state_service_pb2.pyi +0 -69
  131. union/_protos/workflow/state_service_pb2_grpc.py +0 -138
  132. union/_protos/workflow/task_definition_pb2.py +0 -72
  133. union/_protos/workflow/task_definition_pb2.pyi +0 -65
  134. union/_protos/workflow/task_definition_pb2_grpc.py +0 -4
  135. union/_protos/workflow/task_service_pb2.py +0 -44
  136. union/_protos/workflow/task_service_pb2.pyi +0 -31
  137. union/_protos/workflow/task_service_pb2_grpc.py +0 -104
  138. union/_resources.py +0 -226
  139. union/_retry.py +0 -32
  140. union/_reusable_environment.py +0 -25
  141. union/_run.py +0 -374
  142. union/_secret.py +0 -61
  143. union/_task.py +0 -354
  144. union/_task_environment.py +0 -186
  145. union/_timeout.py +0 -47
  146. union/_tools.py +0 -27
  147. union/_utils/__init__.py +0 -11
  148. union/_utils/asyn.py +0 -119
  149. union/_utils/file_handling.py +0 -71
  150. union/_utils/helpers.py +0 -46
  151. union/_utils/lazy_module.py +0 -54
  152. union/_utils/uv_script_parser.py +0 -49
  153. union/_version.py +0 -21
  154. union/connectors/__init__.py +0 -0
  155. union/errors.py +0 -128
  156. union/extras/__init__.py +0 -5
  157. union/extras/_container.py +0 -263
  158. union/io/__init__.py +0 -11
  159. union/io/_dataframe.py +0 -0
  160. union/io/_dir.py +0 -425
  161. union/io/_file.py +0 -418
  162. union/io/pickle/__init__.py +0 -0
  163. union/io/pickle/transformer.py +0 -117
  164. union/io/structured_dataset/__init__.py +0 -122
  165. union/io/structured_dataset/basic_dfs.py +0 -219
  166. union/io/structured_dataset/structured_dataset.py +0 -1057
  167. union/py.typed +0 -0
  168. union/remote/__init__.py +0 -23
  169. union/remote/_client/__init__.py +0 -0
  170. union/remote/_client/_protocols.py +0 -129
  171. union/remote/_client/auth/__init__.py +0 -12
  172. union/remote/_client/auth/_authenticators/__init__.py +0 -0
  173. union/remote/_client/auth/_authenticators/base.py +0 -391
  174. union/remote/_client/auth/_authenticators/client_credentials.py +0 -73
  175. union/remote/_client/auth/_authenticators/device_code.py +0 -120
  176. union/remote/_client/auth/_authenticators/external_command.py +0 -77
  177. union/remote/_client/auth/_authenticators/factory.py +0 -200
  178. union/remote/_client/auth/_authenticators/pkce.py +0 -515
  179. union/remote/_client/auth/_channel.py +0 -184
  180. union/remote/_client/auth/_client_config.py +0 -83
  181. union/remote/_client/auth/_default_html.py +0 -32
  182. union/remote/_client/auth/_grpc_utils/__init__.py +0 -0
  183. union/remote/_client/auth/_grpc_utils/auth_interceptor.py +0 -204
  184. union/remote/_client/auth/_grpc_utils/default_metadata_interceptor.py +0 -144
  185. union/remote/_client/auth/_keyring.py +0 -154
  186. union/remote/_client/auth/_token_client.py +0 -258
  187. union/remote/_client/auth/errors.py +0 -16
  188. union/remote/_client/controlplane.py +0 -86
  189. union/remote/_data.py +0 -149
  190. union/remote/_logs.py +0 -74
  191. union/remote/_project.py +0 -86
  192. union/remote/_run.py +0 -820
  193. union/remote/_secret.py +0 -132
  194. union/remote/_task.py +0 -193
  195. union/report/__init__.py +0 -3
  196. union/report/_report.py +0 -178
  197. union/report/_template.html +0 -124
  198. union/storage/__init__.py +0 -24
  199. union/storage/_remote_fs.py +0 -34
  200. union/storage/_storage.py +0 -247
  201. union/storage/_utils.py +0 -5
  202. union/types/__init__.py +0 -11
  203. union/types/_renderer.py +0 -162
  204. union/types/_string_literals.py +0 -120
  205. union/types/_type_engine.py +0 -2131
  206. union/types/_utils.py +0 -80
  207. {flyte-0.0.1b3.dist-info → flyte-0.2.0b0.dist-info}/WHEEL +0 -0
  208. {flyte-0.0.1b3.dist-info → flyte-0.2.0b0.dist-info}/entry_points.txt +0 -0
  209. {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
 
@@ -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 union._logging import logger
21
- from union.io import Dir, File
22
- from union.io.pickle.transformer import FlytePickleTransformer
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
- if isinstance(value, ArtifactQuery):
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, remote_directory=remote_directory)
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
- if isinstance(value, ArtifactQuery):
142
- return value
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, remote_path=remote_path)
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: click.Parameter) -> t.Optional[str]:
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.verbose > 0:
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
- part = part.strip().lower()
174
+ new_part = part.strip().lower()
215
175
 
216
176
  # Match 1:24 or :45
217
- m_colon = re.match(r"^(?:(\d+):)?(\d+)$", part)
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?$", part)
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") or value.endswith(".yml"):
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) == typing.get_origin(self._python_type) or type(parsed_value) == self._python_type:
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
- return [j.convert(v, param, ctx) for v in parsed_value]
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
- return {k: j.convert(v, param, ctx) for k, v in parsed_value.items()}
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
- if is_pydantic_basemodel(self._python_type):
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
- def modify_literal_uris(lit: Literal):
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
- SIMPLE_TYPE_CONVERTER: typing.Dict[SimpleType, click.ParamType] = {
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.enum_type:
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
- ct = literal_type_to_click_type(variant, variant_python_type)
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 Flyte Literal or a python native type. This is used by click to convert the input.
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
- # If this is used for remote execution, then we need to convert it back to a python native type
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(ctx: click.Context, endpoint: str | None, insecure: bool, verbose: int, org_override: str | None):
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
- ctx.obj = CLIConfig(log_level=log_level, endpoint=endpoint, insecure=insecure, org_override=org_override)
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
- dist_folder = curr.parent.parent.parent / "dist"
346
- return pyproject.exists() and dist_folder
345
+ return pyproject.exists()
347
346
 
348
347
  @classmethod
349
348
  def from_uv_debian(