flyte 2.0.0b13__py3-none-any.whl → 2.0.0b30__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.
Files changed (211) hide show
  1. flyte/__init__.py +18 -2
  2. flyte/_bin/debug.py +38 -0
  3. flyte/_bin/runtime.py +62 -8
  4. flyte/_cache/cache.py +4 -2
  5. flyte/_cache/local_cache.py +216 -0
  6. flyte/_code_bundle/_ignore.py +12 -4
  7. flyte/_code_bundle/_packaging.py +13 -9
  8. flyte/_code_bundle/_utils.py +18 -10
  9. flyte/_code_bundle/bundle.py +17 -9
  10. flyte/_constants.py +1 -0
  11. flyte/_context.py +4 -1
  12. flyte/_custom_context.py +73 -0
  13. flyte/_debug/constants.py +38 -0
  14. flyte/_debug/utils.py +17 -0
  15. flyte/_debug/vscode.py +307 -0
  16. flyte/_deploy.py +235 -61
  17. flyte/_environment.py +20 -6
  18. flyte/_excepthook.py +1 -1
  19. flyte/_hash.py +1 -16
  20. flyte/_image.py +178 -81
  21. flyte/_initialize.py +132 -51
  22. flyte/_interface.py +39 -2
  23. flyte/_internal/controllers/__init__.py +4 -5
  24. flyte/_internal/controllers/_local_controller.py +70 -29
  25. flyte/_internal/controllers/_trace.py +1 -1
  26. flyte/_internal/controllers/remote/__init__.py +0 -2
  27. flyte/_internal/controllers/remote/_action.py +14 -16
  28. flyte/_internal/controllers/remote/_client.py +1 -1
  29. flyte/_internal/controllers/remote/_controller.py +68 -70
  30. flyte/_internal/controllers/remote/_core.py +127 -99
  31. flyte/_internal/controllers/remote/_informer.py +19 -10
  32. flyte/_internal/controllers/remote/_service_protocol.py +7 -7
  33. flyte/_internal/imagebuild/docker_builder.py +181 -69
  34. flyte/_internal/imagebuild/image_builder.py +0 -5
  35. flyte/_internal/imagebuild/remote_builder.py +155 -64
  36. flyte/_internal/imagebuild/utils.py +51 -2
  37. flyte/_internal/resolvers/_task_module.py +5 -38
  38. flyte/_internal/resolvers/default.py +2 -2
  39. flyte/_internal/runtime/convert.py +110 -21
  40. flyte/_internal/runtime/entrypoints.py +27 -1
  41. flyte/_internal/runtime/io.py +21 -8
  42. flyte/_internal/runtime/resources_serde.py +20 -6
  43. flyte/_internal/runtime/reuse.py +1 -1
  44. flyte/_internal/runtime/rusty.py +20 -5
  45. flyte/_internal/runtime/task_serde.py +34 -19
  46. flyte/_internal/runtime/taskrunner.py +22 -4
  47. flyte/_internal/runtime/trigger_serde.py +160 -0
  48. flyte/_internal/runtime/types_serde.py +1 -1
  49. flyte/_keyring/__init__.py +0 -0
  50. flyte/_keyring/file.py +115 -0
  51. flyte/_logging.py +201 -39
  52. flyte/_map.py +111 -14
  53. flyte/_module.py +70 -0
  54. flyte/_pod.py +4 -3
  55. flyte/_resources.py +213 -31
  56. flyte/_run.py +110 -39
  57. flyte/_task.py +75 -16
  58. flyte/_task_environment.py +105 -29
  59. flyte/_task_plugins.py +4 -2
  60. flyte/_trace.py +5 -0
  61. flyte/_trigger.py +1000 -0
  62. flyte/_utils/__init__.py +2 -1
  63. flyte/_utils/asyn.py +3 -1
  64. flyte/_utils/coro_management.py +2 -1
  65. flyte/_utils/docker_credentials.py +173 -0
  66. flyte/_utils/module_loader.py +17 -2
  67. flyte/_version.py +3 -3
  68. flyte/cli/_abort.py +3 -3
  69. flyte/cli/_build.py +3 -6
  70. flyte/cli/_common.py +78 -7
  71. flyte/cli/_create.py +182 -4
  72. flyte/cli/_delete.py +23 -1
  73. flyte/cli/_deploy.py +63 -16
  74. flyte/cli/_get.py +79 -34
  75. flyte/cli/_params.py +26 -10
  76. flyte/cli/_plugins.py +209 -0
  77. flyte/cli/_run.py +151 -26
  78. flyte/cli/_serve.py +64 -0
  79. flyte/cli/_update.py +37 -0
  80. flyte/cli/_user.py +17 -0
  81. flyte/cli/main.py +30 -4
  82. flyte/config/_config.py +10 -6
  83. flyte/config/_internal.py +1 -0
  84. flyte/config/_reader.py +29 -8
  85. flyte/connectors/__init__.py +11 -0
  86. flyte/connectors/_connector.py +270 -0
  87. flyte/connectors/_server.py +197 -0
  88. flyte/connectors/utils.py +135 -0
  89. flyte/errors.py +22 -2
  90. flyte/extend.py +8 -1
  91. flyte/extras/_container.py +6 -1
  92. flyte/git/__init__.py +3 -0
  93. flyte/git/_config.py +21 -0
  94. flyte/io/__init__.py +2 -0
  95. flyte/io/_dataframe/__init__.py +2 -0
  96. flyte/io/_dataframe/basic_dfs.py +17 -8
  97. flyte/io/_dataframe/dataframe.py +98 -132
  98. flyte/io/_dir.py +575 -113
  99. flyte/io/_file.py +582 -139
  100. flyte/io/_hashing_io.py +342 -0
  101. flyte/models.py +74 -15
  102. flyte/remote/__init__.py +6 -1
  103. flyte/remote/_action.py +34 -26
  104. flyte/remote/_client/_protocols.py +39 -4
  105. flyte/remote/_client/auth/_authenticators/device_code.py +4 -5
  106. flyte/remote/_client/auth/_authenticators/pkce.py +1 -1
  107. flyte/remote/_client/auth/_channel.py +10 -6
  108. flyte/remote/_client/controlplane.py +17 -5
  109. flyte/remote/_console.py +3 -2
  110. flyte/remote/_data.py +6 -6
  111. flyte/remote/_logs.py +3 -3
  112. flyte/remote/_run.py +64 -8
  113. flyte/remote/_secret.py +26 -17
  114. flyte/remote/_task.py +75 -33
  115. flyte/remote/_trigger.py +306 -0
  116. flyte/remote/_user.py +33 -0
  117. flyte/report/_report.py +1 -1
  118. flyte/storage/__init__.py +6 -1
  119. flyte/storage/_config.py +5 -1
  120. flyte/storage/_parallel_reader.py +274 -0
  121. flyte/storage/_storage.py +200 -103
  122. flyte/types/__init__.py +16 -0
  123. flyte/types/_interface.py +2 -2
  124. flyte/types/_pickle.py +35 -8
  125. flyte/types/_string_literals.py +8 -9
  126. flyte/types/_type_engine.py +40 -70
  127. flyte/types/_utils.py +1 -1
  128. flyte-2.0.0b30.data/scripts/debug.py +38 -0
  129. {flyte-2.0.0b13.data → flyte-2.0.0b30.data}/scripts/runtime.py +62 -8
  130. {flyte-2.0.0b13.dist-info → flyte-2.0.0b30.dist-info}/METADATA +11 -3
  131. flyte-2.0.0b30.dist-info/RECORD +192 -0
  132. {flyte-2.0.0b13.dist-info → flyte-2.0.0b30.dist-info}/entry_points.txt +3 -0
  133. flyte/_protos/common/authorization_pb2.py +0 -66
  134. flyte/_protos/common/authorization_pb2.pyi +0 -108
  135. flyte/_protos/common/authorization_pb2_grpc.py +0 -4
  136. flyte/_protos/common/identifier_pb2.py +0 -93
  137. flyte/_protos/common/identifier_pb2.pyi +0 -110
  138. flyte/_protos/common/identifier_pb2_grpc.py +0 -4
  139. flyte/_protos/common/identity_pb2.py +0 -48
  140. flyte/_protos/common/identity_pb2.pyi +0 -72
  141. flyte/_protos/common/identity_pb2_grpc.py +0 -4
  142. flyte/_protos/common/list_pb2.py +0 -36
  143. flyte/_protos/common/list_pb2.pyi +0 -71
  144. flyte/_protos/common/list_pb2_grpc.py +0 -4
  145. flyte/_protos/common/policy_pb2.py +0 -37
  146. flyte/_protos/common/policy_pb2.pyi +0 -27
  147. flyte/_protos/common/policy_pb2_grpc.py +0 -4
  148. flyte/_protos/common/role_pb2.py +0 -37
  149. flyte/_protos/common/role_pb2.pyi +0 -53
  150. flyte/_protos/common/role_pb2_grpc.py +0 -4
  151. flyte/_protos/common/runtime_version_pb2.py +0 -28
  152. flyte/_protos/common/runtime_version_pb2.pyi +0 -24
  153. flyte/_protos/common/runtime_version_pb2_grpc.py +0 -4
  154. flyte/_protos/imagebuilder/definition_pb2.py +0 -59
  155. flyte/_protos/imagebuilder/definition_pb2.pyi +0 -140
  156. flyte/_protos/imagebuilder/definition_pb2_grpc.py +0 -4
  157. flyte/_protos/imagebuilder/payload_pb2.py +0 -32
  158. flyte/_protos/imagebuilder/payload_pb2.pyi +0 -21
  159. flyte/_protos/imagebuilder/payload_pb2_grpc.py +0 -4
  160. flyte/_protos/imagebuilder/service_pb2.py +0 -29
  161. flyte/_protos/imagebuilder/service_pb2.pyi +0 -5
  162. flyte/_protos/imagebuilder/service_pb2_grpc.py +0 -66
  163. flyte/_protos/logs/dataplane/payload_pb2.py +0 -100
  164. flyte/_protos/logs/dataplane/payload_pb2.pyi +0 -177
  165. flyte/_protos/logs/dataplane/payload_pb2_grpc.py +0 -4
  166. flyte/_protos/secret/definition_pb2.py +0 -49
  167. flyte/_protos/secret/definition_pb2.pyi +0 -93
  168. flyte/_protos/secret/definition_pb2_grpc.py +0 -4
  169. flyte/_protos/secret/payload_pb2.py +0 -62
  170. flyte/_protos/secret/payload_pb2.pyi +0 -94
  171. flyte/_protos/secret/payload_pb2_grpc.py +0 -4
  172. flyte/_protos/secret/secret_pb2.py +0 -38
  173. flyte/_protos/secret/secret_pb2.pyi +0 -6
  174. flyte/_protos/secret/secret_pb2_grpc.py +0 -198
  175. flyte/_protos/secret/secret_pb2_grpc_grpc.py +0 -198
  176. flyte/_protos/validate/validate/validate_pb2.py +0 -76
  177. flyte/_protos/workflow/common_pb2.py +0 -27
  178. flyte/_protos/workflow/common_pb2.pyi +0 -14
  179. flyte/_protos/workflow/common_pb2_grpc.py +0 -4
  180. flyte/_protos/workflow/environment_pb2.py +0 -29
  181. flyte/_protos/workflow/environment_pb2.pyi +0 -12
  182. flyte/_protos/workflow/environment_pb2_grpc.py +0 -4
  183. flyte/_protos/workflow/node_execution_service_pb2.py +0 -26
  184. flyte/_protos/workflow/node_execution_service_pb2.pyi +0 -4
  185. flyte/_protos/workflow/node_execution_service_pb2_grpc.py +0 -32
  186. flyte/_protos/workflow/queue_service_pb2.py +0 -109
  187. flyte/_protos/workflow/queue_service_pb2.pyi +0 -166
  188. flyte/_protos/workflow/queue_service_pb2_grpc.py +0 -172
  189. flyte/_protos/workflow/run_definition_pb2.py +0 -121
  190. flyte/_protos/workflow/run_definition_pb2.pyi +0 -327
  191. flyte/_protos/workflow/run_definition_pb2_grpc.py +0 -4
  192. flyte/_protos/workflow/run_logs_service_pb2.py +0 -41
  193. flyte/_protos/workflow/run_logs_service_pb2.pyi +0 -28
  194. flyte/_protos/workflow/run_logs_service_pb2_grpc.py +0 -69
  195. flyte/_protos/workflow/run_service_pb2.py +0 -137
  196. flyte/_protos/workflow/run_service_pb2.pyi +0 -185
  197. flyte/_protos/workflow/run_service_pb2_grpc.py +0 -446
  198. flyte/_protos/workflow/state_service_pb2.py +0 -67
  199. flyte/_protos/workflow/state_service_pb2.pyi +0 -76
  200. flyte/_protos/workflow/state_service_pb2_grpc.py +0 -138
  201. flyte/_protos/workflow/task_definition_pb2.py +0 -79
  202. flyte/_protos/workflow/task_definition_pb2.pyi +0 -81
  203. flyte/_protos/workflow/task_definition_pb2_grpc.py +0 -4
  204. flyte/_protos/workflow/task_service_pb2.py +0 -60
  205. flyte/_protos/workflow/task_service_pb2.pyi +0 -59
  206. flyte/_protos/workflow/task_service_pb2_grpc.py +0 -138
  207. flyte-2.0.0b13.dist-info/RECORD +0 -239
  208. /flyte/{_protos → _debug}/__init__.py +0 -0
  209. {flyte-2.0.0b13.dist-info → flyte-2.0.0b30.dist-info}/WHEEL +0 -0
  210. {flyte-2.0.0b13.dist-info → flyte-2.0.0b30.dist-info}/licenses/LICENSE +0 -0
  211. {flyte-2.0.0b13.dist-info → flyte-2.0.0b30.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,197 @@
1
+ import inspect
2
+ import os
3
+ import sys
4
+ from http import HTTPStatus
5
+ from typing import Callable, Dict, List, Tuple, Type, Union
6
+
7
+ import grpc
8
+ from flyteidl2.core.security_pb2 import Connection
9
+ from flyteidl2.plugins.connector_pb2 import (
10
+ CreateTaskRequest,
11
+ CreateTaskResponse,
12
+ DeleteTaskRequest,
13
+ DeleteTaskResponse,
14
+ GetConnectorRequest,
15
+ GetConnectorResponse,
16
+ GetTaskLogsRequest,
17
+ GetTaskLogsResponse,
18
+ GetTaskMetricsRequest,
19
+ GetTaskMetricsResponse,
20
+ GetTaskRequest,
21
+ GetTaskResponse,
22
+ ListConnectorsRequest,
23
+ ListConnectorsResponse,
24
+ )
25
+ from flyteidl2.service.connector_pb2_grpc import (
26
+ AsyncConnectorServiceServicer,
27
+ ConnectorMetadataServiceServicer,
28
+ )
29
+ from prometheus_client import Counter, Summary
30
+
31
+ from flyte._internal.runtime.convert import Inputs, convert_from_inputs_to_native
32
+ from flyte._logging import logger
33
+ from flyte.connectors._connector import ConnectorRegistry, FlyteConnectorNotFound, get_resource_proto
34
+ from flyte.connectors.utils import _start_grpc_server
35
+ from flyte.models import NativeInterface, _has_default
36
+ from flyte.syncify import syncify
37
+ from flyte.types import TypeEngine
38
+
39
+ metric_prefix = "flyte_connector_"
40
+ create_operation = "create"
41
+ get_operation = "get"
42
+ delete_operation = "delete"
43
+
44
+ # Follow the naming convention. https://prometheus.io/docs/practices/naming/
45
+ request_success_count = Counter(
46
+ f"{metric_prefix}requests_success_total",
47
+ "Total number of successful requests",
48
+ ["task_type", "operation"],
49
+ )
50
+ request_failure_count = Counter(
51
+ f"{metric_prefix}requests_failure_total",
52
+ "Total number of failed requests",
53
+ ["task_type", "operation", "error_code"],
54
+ )
55
+ request_latency = Summary(
56
+ f"{metric_prefix}request_latency_seconds",
57
+ "Time spent processing connector request",
58
+ ["task_type", "operation"],
59
+ )
60
+ input_literal_size = Summary(f"{metric_prefix}input_literal_bytes", "Size of input literal", ["task_type"])
61
+
62
+
63
+ def _handle_exception(e: Exception, context: grpc.ServicerContext, task_type: str, operation: str):
64
+ if isinstance(e, FlyteConnectorNotFound):
65
+ error_message = f"Cannot find connector for task type: {task_type}."
66
+ logger.error(error_message)
67
+ context.set_code(grpc.StatusCode.NOT_FOUND)
68
+ context.set_details(error_message)
69
+ request_failure_count.labels(task_type=task_type, operation=operation, error_code=HTTPStatus.NOT_FOUND).inc()
70
+ else:
71
+ error_message = f"failed to {operation} {task_type} task with error:\n {e}."
72
+ logger.error(error_message)
73
+ context.set_code(grpc.StatusCode.INTERNAL)
74
+ context.set_details(error_message)
75
+ request_failure_count.labels(
76
+ task_type=task_type, operation=operation, error_code=HTTPStatus.INTERNAL_SERVER_ERROR
77
+ ).inc()
78
+
79
+
80
+ class ConnectorService:
81
+ @syncify
82
+ @classmethod
83
+ async def run(cls, port: int, prometheus_port: int, worker: int, timeout: int | None, modules: List[str] | None):
84
+ working_dir = os.getcwd()
85
+ if all(os.path.realpath(path) != working_dir for path in sys.path):
86
+ sys.path.append(working_dir)
87
+ await _start_grpc_server(port, prometheus_port, worker, timeout, modules)
88
+
89
+
90
+ def record_connector_metrics(func: Callable):
91
+ async def wrapper(
92
+ self,
93
+ request: Union[CreateTaskRequest, GetTaskRequest, DeleteTaskRequest],
94
+ context: grpc.ServicerContext,
95
+ *args,
96
+ **kwargs,
97
+ ):
98
+ if isinstance(request, CreateTaskRequest):
99
+ task_type = request.template.type
100
+ operation = create_operation
101
+ if request.inputs:
102
+ input_literal_size.labels(task_type=task_type).observe(request.inputs.ByteSize())
103
+ elif isinstance(request, GetTaskRequest):
104
+ task_type = request.task_category.name
105
+ operation = get_operation
106
+ elif isinstance(request, DeleteTaskRequest):
107
+ task_type = request.task_category.name
108
+ operation = delete_operation
109
+ else:
110
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
111
+ context.set_details("Method not implemented!")
112
+ return None
113
+
114
+ try:
115
+ with request_latency.labels(task_type=task_type, operation=operation).time():
116
+ res = await func(self, request, context, *args, **kwargs)
117
+ request_success_count.labels(task_type=task_type, operation=operation).inc()
118
+ return res
119
+ except Exception as e:
120
+ _handle_exception(e, context, task_type, operation)
121
+
122
+ return wrapper
123
+
124
+
125
+ def _get_connection_kwargs(request: Connection) -> Dict[str, str]:
126
+ kwargs = {}
127
+
128
+ for k, v in request.secrets.items():
129
+ kwargs[k] = v
130
+ for k, v in request.configs.items():
131
+ kwargs[k] = v
132
+
133
+ return kwargs
134
+
135
+
136
+ class AsyncConnectorService(AsyncConnectorServiceServicer):
137
+ @record_connector_metrics
138
+ async def CreateTask(self, request: CreateTaskRequest, context: grpc.ServicerContext) -> CreateTaskResponse:
139
+ template = request.template
140
+ connector = ConnectorRegistry.get_connector(template.type, template.task_type_version)
141
+ logger.info(f"{connector.name} start creating the job")
142
+ python_interface_inputs: Dict[str, Tuple[Type, Type[_has_default] | Type[inspect._empty]]] = {
143
+ name: (TypeEngine.guess_python_type(lt.type), inspect.Parameter.empty)
144
+ for name, lt in template.interface.inputs.variables.items()
145
+ }
146
+ native_interface = NativeInterface.from_types(inputs=python_interface_inputs, outputs={})
147
+ native_inputs = await convert_from_inputs_to_native(native_interface, Inputs(proto_inputs=request.inputs))
148
+ resource_meta = await connector.create(
149
+ task_template=request.template,
150
+ inputs=native_inputs,
151
+ output_prefix=request.output_prefix,
152
+ task_execution_metadata=request.task_execution_metadata,
153
+ connection=_get_connection_kwargs(request.connection),
154
+ )
155
+ return CreateTaskResponse(resource_meta=resource_meta.encode())
156
+
157
+ @record_connector_metrics
158
+ async def GetTask(self, request: GetTaskRequest, context: grpc.ServicerContext) -> GetTaskResponse:
159
+ connector = ConnectorRegistry.get_connector(request.task_category.name, request.task_category.version)
160
+ logger.info(f"{connector.name} start checking the status of the job")
161
+ res = await connector.get(
162
+ resource_meta=connector.metadata_type.decode(request.resource_meta),
163
+ connection=_get_connection_kwargs(request.connection),
164
+ )
165
+ return GetTaskResponse(resource=await get_resource_proto(res))
166
+
167
+ @record_connector_metrics
168
+ async def DeleteTask(self, request: DeleteTaskRequest, context: grpc.ServicerContext) -> DeleteTaskResponse:
169
+ connector = ConnectorRegistry.get_connector(request.task_category.name, request.task_category.version)
170
+ logger.info(f"{connector.name} start deleting the job")
171
+ await connector.delete(
172
+ resource_meta=connector.metadata_type.decode(request.resource_meta),
173
+ connection=_get_connection_kwargs(request.connection),
174
+ )
175
+ return DeleteTaskResponse()
176
+
177
+ async def GetTaskMetrics(
178
+ self, request: GetTaskMetricsRequest, context: grpc.ServicerContext
179
+ ) -> GetTaskMetricsResponse:
180
+ connector = ConnectorRegistry.get_connector(request.task_category.name, request.task_category.version)
181
+ logger.info(f"{connector.name} start getting metrics of the job")
182
+ return await connector.get_metrics(resource_meta=connector.metadata_type.decode(request.resource_meta))
183
+
184
+ async def GetTaskLogs(self, request: GetTaskLogsRequest, context: grpc.ServicerContext) -> GetTaskLogsResponse:
185
+ connector = ConnectorRegistry.get_connector(request.task_category.name, request.task_category.version)
186
+ logger.info(f"{connector.name} start getting logs of the job")
187
+ return await connector.get_logs(resource_meta=connector.metadata_type.decode(request.resource_meta))
188
+
189
+
190
+ class ConnectorMetadataService(ConnectorMetadataServiceServicer):
191
+ async def GetConnector(self, request: GetConnectorRequest, context: grpc.ServicerContext) -> GetConnectorResponse:
192
+ return GetConnectorResponse(connector=ConnectorRegistry._get_connector_metadata(request.name))
193
+
194
+ async def ListConnectors(
195
+ self, request: ListConnectorsRequest, context: grpc.ServicerContext
196
+ ) -> ListConnectorsResponse:
197
+ return ListConnectorsResponse(connectors=ConnectorRegistry._list_connectors())
@@ -0,0 +1,135 @@
1
+ import importlib
2
+ from concurrent import futures
3
+ from importlib.metadata import entry_points
4
+ from typing import List
5
+
6
+ import click
7
+ import grpc
8
+ from flyteidl2.core.execution_pb2 import TaskExecution
9
+ from flyteidl2.service import connector_pb2
10
+ from flyteidl2.service.connector_pb2_grpc import (
11
+ add_AsyncConnectorServiceServicer_to_server,
12
+ add_ConnectorMetadataServiceServicer_to_server,
13
+ )
14
+ from rich.console import Console
15
+ from rich.table import Table
16
+
17
+ from flyte import logger
18
+
19
+
20
+ def is_terminal_phase(phase: TaskExecution.Phase) -> bool:
21
+ """
22
+ Return true if the phase is terminal.
23
+ """
24
+ return phase in [TaskExecution.SUCCEEDED, TaskExecution.ABORTED, TaskExecution.FAILED]
25
+
26
+
27
+ def convert_to_flyte_phase(state: str) -> TaskExecution.Phase:
28
+ """
29
+ Convert the state from the connector to the phase in flyte.
30
+ """
31
+ state = state.lower()
32
+ if state in ["failed", "timeout", "timedout", "canceled", "cancelled", "skipped"]:
33
+ return TaskExecution.FAILED
34
+ if state in ["internal_error"]:
35
+ return TaskExecution.RETRYABLE_FAILED
36
+ elif state in ["done", "succeeded", "success", "completed"]:
37
+ return TaskExecution.SUCCEEDED
38
+ elif state in ["running", "terminating"]:
39
+ return TaskExecution.RUNNING
40
+ elif state in ["pending"]:
41
+ return TaskExecution.INITIALIZING
42
+ raise ValueError(f"Unrecognized state: {state}")
43
+
44
+
45
+ async def _start_grpc_server(
46
+ port: int, prometheus_port: int, worker: int, timeout: int | None, modules: List[str] | None
47
+ ):
48
+ try:
49
+ from flyte.connectors._server import (
50
+ AsyncConnectorService,
51
+ ConnectorMetadataService,
52
+ )
53
+ except ImportError as e:
54
+ raise ImportError(
55
+ "Flyte connector dependencies are not installed."
56
+ " Please install it using `pip install flyteplugins-connector`"
57
+ ) from e
58
+
59
+ click.secho("🚀 Starting the connector service...")
60
+ _load_connectors(modules)
61
+ _start_http_server(prometheus_port)
62
+
63
+ print_metadata()
64
+
65
+ server = grpc.aio.server(futures.ThreadPoolExecutor(max_workers=worker))
66
+
67
+ add_AsyncConnectorServiceServicer_to_server(AsyncConnectorService(), server)
68
+ add_ConnectorMetadataServiceServicer_to_server(ConnectorMetadataService(), server)
69
+ _start_health_check_server(server, worker)
70
+
71
+ server.add_insecure_port(f"[::]:{port}")
72
+ await server.start()
73
+ await server.wait_for_termination(timeout)
74
+
75
+
76
+ def _start_http_server(prometheus_port: int):
77
+ try:
78
+ from prometheus_client import start_http_server
79
+
80
+ click.secho("Starting up the server to expose the prometheus metrics...")
81
+ start_http_server(prometheus_port)
82
+ except ImportError as e:
83
+ click.secho(f"Failed to start the prometheus server with error {e}", fg="red")
84
+
85
+
86
+ def _start_health_check_server(server: grpc.Server, worker: int):
87
+ try:
88
+ from grpc_health.v1 import health, health_pb2, health_pb2_grpc
89
+
90
+ health_servicer = health.HealthServicer(
91
+ experimental_non_blocking=True,
92
+ experimental_thread_pool=futures.ThreadPoolExecutor(max_workers=worker),
93
+ )
94
+
95
+ for service in connector_pb2.DESCRIPTOR.services_by_name.values():
96
+ health_servicer.set(service.full_name, health_pb2.HealthCheckResponse.SERVING)
97
+ health_servicer.set(health.SERVICE_NAME, health_pb2.HealthCheckResponse.SERVING)
98
+
99
+ health_pb2_grpc.add_HealthServicer_to_server(health_servicer, server)
100
+
101
+ except ImportError as e:
102
+ click.secho(f"Failed to start the health check servicer with error {e}", fg="red")
103
+
104
+
105
+ def print_metadata():
106
+ from flyte.connectors import ConnectorRegistry
107
+
108
+ connectors = ConnectorRegistry._list_connectors()
109
+
110
+ table = Table(title="Connector Metadata")
111
+ table.add_column("Connector Name", style="cyan", no_wrap=True)
112
+ table.add_column("Support Task Types", style="cyan")
113
+
114
+ for connector in connectors:
115
+ categories = ""
116
+ for category in connector.supported_task_categories:
117
+ categories += f"{category.name} ({category.version}) "
118
+ table.add_row(connector.name, categories)
119
+
120
+ console = Console()
121
+ console.print(table)
122
+
123
+
124
+ def _load_connectors(modules: List[str] | None):
125
+ plugins = entry_points(group="flyte.connectors")
126
+ for ep in plugins:
127
+ try:
128
+ logger.info(f"Loading connector: {ep.name}")
129
+ ep.load()
130
+ except Exception as e:
131
+ logger.warning(f"Failed to load type transformer {ep.name} with error: {e}")
132
+
133
+ if modules:
134
+ for m in modules:
135
+ importlib.import_module(m)
flyte/errors.py CHANGED
@@ -132,7 +132,9 @@ class CustomError(RuntimeUserError):
132
132
  Create a CustomError from an exception. The exception's class name is used as the error code and the exception
133
133
  message is used as the error message.
134
134
  """
135
- return cls(e.__class__.__name__, str(e))
135
+ new_exc = cls(e.__class__.__name__, str(e))
136
+ new_exc.__cause__ = e
137
+ return new_exc
136
138
 
137
139
 
138
140
  class NotInTaskContextError(RuntimeUserError):
@@ -172,7 +174,7 @@ class RuntimeDataValidationError(RuntimeUserError):
172
174
 
173
175
  def __init__(self, var: str, e: Exception | str, task_name: str = ""):
174
176
  super().__init__(
175
- "DataValiationError", f"In task {task_name} variable {var}, failed to serialize/deserialize because of {e}"
177
+ "DataValidationError", f"In task {task_name} variable {var}, failed to serialize/deserialize because of {e}"
176
178
  )
177
179
 
178
180
 
@@ -221,3 +223,21 @@ class RunAbortedError(RuntimeUserError):
221
223
 
222
224
  def __init__(self, message: str):
223
225
  super().__init__("RunAbortedError", message, "user")
226
+
227
+
228
+ class SlowDownError(RuntimeUserError):
229
+ """
230
+ This error is raised when the user tries to access a resource that does not exist or is invalid.
231
+ """
232
+
233
+ def __init__(self, message: str):
234
+ super().__init__("SlowDownError", message, "user")
235
+
236
+
237
+ class OnlyAsyncIOSupportedError(RuntimeUserError):
238
+ """
239
+ This error is raised when the user tries to use sync IO in an async task.
240
+ """
241
+
242
+ def __init__(self, message: str):
243
+ super().__init__("OnlyAsyncIOSupportedError", message, "user")
flyte/extend.py CHANGED
@@ -1,12 +1,19 @@
1
1
  from ._initialize import is_initialized
2
+ from ._internal.imagebuild.image_builder import ImageBuildEngine
3
+ from ._internal.runtime.entrypoints import download_code_bundle
4
+ from ._internal.runtime.resources_serde import get_proto_resources
2
5
  from ._resources import PRIMARY_CONTAINER_DEFAULT_NAME, pod_spec_from_resources
3
- from ._task import AsyncFunctionTaskTemplate
6
+ from ._task import AsyncFunctionTaskTemplate, TaskTemplate
4
7
  from ._task_plugins import TaskPluginRegistry
5
8
 
6
9
  __all__ = [
7
10
  "PRIMARY_CONTAINER_DEFAULT_NAME",
8
11
  "AsyncFunctionTaskTemplate",
12
+ "ImageBuildEngine",
9
13
  "TaskPluginRegistry",
14
+ "TaskTemplate",
15
+ "download_code_bundle",
16
+ "get_proto_resources",
10
17
  "is_initialized",
11
18
  "pod_spec_from_resources",
12
19
  ]
@@ -2,7 +2,7 @@ import os
2
2
  import pathlib
3
3
  from typing import Any, Dict, List, Literal, Optional, Tuple, Type, Union
4
4
 
5
- from flyteidl.core import tasks_pb2
5
+ from flyteidl2.core import tasks_pb2
6
6
 
7
7
  from flyte import Image, storage
8
8
  from flyte._logging import logger
@@ -83,6 +83,11 @@ class ContainerTask(TaskTemplate):
83
83
  self._image = Image.from_debian_base()
84
84
  else:
85
85
  self._image = Image.from_base(image)
86
+
87
+ if command and any(not isinstance(c, str) for c in command):
88
+ raise ValueError("All elements in the command list must be strings.")
89
+ if arguments and any(not isinstance(a, str) for a in arguments):
90
+ raise ValueError("All elements in the arguments list must be strings.")
86
91
  self._cmd = command
87
92
  self._args = arguments
88
93
  self._input_data_dir = input_data_dir
flyte/git/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ from ._config import config_from_root
2
+
3
+ __all__ = ["config_from_root"]
flyte/git/_config.py ADDED
@@ -0,0 +1,21 @@
1
+ import pathlib
2
+ import subprocess
3
+
4
+ import flyte.config
5
+
6
+
7
+ def config_from_root(path: pathlib.Path | str = ".flyte/config.yaml") -> flyte.config.Config | None:
8
+ """Get the config file from the git root directory.
9
+
10
+ By default, the config file is expected to be in `.flyte/config.yaml` in the git root directory.
11
+ """
12
+ try:
13
+ result = subprocess.run(["git", "rev-parse", "--show-toplevel"], check=False, capture_output=True, text=True)
14
+ if result.returncode != 0:
15
+ return None
16
+ root = pathlib.Path(result.stdout.strip())
17
+ if not (root / path).exists():
18
+ return None
19
+ return flyte.config.auto(root / path)
20
+ except Exception:
21
+ return None
flyte/io/__init__.py CHANGED
@@ -7,6 +7,7 @@ of large datasets in Union.
7
7
  """
8
8
 
9
9
  __all__ = [
10
+ "PARQUET",
10
11
  "DataFrame",
11
12
  "DataFrameDecoder",
12
13
  "DataFrameEncoder",
@@ -17,6 +18,7 @@ __all__ = [
17
18
  ]
18
19
 
19
20
  from ._dataframe import (
21
+ PARQUET,
20
22
  DataFrame,
21
23
  DataFrameDecoder,
22
24
  DataFrameEncoder,
@@ -18,6 +18,7 @@ from flyte._logging import logger
18
18
  from flyte._utils.lazy_module import is_imported
19
19
 
20
20
  from .dataframe import (
21
+ PARQUET,
21
22
  DataFrame,
22
23
  DataFrameDecoder,
23
24
  DataFrameEncoder,
@@ -121,6 +122,7 @@ def lazy_import_dataframe_handler():
121
122
 
122
123
 
123
124
  __all__ = [
125
+ "PARQUET",
124
126
  "DataFrame",
125
127
  "DataFrameDecoder",
126
128
  "DataFrameEncoder",
@@ -3,7 +3,7 @@ import typing
3
3
  from pathlib import Path
4
4
  from typing import TypeVar
5
5
 
6
- from flyteidl.core import literals_pb2, types_pb2
6
+ from flyteidl2.core import literals_pb2, types_pb2
7
7
  from fsspec.core import split_protocol, strip_protocol
8
8
 
9
9
  import flyte.storage as storage
@@ -58,16 +58,16 @@ class PandasToCSVEncodingHandler(DataFrameEncoder):
58
58
 
59
59
  if not storage.is_remote(uri):
60
60
  Path(uri).mkdir(parents=True, exist_ok=True)
61
- path = os.path.join(uri, ".csv")
61
+ csv_file = storage.join(uri, "data.csv")
62
62
  df = typing.cast(pd.DataFrame, dataframe.val)
63
63
  df.to_csv(
64
- path,
64
+ csv_file,
65
65
  index=False,
66
- storage_options=get_pandas_storage_options(uri=path),
66
+ storage_options=get_pandas_storage_options(uri=csv_file),
67
67
  )
68
68
  structured_dataset_type.format = CSV
69
69
  return literals_pb2.StructuredDataset(
70
- uri=uri, metadata=literals_pb2.StructuredDatasetMetadata(structured_dataset_type)
70
+ uri=uri, metadata=literals_pb2.StructuredDatasetMetadata(structured_dataset_type=structured_dataset_type)
71
71
  )
72
72
 
73
73
 
@@ -83,16 +83,25 @@ class CSVToPandasDecodingHandler(DataFrameDecoder):
83
83
  uri = proto_value.uri
84
84
  columns = None
85
85
  kwargs = get_pandas_storage_options(uri=uri)
86
- path = os.path.join(uri, ".csv")
86
+ csv_file = storage.join(uri, "data.csv")
87
87
  if current_task_metadata.structured_dataset_type and current_task_metadata.structured_dataset_type.columns:
88
88
  columns = [c.name for c in current_task_metadata.structured_dataset_type.columns]
89
89
  try:
90
- return pd.read_csv(path, usecols=columns, storage_options=kwargs)
90
+ import io
91
+
92
+ # The pattern used here is a bit wonky because of obstore issues with csv, getting early eof error.
93
+ buf = io.BytesIO()
94
+ async for chunk in storage.get_stream(csv_file):
95
+ buf.write(chunk)
96
+ buf.seek(0)
97
+ df = pd.read_csv(buf)
98
+ return df
99
+
91
100
  except Exception as exc:
92
101
  if exc.__class__.__name__ == "NoCredentialsError":
93
102
  logger.debug("S3 source detected, attempting anonymous S3 access")
94
103
  kwargs = get_pandas_storage_options(uri=uri, anonymous=True)
95
- return pd.read_csv(path, usecols=columns, storage_options=kwargs)
104
+ return pd.read_csv(csv_file, usecols=columns, storage_options=kwargs)
96
105
  else:
97
106
  raise
98
107