flyte 0.0.1b0__py3-none-any.whl → 2.0.0b46__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 (455) hide show
  1. flyte/__init__.py +83 -30
  2. flyte/_bin/connect.py +61 -0
  3. flyte/_bin/debug.py +38 -0
  4. flyte/_bin/runtime.py +87 -19
  5. flyte/_bin/serve.py +351 -0
  6. flyte/_build.py +3 -2
  7. flyte/_cache/cache.py +6 -5
  8. flyte/_cache/local_cache.py +216 -0
  9. flyte/_code_bundle/_ignore.py +31 -5
  10. flyte/_code_bundle/_packaging.py +42 -11
  11. flyte/_code_bundle/_utils.py +57 -34
  12. flyte/_code_bundle/bundle.py +130 -27
  13. flyte/_constants.py +1 -0
  14. flyte/_context.py +21 -5
  15. flyte/_custom_context.py +73 -0
  16. flyte/_debug/constants.py +37 -0
  17. flyte/_debug/utils.py +17 -0
  18. flyte/_debug/vscode.py +315 -0
  19. flyte/_deploy.py +396 -75
  20. flyte/_deployer.py +109 -0
  21. flyte/_environment.py +94 -11
  22. flyte/_excepthook.py +37 -0
  23. flyte/_group.py +2 -1
  24. flyte/_hash.py +1 -16
  25. flyte/_image.py +544 -234
  26. flyte/_initialize.py +443 -294
  27. flyte/_interface.py +40 -5
  28. flyte/_internal/controllers/__init__.py +22 -8
  29. flyte/_internal/controllers/_local_controller.py +159 -35
  30. flyte/_internal/controllers/_trace.py +18 -10
  31. flyte/_internal/controllers/remote/__init__.py +38 -9
  32. flyte/_internal/controllers/remote/_action.py +82 -12
  33. flyte/_internal/controllers/remote/_client.py +6 -2
  34. flyte/_internal/controllers/remote/_controller.py +290 -64
  35. flyte/_internal/controllers/remote/_core.py +155 -95
  36. flyte/_internal/controllers/remote/_informer.py +40 -20
  37. flyte/_internal/controllers/remote/_service_protocol.py +2 -2
  38. flyte/_internal/imagebuild/__init__.py +2 -10
  39. flyte/_internal/imagebuild/docker_builder.py +391 -84
  40. flyte/_internal/imagebuild/image_builder.py +111 -55
  41. flyte/_internal/imagebuild/remote_builder.py +409 -0
  42. flyte/_internal/imagebuild/utils.py +79 -0
  43. flyte/_internal/resolvers/_app_env_module.py +92 -0
  44. flyte/_internal/resolvers/_task_module.py +5 -38
  45. flyte/_internal/resolvers/app_env.py +26 -0
  46. flyte/_internal/resolvers/common.py +8 -1
  47. flyte/_internal/resolvers/default.py +2 -2
  48. flyte/_internal/runtime/convert.py +322 -33
  49. flyte/_internal/runtime/entrypoints.py +106 -18
  50. flyte/_internal/runtime/io.py +71 -23
  51. flyte/_internal/runtime/resources_serde.py +21 -7
  52. flyte/_internal/runtime/reuse.py +125 -0
  53. flyte/_internal/runtime/rusty.py +196 -0
  54. flyte/_internal/runtime/task_serde.py +239 -66
  55. flyte/_internal/runtime/taskrunner.py +48 -8
  56. flyte/_internal/runtime/trigger_serde.py +162 -0
  57. flyte/_internal/runtime/types_serde.py +7 -16
  58. flyte/_keyring/file.py +115 -0
  59. flyte/_link.py +30 -0
  60. flyte/_logging.py +241 -42
  61. flyte/_map.py +312 -0
  62. flyte/_metrics.py +59 -0
  63. flyte/_module.py +74 -0
  64. flyte/_pod.py +30 -0
  65. flyte/_resources.py +296 -33
  66. flyte/_retry.py +1 -7
  67. flyte/_reusable_environment.py +72 -7
  68. flyte/_run.py +461 -132
  69. flyte/_secret.py +47 -11
  70. flyte/_serve.py +333 -0
  71. flyte/_task.py +245 -56
  72. flyte/_task_environment.py +219 -97
  73. flyte/_task_plugins.py +47 -0
  74. flyte/_tools.py +8 -8
  75. flyte/_trace.py +15 -24
  76. flyte/_trigger.py +1027 -0
  77. flyte/_utils/__init__.py +12 -1
  78. flyte/_utils/asyn.py +3 -1
  79. flyte/_utils/async_cache.py +139 -0
  80. flyte/_utils/coro_management.py +5 -4
  81. flyte/_utils/description_parser.py +19 -0
  82. flyte/_utils/docker_credentials.py +173 -0
  83. flyte/_utils/helpers.py +45 -19
  84. flyte/_utils/module_loader.py +123 -0
  85. flyte/_utils/org_discovery.py +57 -0
  86. flyte/_utils/uv_script_parser.py +8 -1
  87. flyte/_version.py +16 -3
  88. flyte/app/__init__.py +27 -0
  89. flyte/app/_app_environment.py +362 -0
  90. flyte/app/_connector_environment.py +40 -0
  91. flyte/app/_deploy.py +130 -0
  92. flyte/app/_parameter.py +343 -0
  93. flyte/app/_runtime/__init__.py +3 -0
  94. flyte/app/_runtime/app_serde.py +383 -0
  95. flyte/app/_types.py +113 -0
  96. flyte/app/extras/__init__.py +9 -0
  97. flyte/app/extras/_auth_middleware.py +217 -0
  98. flyte/app/extras/_fastapi.py +93 -0
  99. flyte/app/extras/_model_loader/__init__.py +3 -0
  100. flyte/app/extras/_model_loader/config.py +7 -0
  101. flyte/app/extras/_model_loader/loader.py +288 -0
  102. flyte/cli/__init__.py +12 -0
  103. flyte/cli/_abort.py +28 -0
  104. flyte/cli/_build.py +114 -0
  105. flyte/cli/_common.py +493 -0
  106. flyte/cli/_create.py +371 -0
  107. flyte/cli/_delete.py +45 -0
  108. flyte/cli/_deploy.py +401 -0
  109. flyte/cli/_gen.py +316 -0
  110. flyte/cli/_get.py +446 -0
  111. flyte/cli/_option.py +33 -0
  112. {union/_cli → flyte/cli}/_params.py +152 -153
  113. flyte/cli/_plugins.py +209 -0
  114. flyte/cli/_prefetch.py +292 -0
  115. flyte/cli/_run.py +690 -0
  116. flyte/cli/_serve.py +338 -0
  117. flyte/cli/_update.py +86 -0
  118. flyte/cli/_user.py +20 -0
  119. flyte/cli/main.py +246 -0
  120. flyte/config/__init__.py +3 -0
  121. flyte/config/_config.py +248 -0
  122. flyte/config/_internal.py +73 -0
  123. flyte/config/_reader.py +225 -0
  124. flyte/connectors/__init__.py +11 -0
  125. flyte/connectors/_connector.py +330 -0
  126. flyte/connectors/_server.py +194 -0
  127. flyte/connectors/utils.py +159 -0
  128. flyte/errors.py +134 -2
  129. flyte/extend.py +24 -0
  130. flyte/extras/_container.py +69 -56
  131. flyte/git/__init__.py +3 -0
  132. flyte/git/_config.py +279 -0
  133. flyte/io/__init__.py +8 -1
  134. flyte/io/{structured_dataset → _dataframe}/__init__.py +32 -30
  135. flyte/io/{structured_dataset → _dataframe}/basic_dfs.py +75 -68
  136. flyte/io/{structured_dataset/structured_dataset.py → _dataframe/dataframe.py} +207 -242
  137. flyte/io/_dir.py +575 -113
  138. flyte/io/_file.py +587 -141
  139. flyte/io/_hashing_io.py +342 -0
  140. flyte/io/extend.py +7 -0
  141. flyte/models.py +635 -0
  142. flyte/prefetch/__init__.py +22 -0
  143. flyte/prefetch/_hf_model.py +563 -0
  144. flyte/remote/__init__.py +14 -3
  145. flyte/remote/_action.py +879 -0
  146. flyte/remote/_app.py +346 -0
  147. flyte/remote/_auth_metadata.py +42 -0
  148. flyte/remote/_client/_protocols.py +62 -4
  149. flyte/remote/_client/auth/_auth_utils.py +19 -0
  150. flyte/remote/_client/auth/_authenticators/base.py +8 -2
  151. flyte/remote/_client/auth/_authenticators/device_code.py +4 -5
  152. flyte/remote/_client/auth/_authenticators/factory.py +4 -0
  153. flyte/remote/_client/auth/_authenticators/passthrough.py +79 -0
  154. flyte/remote/_client/auth/_authenticators/pkce.py +17 -18
  155. flyte/remote/_client/auth/_channel.py +47 -18
  156. flyte/remote/_client/auth/_client_config.py +5 -3
  157. flyte/remote/_client/auth/_keyring.py +15 -2
  158. flyte/remote/_client/auth/_token_client.py +3 -3
  159. flyte/remote/_client/controlplane.py +206 -18
  160. flyte/remote/_common.py +66 -0
  161. flyte/remote/_data.py +107 -22
  162. flyte/remote/_logs.py +116 -33
  163. flyte/remote/_project.py +21 -19
  164. flyte/remote/_run.py +164 -631
  165. flyte/remote/_secret.py +72 -29
  166. flyte/remote/_task.py +387 -46
  167. flyte/remote/_trigger.py +368 -0
  168. flyte/remote/_user.py +43 -0
  169. flyte/report/_report.py +10 -6
  170. flyte/storage/__init__.py +13 -1
  171. flyte/storage/_config.py +237 -0
  172. flyte/storage/_parallel_reader.py +289 -0
  173. flyte/storage/_storage.py +268 -59
  174. flyte/syncify/__init__.py +56 -0
  175. flyte/syncify/_api.py +414 -0
  176. flyte/types/__init__.py +39 -0
  177. flyte/types/_interface.py +22 -7
  178. flyte/{io/pickle/transformer.py → types/_pickle.py} +37 -9
  179. flyte/types/_string_literals.py +8 -9
  180. flyte/types/_type_engine.py +230 -129
  181. flyte/types/_utils.py +1 -1
  182. flyte-2.0.0b46.data/scripts/debug.py +38 -0
  183. flyte-2.0.0b46.data/scripts/runtime.py +194 -0
  184. flyte-2.0.0b46.dist-info/METADATA +352 -0
  185. flyte-2.0.0b46.dist-info/RECORD +221 -0
  186. flyte-2.0.0b46.dist-info/entry_points.txt +8 -0
  187. flyte-2.0.0b46.dist-info/licenses/LICENSE +201 -0
  188. flyte/_api_commons.py +0 -3
  189. flyte/_cli/_common.py +0 -287
  190. flyte/_cli/_create.py +0 -42
  191. flyte/_cli/_delete.py +0 -23
  192. flyte/_cli/_deploy.py +0 -140
  193. flyte/_cli/_get.py +0 -235
  194. flyte/_cli/_run.py +0 -152
  195. flyte/_cli/main.py +0 -72
  196. flyte/_datastructures.py +0 -342
  197. flyte/_internal/controllers/pbhash.py +0 -39
  198. flyte/_protos/common/authorization_pb2.py +0 -66
  199. flyte/_protos/common/authorization_pb2.pyi +0 -108
  200. flyte/_protos/common/authorization_pb2_grpc.py +0 -4
  201. flyte/_protos/common/identifier_pb2.py +0 -71
  202. flyte/_protos/common/identifier_pb2.pyi +0 -82
  203. flyte/_protos/common/identifier_pb2_grpc.py +0 -4
  204. flyte/_protos/common/identity_pb2.py +0 -48
  205. flyte/_protos/common/identity_pb2.pyi +0 -72
  206. flyte/_protos/common/identity_pb2_grpc.py +0 -4
  207. flyte/_protos/common/list_pb2.py +0 -36
  208. flyte/_protos/common/list_pb2.pyi +0 -69
  209. flyte/_protos/common/list_pb2_grpc.py +0 -4
  210. flyte/_protos/common/policy_pb2.py +0 -37
  211. flyte/_protos/common/policy_pb2.pyi +0 -27
  212. flyte/_protos/common/policy_pb2_grpc.py +0 -4
  213. flyte/_protos/common/role_pb2.py +0 -37
  214. flyte/_protos/common/role_pb2.pyi +0 -53
  215. flyte/_protos/common/role_pb2_grpc.py +0 -4
  216. flyte/_protos/common/runtime_version_pb2.py +0 -28
  217. flyte/_protos/common/runtime_version_pb2.pyi +0 -24
  218. flyte/_protos/common/runtime_version_pb2_grpc.py +0 -4
  219. flyte/_protos/logs/dataplane/payload_pb2.py +0 -96
  220. flyte/_protos/logs/dataplane/payload_pb2.pyi +0 -168
  221. flyte/_protos/logs/dataplane/payload_pb2_grpc.py +0 -4
  222. flyte/_protos/secret/definition_pb2.py +0 -49
  223. flyte/_protos/secret/definition_pb2.pyi +0 -93
  224. flyte/_protos/secret/definition_pb2_grpc.py +0 -4
  225. flyte/_protos/secret/payload_pb2.py +0 -62
  226. flyte/_protos/secret/payload_pb2.pyi +0 -94
  227. flyte/_protos/secret/payload_pb2_grpc.py +0 -4
  228. flyte/_protos/secret/secret_pb2.py +0 -38
  229. flyte/_protos/secret/secret_pb2.pyi +0 -6
  230. flyte/_protos/secret/secret_pb2_grpc.py +0 -198
  231. flyte/_protos/secret/secret_pb2_grpc_grpc.py +0 -198
  232. flyte/_protos/validate/validate/validate_pb2.py +0 -76
  233. flyte/_protos/workflow/node_execution_service_pb2.py +0 -26
  234. flyte/_protos/workflow/node_execution_service_pb2.pyi +0 -4
  235. flyte/_protos/workflow/node_execution_service_pb2_grpc.py +0 -32
  236. flyte/_protos/workflow/queue_service_pb2.py +0 -106
  237. flyte/_protos/workflow/queue_service_pb2.pyi +0 -141
  238. flyte/_protos/workflow/queue_service_pb2_grpc.py +0 -172
  239. flyte/_protos/workflow/run_definition_pb2.py +0 -128
  240. flyte/_protos/workflow/run_definition_pb2.pyi +0 -310
  241. flyte/_protos/workflow/run_definition_pb2_grpc.py +0 -4
  242. flyte/_protos/workflow/run_logs_service_pb2.py +0 -41
  243. flyte/_protos/workflow/run_logs_service_pb2.pyi +0 -28
  244. flyte/_protos/workflow/run_logs_service_pb2_grpc.py +0 -69
  245. flyte/_protos/workflow/run_service_pb2.py +0 -133
  246. flyte/_protos/workflow/run_service_pb2.pyi +0 -175
  247. flyte/_protos/workflow/run_service_pb2_grpc.py +0 -412
  248. flyte/_protos/workflow/state_service_pb2.py +0 -58
  249. flyte/_protos/workflow/state_service_pb2.pyi +0 -71
  250. flyte/_protos/workflow/state_service_pb2_grpc.py +0 -138
  251. flyte/_protos/workflow/task_definition_pb2.py +0 -72
  252. flyte/_protos/workflow/task_definition_pb2.pyi +0 -65
  253. flyte/_protos/workflow/task_definition_pb2_grpc.py +0 -4
  254. flyte/_protos/workflow/task_service_pb2.py +0 -44
  255. flyte/_protos/workflow/task_service_pb2.pyi +0 -31
  256. flyte/_protos/workflow/task_service_pb2_grpc.py +0 -104
  257. flyte/io/_dataframe.py +0 -0
  258. flyte/io/pickle/__init__.py +0 -0
  259. flyte/remote/_console.py +0 -18
  260. flyte-0.0.1b0.dist-info/METADATA +0 -179
  261. flyte-0.0.1b0.dist-info/RECORD +0 -390
  262. flyte-0.0.1b0.dist-info/entry_points.txt +0 -3
  263. union/__init__.py +0 -54
  264. union/_api_commons.py +0 -3
  265. union/_bin/__init__.py +0 -0
  266. union/_bin/runtime.py +0 -113
  267. union/_build.py +0 -25
  268. union/_cache/__init__.py +0 -12
  269. union/_cache/cache.py +0 -141
  270. union/_cache/defaults.py +0 -9
  271. union/_cache/policy_function_body.py +0 -42
  272. union/_cli/__init__.py +0 -0
  273. union/_cli/_common.py +0 -263
  274. union/_cli/_create.py +0 -40
  275. union/_cli/_delete.py +0 -23
  276. union/_cli/_deploy.py +0 -120
  277. union/_cli/_get.py +0 -162
  278. union/_cli/_run.py +0 -150
  279. union/_cli/main.py +0 -72
  280. union/_code_bundle/__init__.py +0 -8
  281. union/_code_bundle/_ignore.py +0 -113
  282. union/_code_bundle/_packaging.py +0 -187
  283. union/_code_bundle/_utils.py +0 -342
  284. union/_code_bundle/bundle.py +0 -176
  285. union/_context.py +0 -146
  286. union/_datastructures.py +0 -295
  287. union/_deploy.py +0 -185
  288. union/_doc.py +0 -29
  289. union/_docstring.py +0 -26
  290. union/_environment.py +0 -43
  291. union/_group.py +0 -31
  292. union/_hash.py +0 -23
  293. union/_image.py +0 -760
  294. union/_initialize.py +0 -585
  295. union/_interface.py +0 -84
  296. union/_internal/__init__.py +0 -3
  297. union/_internal/controllers/__init__.py +0 -77
  298. union/_internal/controllers/_local_controller.py +0 -77
  299. union/_internal/controllers/pbhash.py +0 -39
  300. union/_internal/controllers/remote/__init__.py +0 -40
  301. union/_internal/controllers/remote/_action.py +0 -131
  302. union/_internal/controllers/remote/_client.py +0 -43
  303. union/_internal/controllers/remote/_controller.py +0 -169
  304. union/_internal/controllers/remote/_core.py +0 -341
  305. union/_internal/controllers/remote/_informer.py +0 -260
  306. union/_internal/controllers/remote/_service_protocol.py +0 -44
  307. union/_internal/imagebuild/__init__.py +0 -11
  308. union/_internal/imagebuild/docker_builder.py +0 -416
  309. union/_internal/imagebuild/image_builder.py +0 -243
  310. union/_internal/imagebuild/remote_builder.py +0 -0
  311. union/_internal/resolvers/__init__.py +0 -0
  312. union/_internal/resolvers/_task_module.py +0 -31
  313. union/_internal/resolvers/common.py +0 -24
  314. union/_internal/resolvers/default.py +0 -27
  315. union/_internal/runtime/__init__.py +0 -0
  316. union/_internal/runtime/convert.py +0 -163
  317. union/_internal/runtime/entrypoints.py +0 -121
  318. union/_internal/runtime/io.py +0 -136
  319. union/_internal/runtime/resources_serde.py +0 -134
  320. union/_internal/runtime/task_serde.py +0 -202
  321. union/_internal/runtime/taskrunner.py +0 -179
  322. union/_internal/runtime/types_serde.py +0 -53
  323. union/_logging.py +0 -124
  324. union/_protos/__init__.py +0 -0
  325. union/_protos/common/authorization_pb2.py +0 -66
  326. union/_protos/common/authorization_pb2.pyi +0 -106
  327. union/_protos/common/authorization_pb2_grpc.py +0 -4
  328. union/_protos/common/identifier_pb2.py +0 -71
  329. union/_protos/common/identifier_pb2.pyi +0 -82
  330. union/_protos/common/identifier_pb2_grpc.py +0 -4
  331. union/_protos/common/identity_pb2.py +0 -48
  332. union/_protos/common/identity_pb2.pyi +0 -72
  333. union/_protos/common/identity_pb2_grpc.py +0 -4
  334. union/_protos/common/list_pb2.py +0 -36
  335. union/_protos/common/list_pb2.pyi +0 -69
  336. union/_protos/common/list_pb2_grpc.py +0 -4
  337. union/_protos/common/policy_pb2.py +0 -37
  338. union/_protos/common/policy_pb2.pyi +0 -27
  339. union/_protos/common/policy_pb2_grpc.py +0 -4
  340. union/_protos/common/role_pb2.py +0 -37
  341. union/_protos/common/role_pb2.pyi +0 -51
  342. union/_protos/common/role_pb2_grpc.py +0 -4
  343. union/_protos/common/runtime_version_pb2.py +0 -28
  344. union/_protos/common/runtime_version_pb2.pyi +0 -24
  345. union/_protos/common/runtime_version_pb2_grpc.py +0 -4
  346. union/_protos/logs/dataplane/payload_pb2.py +0 -96
  347. union/_protos/logs/dataplane/payload_pb2.pyi +0 -168
  348. union/_protos/logs/dataplane/payload_pb2_grpc.py +0 -4
  349. union/_protos/secret/definition_pb2.py +0 -49
  350. union/_protos/secret/definition_pb2.pyi +0 -93
  351. union/_protos/secret/definition_pb2_grpc.py +0 -4
  352. union/_protos/secret/payload_pb2.py +0 -62
  353. union/_protos/secret/payload_pb2.pyi +0 -94
  354. union/_protos/secret/payload_pb2_grpc.py +0 -4
  355. union/_protos/secret/secret_pb2.py +0 -38
  356. union/_protos/secret/secret_pb2.pyi +0 -6
  357. union/_protos/secret/secret_pb2_grpc.py +0 -198
  358. union/_protos/validate/validate/validate_pb2.py +0 -76
  359. union/_protos/workflow/node_execution_service_pb2.py +0 -26
  360. union/_protos/workflow/node_execution_service_pb2.pyi +0 -4
  361. union/_protos/workflow/node_execution_service_pb2_grpc.py +0 -32
  362. union/_protos/workflow/queue_service_pb2.py +0 -75
  363. union/_protos/workflow/queue_service_pb2.pyi +0 -103
  364. union/_protos/workflow/queue_service_pb2_grpc.py +0 -172
  365. union/_protos/workflow/run_definition_pb2.py +0 -100
  366. union/_protos/workflow/run_definition_pb2.pyi +0 -256
  367. union/_protos/workflow/run_definition_pb2_grpc.py +0 -4
  368. union/_protos/workflow/run_logs_service_pb2.py +0 -41
  369. union/_protos/workflow/run_logs_service_pb2.pyi +0 -28
  370. union/_protos/workflow/run_logs_service_pb2_grpc.py +0 -69
  371. union/_protos/workflow/run_service_pb2.py +0 -133
  372. union/_protos/workflow/run_service_pb2.pyi +0 -173
  373. union/_protos/workflow/run_service_pb2_grpc.py +0 -412
  374. union/_protos/workflow/state_service_pb2.py +0 -58
  375. union/_protos/workflow/state_service_pb2.pyi +0 -69
  376. union/_protos/workflow/state_service_pb2_grpc.py +0 -138
  377. union/_protos/workflow/task_definition_pb2.py +0 -72
  378. union/_protos/workflow/task_definition_pb2.pyi +0 -65
  379. union/_protos/workflow/task_definition_pb2_grpc.py +0 -4
  380. union/_protos/workflow/task_service_pb2.py +0 -44
  381. union/_protos/workflow/task_service_pb2.pyi +0 -31
  382. union/_protos/workflow/task_service_pb2_grpc.py +0 -104
  383. union/_resources.py +0 -226
  384. union/_retry.py +0 -32
  385. union/_reusable_environment.py +0 -25
  386. union/_run.py +0 -374
  387. union/_secret.py +0 -61
  388. union/_task.py +0 -354
  389. union/_task_environment.py +0 -186
  390. union/_timeout.py +0 -47
  391. union/_tools.py +0 -27
  392. union/_utils/__init__.py +0 -11
  393. union/_utils/asyn.py +0 -119
  394. union/_utils/file_handling.py +0 -71
  395. union/_utils/helpers.py +0 -46
  396. union/_utils/lazy_module.py +0 -54
  397. union/_utils/uv_script_parser.py +0 -49
  398. union/_version.py +0 -21
  399. union/connectors/__init__.py +0 -0
  400. union/errors.py +0 -128
  401. union/extras/__init__.py +0 -5
  402. union/extras/_container.py +0 -263
  403. union/io/__init__.py +0 -11
  404. union/io/_dataframe.py +0 -0
  405. union/io/_dir.py +0 -425
  406. union/io/_file.py +0 -418
  407. union/io/pickle/__init__.py +0 -0
  408. union/io/pickle/transformer.py +0 -117
  409. union/io/structured_dataset/__init__.py +0 -122
  410. union/io/structured_dataset/basic_dfs.py +0 -219
  411. union/io/structured_dataset/structured_dataset.py +0 -1057
  412. union/py.typed +0 -0
  413. union/remote/__init__.py +0 -23
  414. union/remote/_client/__init__.py +0 -0
  415. union/remote/_client/_protocols.py +0 -129
  416. union/remote/_client/auth/__init__.py +0 -12
  417. union/remote/_client/auth/_authenticators/__init__.py +0 -0
  418. union/remote/_client/auth/_authenticators/base.py +0 -391
  419. union/remote/_client/auth/_authenticators/client_credentials.py +0 -73
  420. union/remote/_client/auth/_authenticators/device_code.py +0 -120
  421. union/remote/_client/auth/_authenticators/external_command.py +0 -77
  422. union/remote/_client/auth/_authenticators/factory.py +0 -200
  423. union/remote/_client/auth/_authenticators/pkce.py +0 -515
  424. union/remote/_client/auth/_channel.py +0 -184
  425. union/remote/_client/auth/_client_config.py +0 -83
  426. union/remote/_client/auth/_default_html.py +0 -32
  427. union/remote/_client/auth/_grpc_utils/__init__.py +0 -0
  428. union/remote/_client/auth/_grpc_utils/auth_interceptor.py +0 -204
  429. union/remote/_client/auth/_grpc_utils/default_metadata_interceptor.py +0 -144
  430. union/remote/_client/auth/_keyring.py +0 -154
  431. union/remote/_client/auth/_token_client.py +0 -258
  432. union/remote/_client/auth/errors.py +0 -16
  433. union/remote/_client/controlplane.py +0 -86
  434. union/remote/_data.py +0 -149
  435. union/remote/_logs.py +0 -74
  436. union/remote/_project.py +0 -86
  437. union/remote/_run.py +0 -820
  438. union/remote/_secret.py +0 -132
  439. union/remote/_task.py +0 -193
  440. union/report/__init__.py +0 -3
  441. union/report/_report.py +0 -178
  442. union/report/_template.html +0 -124
  443. union/storage/__init__.py +0 -24
  444. union/storage/_remote_fs.py +0 -34
  445. union/storage/_storage.py +0 -247
  446. union/storage/_utils.py +0 -5
  447. union/types/__init__.py +0 -11
  448. union/types/_renderer.py +0 -162
  449. union/types/_string_literals.py +0 -120
  450. union/types/_type_engine.py +0 -2131
  451. union/types/_utils.py +0 -80
  452. /flyte/{_cli → _debug}/__init__.py +0 -0
  453. /flyte/{_protos → _keyring}/__init__.py +0 -0
  454. {flyte-0.0.1b0.dist-info → flyte-2.0.0b46.dist-info}/WHEEL +0 -0
  455. {flyte-0.0.1b0.dist-info → flyte-2.0.0b46.dist-info}/top_level.txt +0 -0
flyte/_deploy.py CHANGED
@@ -1,24 +1,29 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import asyncio
4
+ import hashlib
4
5
  from dataclasses import dataclass
5
- from typing import TYPE_CHECKING, Dict, List, Optional, Tuple
6
+ from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple
6
7
 
8
+ import cloudpickle
7
9
  import rich.repr
8
10
 
9
- from ._api_commons import syncer
10
- from ._datastructures import SerializationContext
11
+ from flyte.models import NativeInterface, SerializationContext
12
+ from flyte.syncify import syncify
13
+
11
14
  from ._environment import Environment
12
15
  from ._image import Image
13
- from ._initialize import get_client, get_common_config, requires_client, requires_initialization
16
+ from ._initialize import ensure_client, get_client, get_init_config, requires_initialization
14
17
  from ._logging import logger
15
18
  from ._task import TaskTemplate
16
19
  from ._task_environment import TaskEnvironment
17
20
 
18
21
  if TYPE_CHECKING:
19
- from flyte._protos.workflow import task_definition_pb2
22
+ from flyteidl2.core import interface_pb2
23
+ from flyteidl2.task import task_definition_pb2
20
24
 
21
25
  from ._code_bundle import CopyFiles
26
+ from ._deployer import DeployedEnvironment, DeploymentContext
22
27
  from ._internal.imagebuild.image_builder import ImageCache
23
28
 
24
29
 
@@ -31,51 +36,289 @@ class DeploymentPlan:
31
36
 
32
37
  @rich.repr.auto
33
38
  @dataclass
39
+ class DeployedTask:
40
+ deployed_task: task_definition_pb2.TaskSpec
41
+ deployed_triggers: List[task_definition_pb2.TaskTrigger]
42
+
43
+ def get_name(self) -> str:
44
+ """
45
+ Returns the name of the deployed environment.
46
+ Returns:
47
+ """
48
+ return self.deployed_task.task_template.id.name
49
+
50
+ def summary_repr(self) -> str:
51
+ """
52
+ Returns a summary representation of the deployed task.
53
+ """
54
+ return (
55
+ f"DeployedTask(name={self.deployed_task.task_template.id.name}, "
56
+ f"version={self.deployed_task.task_template.id.version})"
57
+ )
58
+
59
+ def table_repr(self) -> List[Tuple[str, ...]]:
60
+ """
61
+ Returns a table representation of the deployed task.
62
+ """
63
+ from flyte._initialize import get_client
64
+
65
+ client = get_client()
66
+ task_id = self.deployed_task.task_template.id
67
+ task_url = client.console.task_url(
68
+ project=task_id.project,
69
+ domain=task_id.domain,
70
+ task_name=task_id.name,
71
+ )
72
+ triggers = []
73
+ for t in self.deployed_triggers:
74
+ trigger_url = client.console.trigger_url(
75
+ project=task_id.project,
76
+ domain=task_id.domain,
77
+ task_name=task_id.name,
78
+ trigger_name=t.name,
79
+ )
80
+ triggers.append(f"[link={trigger_url}]{t.name}[/link]")
81
+
82
+ return [
83
+ ("type", "task"),
84
+ ("name", f"[link={task_url}]{task_id.name}[/link]"),
85
+ ("version", task_id.version),
86
+ ("triggers", ",".join(triggers)),
87
+ ]
88
+
89
+
90
+ @rich.repr.auto
91
+ @dataclass
92
+ class DeployedTaskEnvironment:
93
+ env: TaskEnvironment
94
+ deployed_entities: List[DeployedTask]
95
+
96
+ def get_name(self) -> str:
97
+ """
98
+ Returns the name of the deployed environment.
99
+ """
100
+ return self.env.name
101
+
102
+ def summary_repr(self) -> str:
103
+ """
104
+ Returns a summary representation of the deployment.
105
+ """
106
+ entities = ", ".join(f"{e.summary_repr()}" for e in self.deployed_entities or [])
107
+ return f"Deployment(env=[{self.env.name}], entities=[{entities}])"
108
+
109
+ def table_repr(self) -> List[List[Tuple[str, ...]]]:
110
+ """
111
+ Returns a detailed representation of the deployed tasks.
112
+ """
113
+ tuples = []
114
+ if self.deployed_entities:
115
+ for e in self.deployed_entities:
116
+ tuples.append(e.table_repr())
117
+ return tuples
118
+
119
+ def env_repr(self) -> List[Tuple[str, ...]]:
120
+ """
121
+ Returns a detailed representation of the deployed environments.
122
+ """
123
+ env = self.env
124
+ return [
125
+ ("environment", env.name),
126
+ ("image", env.image.uri if isinstance(env.image, Image) else env.image or ""),
127
+ ]
128
+
129
+
130
+ @rich.repr.auto
131
+ @dataclass(frozen=True)
34
132
  class Deployment:
35
- envs: Dict[str, Environment]
36
- deployed_tasks: List[task_definition_pb2.TaskSpec] | None = None
133
+ envs: Dict[str, DeployedEnvironment]
37
134
 
38
135
  def summary_repr(self) -> str:
39
136
  """
40
137
  Returns a summary representation of the deployment.
41
138
  """
42
- env_names = ", ".join(self.envs.keys())
43
- task_names_versions = ", ".join(
44
- f"{task.task_template.id.name} (v{task.task_template.id.version})" for task in self.deployed_tasks or []
45
- )
46
- return f"Deployment(envs=[{env_names}], tasks=[{task_names_versions}])"
139
+ envs = ", ".join(f"{e.summary_repr()}" for e in self.envs.values() or [])
140
+ return f"Deployment(envs=[{envs}])"
141
+
142
+ def table_repr(self) -> List[List[Tuple[str, ...]]]:
143
+ """
144
+ Returns a detailed representation of the deployed tasks.
145
+ """
146
+ tuples = []
147
+ for d in self.envs.values():
148
+ tuples.extend(d.table_repr())
149
+ return tuples
150
+
151
+ def env_repr(self) -> List[List[Tuple[str, ...]]]:
152
+ """
153
+ Returns a detailed representation of the deployed environments.
154
+ """
155
+ tuples = []
156
+ for d in self.envs.values():
157
+ tuples.append(d.env_repr())
158
+ return tuples
47
159
 
48
160
 
49
- @requires_client
50
161
  async def _deploy_task(
51
162
  task: TaskTemplate, serialization_context: SerializationContext, dryrun: bool = False
52
- ) -> task_definition_pb2.TaskSpec:
163
+ ) -> DeployedTask:
53
164
  """
54
165
  Deploy the given task.
55
166
  """
167
+ ensure_client()
168
+ import grpc.aio
169
+ from flyteidl2.task import task_definition_pb2, task_service_pb2
170
+
171
+ import flyte.errors
172
+
173
+ from ._internal.runtime.convert import convert_upload_default_inputs
56
174
  from ._internal.runtime.task_serde import translate_task_to_wire
57
- from ._protos.workflow import task_definition_pb2, task_service_pb2
175
+ from ._internal.runtime.trigger_serde import to_task_trigger
58
176
 
59
177
  image_uri = task.image.uri if isinstance(task.image, Image) else task.image
60
178
 
61
- spec = translate_task_to_wire(task, serialization_context)
62
- if dryrun:
63
- return spec
64
- msg = f"Deploying task {task.name}, with image {image_uri} version {serialization_context.version}"
65
- if spec.task_template.HasField("container") and spec.task_template.container.args:
66
- msg += f" from {spec.task_template.container.args[-3]}.{spec.task_template.container.args[-1]}"
67
- logger.info(msg)
68
- task_id = task_definition_pb2.TaskIdentifier(
69
- org=spec.task_template.id.org,
70
- project=spec.task_template.id.project,
71
- domain=spec.task_template.id.domain,
72
- version=spec.task_template.id.version,
73
- name=spec.task_template.id.name,
179
+ try:
180
+ if dryrun:
181
+ return DeployedTask(translate_task_to_wire(task, serialization_context), [])
182
+
183
+ default_inputs = await convert_upload_default_inputs(task.interface)
184
+ spec = translate_task_to_wire(task, serialization_context, default_inputs=default_inputs)
185
+ # Insert ENV description into spec
186
+ env = task.parent_env() if task.parent_env else None
187
+ if env and env.description:
188
+ spec.environment.description = env.description
189
+
190
+ # Insert documentation entity into task spec
191
+ documentation_entity = _get_documentation_entity(task)
192
+ spec.documentation.CopyFrom(documentation_entity)
193
+
194
+ # Update inputs and outputs descriptions from docstring
195
+ # This is done at deploy time to avoid runtime overhead
196
+ updated_interface = _update_interface_inputs_and_outputs_docstring(
197
+ spec.task_template.interface, task.native_interface
198
+ )
199
+ spec.task_template.interface.CopyFrom(updated_interface)
200
+ msg = f"Deploying task {task.name}, with image {image_uri} version {serialization_context.version}"
201
+ if spec.task_template.HasField("container") and spec.task_template.container.args:
202
+ msg += f" from {spec.task_template.container.args[-3]}.{spec.task_template.container.args[-1]}"
203
+ logger.info(msg)
204
+ task_id = task_definition_pb2.TaskIdentifier(
205
+ org=spec.task_template.id.org,
206
+ project=spec.task_template.id.project,
207
+ domain=spec.task_template.id.domain,
208
+ version=spec.task_template.id.version,
209
+ name=spec.task_template.id.name,
210
+ )
211
+
212
+ deployable_triggers = []
213
+ for t in task.triggers:
214
+ inputs = spec.task_template.interface.inputs
215
+ default_inputs = spec.default_inputs
216
+ deployable_triggers.append(
217
+ await to_task_trigger(
218
+ t=t, task_name=task.name, task_inputs=inputs, task_default_inputs=list(default_inputs)
219
+ )
220
+ )
221
+
222
+ try:
223
+ await get_client().task_service.DeployTask(
224
+ task_service_pb2.DeployTaskRequest(
225
+ task_id=task_id,
226
+ spec=spec,
227
+ triggers=deployable_triggers,
228
+ )
229
+ )
230
+ logger.info(f"Deployed task {task.name} with version {task_id.version}")
231
+ except grpc.aio.AioRpcError as e:
232
+ if e.code() == grpc.StatusCode.ALREADY_EXISTS:
233
+ logger.info(f"Task {task.name} with image {image_uri} already exists, skipping deployment.")
234
+ return DeployedTask(spec, deployable_triggers)
235
+ raise
236
+
237
+ return DeployedTask(spec, deployable_triggers)
238
+ except Exception as e:
239
+ logger.error(f"Failed to deploy task {task.name} with image {image_uri}: {e}")
240
+ raise flyte.errors.DeploymentError(
241
+ f"Failed to deploy task {task.name} file{task.source_file} with image {image_uri}, Error: {e!s}"
242
+ ) from e
243
+
244
+
245
+ def _get_documentation_entity(task_template: TaskTemplate) -> task_definition_pb2.DocumentationEntity:
246
+ """
247
+ Create a DocumentationEntity with descriptions and source code url.
248
+ Short descriptions are truncated to 255 chars, long descriptions to 2048 chars.
249
+
250
+ :param task_template: TaskTemplate containing the interface docstring.
251
+ :return: DocumentationEntity with short description, long description, and source code url link.
252
+ """
253
+ from flyteidl2.task import task_definition_pb2
254
+
255
+ from flyte._utils.description_parser import parse_description
256
+ from flyte.git import GitStatus
257
+
258
+ docstring = task_template.interface.docstring
259
+ short_desc = None
260
+ long_desc = None
261
+ source_code = None
262
+ if docstring and docstring.short_description:
263
+ short_desc = parse_description(docstring.short_description, 255)
264
+ if docstring and docstring.long_description:
265
+ long_desc = parse_description(docstring.long_description, 2048)
266
+ if hasattr(task_template, "func") and hasattr(task_template.func, "__code__") and task_template.func.__code__:
267
+ line_number = (
268
+ task_template.func.__code__.co_firstlineno + 1
269
+ ) # The function definition line number is located at the line after @env.task decorator
270
+ file_path = task_template.func.__code__.co_filename
271
+ git_status = GitStatus.from_current_repo()
272
+ if git_status.is_valid:
273
+ # Build git host url
274
+ git_host_url = git_status.build_url(file_path, line_number)
275
+ if git_host_url:
276
+ source_code = task_definition_pb2.SourceCode(link=git_host_url)
277
+
278
+ return task_definition_pb2.DocumentationEntity(
279
+ short_description=short_desc,
280
+ long_description=long_desc,
281
+ source_code=source_code,
74
282
  )
75
283
 
76
- await get_client().task_service.DeployTask(task_service_pb2.DeployTaskRequest(task_id=task_id, spec=spec))
77
- logger.info(f"Deployed task {task.name} with version {task_id.version}")
78
- return spec
284
+
285
+ def _update_interface_inputs_and_outputs_docstring(
286
+ typed_interface: interface_pb2.TypedInterface, native_interface: NativeInterface
287
+ ) -> interface_pb2.TypedInterface:
288
+ """
289
+ Create a new TypedInterface with updated descriptions from the NativeInterface docstring.
290
+ This is done during deployment to avoid runtime overhead of parsing docstrings during task execution.
291
+
292
+ :param typed_interface: The protobuf TypedInterface to copy.
293
+ :param native_interface: The NativeInterface containing the docstring.
294
+ :return: New TypedInterface with descriptions from docstring if docstring exists.
295
+ """
296
+ from flyteidl2.core import interface_pb2
297
+
298
+ # Create a copy of the typed_interface to avoid mutating the input
299
+ updated_interface = interface_pb2.TypedInterface()
300
+ updated_interface.CopyFrom(typed_interface)
301
+
302
+ if not native_interface.docstring:
303
+ return updated_interface
304
+
305
+ # Extract descriptions from the parsed docstring
306
+ input_descriptions = {k: v for k, v in native_interface.docstring.input_descriptions.items() if v is not None}
307
+ output_descriptions = {k: v for k, v in native_interface.docstring.output_descriptions.items() if v is not None}
308
+
309
+ # Update input variable descriptions
310
+ if updated_interface.inputs and updated_interface.inputs.variables:
311
+ for var_name, desc in input_descriptions.items():
312
+ if var_name in updated_interface.inputs.variables:
313
+ updated_interface.inputs.variables[var_name].description = desc
314
+
315
+ # Update output variable descriptions
316
+ if updated_interface.outputs and updated_interface.outputs.variables:
317
+ for var_name, desc in output_descriptions.items():
318
+ if var_name in updated_interface.outputs.variables:
319
+ updated_interface.outputs.variables[var_name].description = desc
320
+
321
+ return updated_interface
79
322
 
80
323
 
81
324
  async def _build_image_bg(env_name: str, image: Image) -> Tuple[str, str]:
@@ -88,101 +331,163 @@ async def _build_image_bg(env_name: str, image: Image) -> Tuple[str, str]:
88
331
  return env_name, await build.aio(image)
89
332
 
90
333
 
91
- async def build_images(deployment: DeploymentPlan) -> ImageCache:
334
+ async def _build_images(deployment: DeploymentPlan, image_refs: Dict[str, str] | None = None) -> ImageCache:
92
335
  """
93
336
  Build the images for the given deployment plan and update the environment with the built image.
94
337
  """
338
+ from flyte._image import _DEFAULT_IMAGE_REF_NAME
339
+
95
340
  from ._internal.imagebuild.image_builder import ImageCache
96
341
 
342
+ if image_refs is None:
343
+ image_refs = {}
344
+
97
345
  images = []
98
346
  image_identifier_map = {}
99
347
  for env_name, env in deployment.envs.items():
100
348
  if not isinstance(env.image, str):
101
- logger.info(f"Building Image for environment {env_name}, image: {env.image}")
349
+ if env.image._ref_name is not None:
350
+ if env.image._ref_name in image_refs:
351
+ # If the image is set in the config, set it as the base_image
352
+ image_uri = image_refs[env.image._ref_name]
353
+ env.image = env.image.clone(base_image=image_uri)
354
+ else:
355
+ raise ValueError(
356
+ f"Image name '{env.image._ref_name}' not found in config. Available: {list(image_refs.keys())}"
357
+ )
358
+ if not env.image._layers:
359
+ # No additional layers, use the base_image directly without building
360
+ image_identifier_map[env_name] = image_uri
361
+ continue
362
+ logger.debug(f"Building Image for environment {env_name}, image: {env.image}")
102
363
  images.append(_build_image_bg(env_name, env.image))
103
364
 
104
365
  elif env.image == "auto" and "auto" not in image_identifier_map:
105
- auto_image = Image.auto()
106
- image_identifier_map["auto"] = auto_image.uri
366
+ if _DEFAULT_IMAGE_REF_NAME in image_refs:
367
+ # If the default image is set through CLI, use it instead
368
+ image_uri = image_refs[_DEFAULT_IMAGE_REF_NAME]
369
+ image_identifier_map[env_name] = image_uri
370
+ continue
371
+ auto_image = Image.from_debian_base()
372
+ images.append(_build_image_bg(env_name, auto_image))
107
373
  final_images = await asyncio.gather(*images)
108
374
 
109
375
  for env_name, image_uri in final_images:
110
- logger.info(f"Built Image for environment {env_name}, image: {image_uri}")
111
- env = deployment.envs[env_name]
112
- if isinstance(env.image, Image):
113
- image_identifier_map[env.image.identifier] = env.image.uri
376
+ logger.warning(f"Built Image for environment {env_name}, image: {image_uri}")
377
+ image_identifier_map[env_name] = image_uri
114
378
 
115
379
  return ImageCache(image_lookup=image_identifier_map)
116
380
 
117
381
 
382
+ async def _deploy_task_env(context: DeploymentContext) -> DeployedTaskEnvironment:
383
+ """
384
+ Deploy the given task environment.
385
+ """
386
+ ensure_client()
387
+ env = context.environment
388
+ if not isinstance(env, TaskEnvironment):
389
+ raise ValueError(f"Expected TaskEnvironment, got {type(env)}")
390
+
391
+ task_coros = []
392
+ for task in env.tasks.values():
393
+ task_coros.append(_deploy_task(task, context.serialization_context, dryrun=context.dryrun))
394
+ deployed_task_vals = await asyncio.gather(*task_coros)
395
+ deployed_tasks = []
396
+ for t in deployed_task_vals:
397
+ deployed_tasks.append(t)
398
+ return DeployedTaskEnvironment(env=env, deployed_entities=deployed_tasks)
399
+
400
+
118
401
  @requires_initialization
119
- async def apply(deployment: DeploymentPlan, copy_style: CopyFiles, dryrun: bool = False) -> Deployment:
402
+ async def apply(deployment_plan: DeploymentPlan, copy_style: CopyFiles, dryrun: bool = False) -> Deployment:
403
+ import flyte.errors
404
+
120
405
  from ._code_bundle import build_code_bundle
406
+ from ._deployer import DeploymentContext, get_deployer
407
+
408
+ cfg = get_init_config()
121
409
 
122
- cfg = get_common_config()
123
- image_cache = await build_images(deployment)
124
- if copy_style == "none":
125
- code_bundle = None
126
- assert deployment.version is not None, "Version must be set when copy_style is none"
410
+ image_cache = await _build_images(deployment_plan, cfg.images)
411
+
412
+ if copy_style == "none" and not deployment_plan.version:
413
+ raise flyte.errors.DeploymentError("Version must be set when copy_style is none")
127
414
  else:
415
+ # if this is an AppEnvironment.include, skip code bundling here and build a code bundle at the
416
+ # app._deploy._deploy_app function
128
417
  code_bundle = await build_code_bundle(from_dir=cfg.root_dir, dryrun=dryrun, copy_style=copy_style)
129
- deployment.version = code_bundle.computed_version
418
+ if deployment_plan.version:
419
+ version = deployment_plan.version
420
+ else:
421
+ h = hashlib.md5()
422
+ h.update(cloudpickle.dumps(deployment_plan.envs))
423
+ h.update(code_bundle.computed_version.encode("utf-8"))
424
+ h.update(cloudpickle.dumps(image_cache))
425
+ version = h.hexdigest()
130
426
 
131
427
  sc = SerializationContext(
132
428
  project=cfg.project,
133
429
  domain=cfg.domain,
134
430
  org=cfg.org,
135
431
  code_bundle=code_bundle,
136
- version=deployment.version,
432
+ version=version,
137
433
  image_cache=image_cache,
138
434
  root_dir=cfg.root_dir,
139
435
  )
140
436
 
141
- tasks = []
142
- for env_name, env in deployment.envs.items():
437
+ deployment_coros = []
438
+ for env_name, env in deployment_plan.envs.items():
143
439
  logger.info(f"Deploying environment {env_name}")
144
- if isinstance(env, TaskEnvironment):
145
- for task in env.tasks.values():
146
- tasks.append(_deploy_task(task, dryrun=dryrun, serialization_context=sc))
147
- return Deployment(envs=deployment.envs, deployed_tasks=await asyncio.gather(*tasks))
440
+ deployer = get_deployer(type(env))
441
+ context = DeploymentContext(environment=env, serialization_context=sc, dryrun=dryrun)
442
+ deployment_coros.append(deployer(context))
443
+ deployed_envs = await asyncio.gather(*deployment_coros)
444
+ envs = {}
445
+ for d in deployed_envs:
446
+ envs[d.get_name()] = d
447
+
448
+ return Deployment(envs)
148
449
 
149
450
 
150
- def _recursive_discover(
151
- planned_envs: Dict[str, Environment], envs: Environment | List[Environment]
152
- ) -> Dict[str, Environment]:
451
+ def _recursive_discover(planned_envs: Dict[str, Environment], env: Environment) -> Dict[str, Environment]:
153
452
  """
154
453
  Recursively deploy the environment and its dependencies, if not already deployed (present in env_tasks) and
155
454
  return the updated env_tasks.
156
455
  """
157
- if isinstance(envs, Environment):
158
- envs = [envs]
159
- for env in envs:
160
- # Skip if the environment is already planned
161
- if env.name in planned_envs:
162
- continue
163
- # Recursively discover dependent environments
164
- for dependent_env in env.env_dep_hints:
165
- _recursive_discover(planned_envs, dependent_env)
166
- # Add the environment to the existing envs
167
- planned_envs[env.name] = env
456
+ if env.name in planned_envs:
457
+ if planned_envs[env.name] is not env:
458
+ # Raise error if different TaskEnvironment objects have the same name
459
+ raise ValueError(f"Duplicate environment name '{env.name}' found")
460
+ # Add the environment to the existing envs
461
+ planned_envs[env.name] = env
462
+
463
+ # Recursively discover dependent environments
464
+ for dependent_env in env.depends_on:
465
+ _recursive_discover(planned_envs, dependent_env)
168
466
  return planned_envs
169
467
 
170
468
 
171
- def plan_deploy(*envs: Environment, version: Optional[str] = None) -> DeploymentPlan:
469
+ def plan_deploy(*envs: Environment, version: Optional[str] = None) -> List[DeploymentPlan]:
172
470
  if envs is None:
173
- return DeploymentPlan({})
174
- planned_envs = _recursive_discover({}, *envs)
175
- return DeploymentPlan(planned_envs, version=version)
471
+ return [DeploymentPlan({})]
472
+ deployment_plans = []
473
+ visited_envs: Set[str] = set()
474
+ for env in envs:
475
+ if env.name in visited_envs:
476
+ raise ValueError(f"Duplicate environment name '{env.name}' found")
477
+ planned_envs = _recursive_discover({}, env)
478
+ deployment_plans.append(DeploymentPlan(planned_envs, version=version))
479
+ visited_envs.update(planned_envs.keys())
480
+ return deployment_plans
176
481
 
177
482
 
178
- @syncer.wrap
483
+ @syncify
179
484
  async def deploy(
180
485
  *envs: Environment,
181
486
  dryrun: bool = False,
182
487
  version: str | None = None,
183
488
  interactive_mode: bool | None = None,
184
489
  copy_style: CopyFiles = "loaded_modules",
185
- ) -> Deployment:
490
+ ) -> List[Deployment]:
186
491
  """
187
492
  Deploy the given environment or list of environments.
188
493
  :param envs: Environment or list of environments to deploy.
@@ -198,5 +503,21 @@ async def deploy(
198
503
  """
199
504
  if interactive_mode:
200
505
  raise NotImplementedError("Interactive mode not yet implemented for deployment")
201
- deployment = plan_deploy(*envs, version=version)
202
- return await apply(deployment, copy_style=copy_style, dryrun=dryrun)
506
+ deployment_plans = plan_deploy(*envs, version=version)
507
+ deployments = []
508
+ for deployment_plan in deployment_plans:
509
+ deployments.append(apply(deployment_plan, copy_style=copy_style, dryrun=dryrun))
510
+ return await asyncio.gather(*deployments)
511
+
512
+
513
+ @syncify
514
+ async def build_images(envs: Environment) -> ImageCache:
515
+ """
516
+ Build the images for the given environments.
517
+ :param envs: Environment to build images for.
518
+ :return: ImageCache containing the built images.
519
+ """
520
+ cfg = get_init_config()
521
+ images = cfg.images if cfg else {}
522
+ deployment = plan_deploy(envs)
523
+ return await _build_images(deployment[0], images)
flyte/_deployer.py ADDED
@@ -0,0 +1,109 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Dict, List, Protocol, Tuple, Type
5
+
6
+ import rich.repr
7
+
8
+ from flyte.models import SerializationContext
9
+
10
+ from ._deploy import _deploy_task_env
11
+ from ._environment import Environment
12
+ from ._task_environment import TaskEnvironment
13
+
14
+
15
+ @rich.repr.auto
16
+ @dataclass
17
+ class DeploymentContext:
18
+ """
19
+ Context for deployment operations.
20
+ """
21
+
22
+ environment: Environment
23
+ serialization_context: SerializationContext
24
+ dryrun: bool = False
25
+
26
+
27
+ class DeployedEnvironment(Protocol):
28
+ """
29
+ Protocol for deployed environment representations.
30
+ """
31
+
32
+ def get_name(self) -> str:
33
+ """
34
+ Returns the name of the deployed environment.
35
+ Returns:
36
+ """
37
+ ...
38
+
39
+ def env_repr(self) -> List[Tuple[str, ...]]:
40
+ """
41
+ Returns a detailed representation of the deployed environment.
42
+ Returns:
43
+ """
44
+ ...
45
+
46
+ def table_repr(self) -> List[List[Tuple[str, ...]]]:
47
+ """
48
+ Returns a detailed representation of the deployed entities in the environment, useful for tabular display.
49
+ Returns:
50
+
51
+ """
52
+ ...
53
+
54
+ def summary_repr(self) -> str:
55
+ """
56
+ Returns a summary representation of the deployed environment.
57
+ Returns:
58
+ """
59
+ ...
60
+
61
+
62
+ class Deployer(Protocol):
63
+ """
64
+ Protocol for deployment callables.
65
+ """
66
+
67
+ async def __call__(self, context: DeploymentContext) -> DeployedEnvironment:
68
+ """
69
+ Deploy the environment described in the context.
70
+
71
+ Args:
72
+ context: Deployment context containing environment, serialization context, and dryrun flag
73
+
74
+ Returns:
75
+ Deployment result
76
+ """
77
+ ...
78
+
79
+
80
+ _ENVTYPE_REGISTRY: Dict[Type[Environment], Deployer] = {
81
+ TaskEnvironment: _deploy_task_env,
82
+ }
83
+
84
+
85
+ def register_deployer(env_type: Type[Environment], deployer: Deployer) -> None:
86
+ """
87
+ Register a deployer for a specific environment type.
88
+
89
+ Args:
90
+ env_type: Type of environment this deployer handles
91
+ deployer: Deployment callable that conforms to the Deployer protocol
92
+ """
93
+ _ENVTYPE_REGISTRY[env_type] = deployer
94
+
95
+
96
+ def get_deployer(env_type: Type[Environment | TaskEnvironment]) -> Deployer:
97
+ """
98
+ Get the registered deployer for an environment type.
99
+
100
+ Args:
101
+ env_type: Type of environment to get deployer for
102
+
103
+ Returns:
104
+ Deployer for the environment type, defaults to task environment deployer
105
+ """
106
+ for tpe, v in _ENVTYPE_REGISTRY.items():
107
+ if issubclass(env_type, tpe):
108
+ return v
109
+ raise ValueError(f"No deployer registered for environment type {env_type}")