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
@@ -1,87 +1,149 @@
1
1
  import asyncio
2
+ import os
2
3
  import shutil
3
4
  import subprocess
4
5
  import tempfile
5
- from pathlib import Path
6
+ import typing
7
+ from pathlib import Path, PurePath
6
8
  from string import Template
7
- from typing import ClassVar, Protocol, cast
9
+ from typing import ClassVar, Optional, Protocol, cast
8
10
 
9
11
  import aiofiles
10
12
  import click
11
13
 
14
+ from flyte import Secret
12
15
  from flyte._image import (
13
16
  AptPackages,
14
17
  Commands,
15
18
  CopyConfig,
19
+ DockerIgnore,
16
20
  Env,
17
21
  Image,
18
22
  Layer,
23
+ PipOption,
19
24
  PipPackages,
25
+ PoetryProject,
26
+ PythonWheels,
20
27
  Requirements,
21
28
  UVProject,
29
+ UVScript,
22
30
  WorkDir,
23
31
  _DockerLines,
32
+ _ensure_tuple,
24
33
  )
34
+ from flyte._internal.imagebuild.image_builder import (
35
+ DockerAPIImageChecker,
36
+ ImageBuilder,
37
+ ImageChecker,
38
+ LocalDockerCommandImageChecker,
39
+ LocalPodmanCommandImageChecker,
40
+ )
41
+ from flyte._internal.imagebuild.utils import copy_files_to_context, get_and_list_dockerignore
25
42
  from flyte._logging import logger
26
43
 
27
44
  _F_IMG_ID = "_F_IMG_ID"
45
+ FLYTE_DOCKER_BUILDER_CACHE_FROM = "FLYTE_DOCKER_BUILDER_CACHE_FROM"
46
+ FLYTE_DOCKER_BUILDER_CACHE_TO = "FLYTE_DOCKER_BUILDER_CACHE_TO"
47
+
48
+ UV_LOCK_WITHOUT_PROJECT_INSTALL_TEMPLATE = Template("""\
49
+ RUN --mount=type=cache,sharing=locked,mode=0777,target=/root/.cache/uv,id=uv \
50
+ --mount=type=bind,target=uv.lock,src=$UV_LOCK_PATH,rw \
51
+ --mount=type=bind,target=pyproject.toml,src=$PYPROJECT_PATH \
52
+ $SECRET_MOUNT \
53
+ uv sync --active --inexact $PIP_INSTALL_ARGS
54
+ """)
28
55
 
29
56
  UV_LOCK_INSTALL_TEMPLATE = Template("""\
30
- WORKDIR /root
31
57
  RUN --mount=type=cache,sharing=locked,mode=0777,target=/root/.cache/uv,id=uv \
32
- --mount=type=bind,target=uv.lock,src=uv.lock \
33
- --mount=type=bind,target=pyproject.toml,src=pyproject.toml \
34
- uv sync $PIP_INSTALL_ARGS
35
- WORKDIR /
36
-
37
- # Update PATH and UV_PYTHON to point to the venv created by uv sync
38
- ENV PATH="/root/.venv/bin:$$PATH" \
39
- VIRTUALENV=/root/.venv \
40
- UV_PYTHON=/root/.venv/bin/python
58
+ --mount=type=bind,target=/root/.flyte/$PYPROJECT_PATH,src=$PYPROJECT_PATH,rw \
59
+ $SECRET_MOUNT \
60
+ uv sync --active --inexact --no-editable $PIP_INSTALL_ARGS --project /root/.flyte/$PYPROJECT_PATH
61
+ """)
62
+
63
+ POETRY_LOCK_WITHOUT_PROJECT_INSTALL_TEMPLATE = Template("""\
64
+ RUN --mount=type=cache,sharing=locked,mode=0777,target=/root/.cache/uv,id=uv \
65
+ uv pip install poetry
66
+
67
+ ENV POETRY_CACHE_DIR=/tmp/poetry_cache \
68
+ POETRY_VIRTUALENVS_IN_PROJECT=true
69
+
70
+ RUN --mount=type=cache,sharing=locked,mode=0777,target=/tmp/poetry_cache,id=poetry \
71
+ --mount=type=bind,target=poetry.lock,src=$POETRY_LOCK_PATH \
72
+ --mount=type=bind,target=pyproject.toml,src=$PYPROJECT_PATH \
73
+ $SECRET_MOUNT \
74
+ poetry install $POETRY_INSTALL_ARGS
75
+ """)
76
+
77
+ POETRY_LOCK_INSTALL_TEMPLATE = Template("""\
78
+ RUN --mount=type=cache,sharing=locked,mode=0777,target=/root/.cache/uv,id=uv \
79
+ uv pip install poetry
80
+
81
+ ENV POETRY_CACHE_DIR=/tmp/poetry_cache \
82
+ POETRY_VIRTUALENVS_IN_PROJECT=true
83
+
84
+ RUN --mount=type=cache,sharing=locked,mode=0777,target=/tmp/poetry_cache,id=poetry \
85
+ --mount=type=bind,target=/root/.flyte/$PYPROJECT_PATH,src=$PYPROJECT_PATH,rw \
86
+ $SECRET_MOUNT \
87
+ poetry install $POETRY_INSTALL_ARGS -C /root/.flyte/$PYPROJECT_PATH
41
88
  """)
42
89
 
43
90
  UV_PACKAGE_INSTALL_COMMAND_TEMPLATE = Template("""\
44
91
  RUN --mount=type=cache,sharing=locked,mode=0777,target=/root/.cache/uv,id=uv \
45
- --mount=type=bind,target=requirements_uv.txt,src=requirements_uv.txt \
46
- uv pip install --python $$UV_PYTHON $PIP_INSTALL_ARGS
92
+ $REQUIREMENTS_MOUNT \
93
+ $SECRET_MOUNT \
94
+ uv pip install --python $$UV_PYTHON $PIP_INSTALL_ARGS
95
+ """)
96
+
97
+ UV_WHEEL_INSTALL_COMMAND_TEMPLATE = Template("""\
98
+ RUN --mount=type=cache,sharing=locked,mode=0777,target=/root/.cache/uv,id=wheel \
99
+ --mount=source=/dist,target=/dist,type=bind \
100
+ $SECRET_MOUNT \
101
+ uv pip install --python $$UV_PYTHON $PIP_INSTALL_ARGS
47
102
  """)
48
103
 
49
104
  APT_INSTALL_COMMAND_TEMPLATE = Template("""\
50
105
  RUN --mount=type=cache,sharing=locked,mode=0777,target=/var/cache/apt,id=apt \
51
- apt-get update && apt-get install -y --no-install-recommends \
52
- $APT_PACKAGES
106
+ $SECRET_MOUNT \
107
+ apt-get update && apt-get install -y --no-install-recommends \
108
+ $APT_PACKAGES
53
109
  """)
54
110
 
55
111
  UV_PYTHON_INSTALL_COMMAND = Template("""\
56
112
  RUN --mount=type=cache,sharing=locked,mode=0777,target=/root/.cache/uv,id=uv \
57
- uv pip install $PIP_INSTALL_ARGS
113
+ $SECRET_MOUNT \
114
+ uv pip install $PIP_INSTALL_ARGS
58
115
  """)
59
116
 
60
117
  # uv pip install --python /root/env/bin/python
61
118
  # new template
62
119
  DOCKER_FILE_UV_BASE_TEMPLATE = Template("""\
63
- #syntax=docker/dockerfile:1.5
64
- FROM ghcr.io/astral-sh/uv:0.6.12 as uv
120
+ # syntax=docker/dockerfile:1.10
121
+ FROM ghcr.io/astral-sh/uv:0.8.13 AS uv
65
122
  FROM $BASE_IMAGE
66
123
 
124
+
67
125
  USER root
68
126
 
127
+
69
128
  # Copy in uv so that later commands don't have to mount it in
70
129
  COPY --from=uv /uv /usr/bin/uv
71
130
 
131
+
72
132
  # Configure default envs
73
133
  ENV UV_COMPILE_BYTECODE=1 \
74
- UV_LINK_MODE=copy \
75
- VIRTUALENV=/opt/venv \
76
- UV_PYTHON=/opt/venv/bin/python \
77
- PATH="/opt/venv/bin:$$PATH"
134
+ UV_LINK_MODE=copy \
135
+ VIRTUALENV=/opt/venv \
136
+ UV_PYTHON=/opt/venv/bin/python \
137
+ PATH="/opt/venv/bin:$$PATH"
138
+
78
139
 
79
140
  # Create a virtualenv with the user specified python version
80
- RUN uv venv $$VIRTUALENV --python=$PYTHON_VERSION
141
+ RUN uv venv $$VIRTUALENV --python=$PYTHON_VERSION && uv run --python=$$UV_PYTHON python -m compileall $$VIRTUALENV
142
+
81
143
 
82
144
  # Adds nvidia just in case it exists
83
145
  ENV PATH="$$PATH:/usr/local/nvidia/bin:/usr/local/cuda/bin" \
84
- LD_LIBRARY_PATH="/usr/local/nvidia/lib64:$$LD_LIBRARY_PATH"
146
+ LD_LIBRARY_PATH="/usr/local/nvidia/lib64"
85
147
  """)
86
148
 
87
149
  # This gets added on to the end of the dockerfile
@@ -100,36 +162,70 @@ class Handler(Protocol):
100
162
  class PipAndRequirementsHandler:
101
163
  @staticmethod
102
164
  async def handle(layer: PipPackages, context_path: Path, dockerfile: str) -> str:
165
+ secret_mounts = _get_secret_mounts_layer(layer.secret_mounts)
166
+
167
+ # Set pip_install_args based on the layer type - either a requirements file or a list of packages
103
168
  if isinstance(layer, Requirements):
104
- async with aiofiles.open(layer.file) as f:
105
- requirements = []
106
- async for line in f:
107
- requirement = await line
108
- requirements.append(requirement.strip())
169
+ if not layer.file.exists():
170
+ raise FileNotFoundError(f"Requirements file {layer.file} does not exist")
171
+ if not layer.file.is_file():
172
+ raise ValueError(f"Requirements file {layer.file} is not a file")
173
+
174
+ # Copy the requirements file to the context path
175
+ requirements_path = copy_files_to_context(layer.file, context_path)
176
+ rel_path = str(requirements_path.relative_to(context_path))
177
+ pip_install_args = layer.get_pip_install_args()
178
+ pip_install_args.extend(["--requirement", "requirements.txt"])
179
+ mount = f"--mount=type=bind,target=requirements.txt,src={rel_path}"
109
180
  else:
181
+ mount = ""
110
182
  requirements = list(layer.packages) if layer.packages else []
111
- requirements_uv_path = context_path / "requirements_uv.txt"
112
- async with aiofiles.open(requirements_uv_path, "w") as f:
113
- reqs = "\n".join(requirements)
114
- await f.write(reqs)
183
+ reqs = " ".join(requirements)
184
+ pip_install_args = layer.get_pip_install_args()
185
+ pip_install_args.append(reqs)
186
+
187
+ delta = UV_PACKAGE_INSTALL_COMMAND_TEMPLATE.substitute(
188
+ SECRET_MOUNT=secret_mounts,
189
+ REQUIREMENTS_MOUNT=mount,
190
+ PIP_INSTALL_ARGS=" ".join(pip_install_args),
191
+ )
115
192
 
116
- pip_install_args = []
117
- if layer.index_url:
118
- pip_install_args.append(f"--index-url {layer.index_url}")
193
+ dockerfile += delta
119
194
 
120
- if layer.extra_index_urls:
121
- pip_install_args.extend([f"--extra-index-url {url}" for url in layer.extra_index_urls])
195
+ return dockerfile
122
196
 
123
- if layer.pre:
124
- pip_install_args.append("--pre")
125
197
 
126
- if layer.extra_args:
127
- pip_install_args.append(layer.extra_args)
198
+ class PythonWheelHandler:
199
+ @staticmethod
200
+ async def handle(layer: PythonWheels, context_path: Path, dockerfile: str) -> str:
201
+ shutil.copytree(layer.wheel_dir, context_path / "dist", dirs_exist_ok=True)
202
+ pip_install_args = layer.get_pip_install_args()
203
+ secret_mounts = _get_secret_mounts_layer(layer.secret_mounts)
204
+
205
+ # First install: Install the wheel without dependencies using --no-deps
206
+ pip_install_args_no_deps = [
207
+ *pip_install_args,
208
+ *[
209
+ "--find-links",
210
+ "/dist",
211
+ "--no-deps",
212
+ "--no-index",
213
+ "--reinstall",
214
+ layer.package_name,
215
+ ],
216
+ ]
128
217
 
129
- pip_install_args.extend(["--requirement", "requirements_uv.txt"])
218
+ delta1 = UV_WHEEL_INSTALL_COMMAND_TEMPLATE.substitute(
219
+ PIP_INSTALL_ARGS=" ".join(pip_install_args_no_deps), SECRET_MOUNT=secret_mounts
220
+ )
221
+ dockerfile += delta1
130
222
 
131
- delta = UV_PACKAGE_INSTALL_COMMAND_TEMPLATE.substitute(PIP_INSTALL_ARGS=" ".join(pip_install_args))
132
- dockerfile += delta
223
+ # Second install: Install dependencies from PyPI
224
+ pip_install_args_deps = [*pip_install_args, layer.package_name]
225
+ delta2 = UV_WHEEL_INSTALL_COMMAND_TEMPLATE.substitute(
226
+ PIP_INSTALL_ARGS=" ".join(pip_install_args_deps), SECRET_MOUNT=secret_mounts
227
+ )
228
+ dockerfile += delta2
133
229
 
134
230
  return dockerfile
135
231
 
@@ -156,9 +252,10 @@ class EnvHandler:
156
252
 
157
253
  class AptPackagesHandler:
158
254
  @staticmethod
159
- async def handle(layer: AptPackages, context_path: Path, dockerfile: str) -> str:
255
+ async def handle(layer: AptPackages, _: Path, dockerfile: str) -> str:
160
256
  packages = layer.packages
161
- delta = APT_INSTALL_COMMAND_TEMPLATE.substitute(APT_PACKAGES=" ".join(packages))
257
+ secret_mounts = _get_secret_mounts_layer(layer.secret_mounts)
258
+ delta = APT_INSTALL_COMMAND_TEMPLATE.substitute(APT_PACKAGES=" ".join(packages), SECRET_MOUNT=secret_mounts)
162
259
  dockerfile += delta
163
260
 
164
261
  return dockerfile
@@ -166,64 +263,227 @@ class AptPackagesHandler:
166
263
 
167
264
  class UVProjectHandler:
168
265
  @staticmethod
169
- async def handle(layer: UVProject, context_path: Path, dockerfile: str) -> str:
170
- # copy the two files
171
- shutil.copy(layer.pyproject, context_path)
172
- shutil.copy(layer.uvlock, context_path)
173
-
174
- # --locked: Assert that the `uv.lock` will remain unchanged
175
- # --no-dev: Omit the development dependency group
176
- # --no-install-project: Do not install the current project
177
- additional_pip_install_args = ["--locked", "--no-dev", "--no-install-project"]
178
- delta = UV_LOCK_INSTALL_TEMPLATE.substitute(PIP_INSTALL_ARGS=" ".join(additional_pip_install_args))
266
+ async def handle(
267
+ layer: UVProject, context_path: Path, dockerfile: str, docker_ignore_patterns: list[str] = []
268
+ ) -> str:
269
+ secret_mounts = _get_secret_mounts_layer(layer.secret_mounts)
270
+ if layer.project_install_mode == "dependencies_only":
271
+ pip_install_args = " ".join(layer.get_pip_install_args())
272
+ if "--no-install-project" not in pip_install_args:
273
+ pip_install_args += " --no-install-project"
274
+ if "--no-sources" not in pip_install_args:
275
+ pip_install_args += " --no-sources"
276
+ # Only Copy pyproject.yaml and uv.lock.
277
+ pyproject_dst = copy_files_to_context(layer.pyproject, context_path)
278
+ uvlock_dst = copy_files_to_context(layer.uvlock, context_path)
279
+ delta = UV_LOCK_WITHOUT_PROJECT_INSTALL_TEMPLATE.substitute(
280
+ UV_LOCK_PATH=uvlock_dst.relative_to(context_path),
281
+ PYPROJECT_PATH=pyproject_dst.relative_to(context_path),
282
+ PIP_INSTALL_ARGS=pip_install_args,
283
+ SECRET_MOUNT=secret_mounts,
284
+ )
285
+ else:
286
+ # Copy the entire project.
287
+ pyproject_dst = copy_files_to_context(layer.pyproject.parent, context_path, docker_ignore_patterns)
288
+
289
+ # Make sure pyproject.toml and uv.lock files are not removed by docker ignore
290
+ uv_lock_context_path = pyproject_dst / "uv.lock"
291
+ pyproject_context_path = pyproject_dst / "pyproject.toml"
292
+ if not uv_lock_context_path.exists():
293
+ shutil.copy(layer.uvlock, pyproject_dst)
294
+ if not pyproject_context_path.exists():
295
+ shutil.copy(layer.pyproject, pyproject_dst)
296
+
297
+ delta = UV_LOCK_INSTALL_TEMPLATE.substitute(
298
+ PYPROJECT_PATH=pyproject_dst.relative_to(context_path),
299
+ PIP_INSTALL_ARGS=" ".join(layer.get_pip_install_args()),
300
+ SECRET_MOUNT=secret_mounts,
301
+ )
302
+
179
303
  dockerfile += delta
304
+ return dockerfile
180
305
 
306
+
307
+ class PoetryProjectHandler:
308
+ @staticmethod
309
+ async def handel(
310
+ layer: PoetryProject, context_path: Path, dockerfile: str, docker_ignore_patterns: list[str] = []
311
+ ) -> str:
312
+ secret_mounts = _get_secret_mounts_layer(layer.secret_mounts)
313
+ extra_args = layer.extra_args or ""
314
+ if layer.project_install_mode == "dependencies_only":
315
+ # Only Copy pyproject.yaml and poetry.lock.
316
+ pyproject_dst = copy_files_to_context(layer.pyproject, context_path)
317
+ poetry_lock_dst = copy_files_to_context(layer.poetry_lock, context_path)
318
+ if "--no-root" not in extra_args:
319
+ extra_args += " --no-root"
320
+ delta = POETRY_LOCK_WITHOUT_PROJECT_INSTALL_TEMPLATE.substitute(
321
+ POETRY_LOCK_PATH=poetry_lock_dst.relative_to(context_path),
322
+ PYPROJECT_PATH=pyproject_dst.relative_to(context_path),
323
+ POETRY_INSTALL_ARGS=extra_args,
324
+ SECRET_MOUNT=secret_mounts,
325
+ )
326
+ else:
327
+ # Copy the entire project.
328
+ pyproject_dst = copy_files_to_context(layer.pyproject.parent, context_path, docker_ignore_patterns)
329
+
330
+ # Make sure pyproject.toml and poetry.lock files are not removed by docker ignore
331
+ poetry_lock_context_path = pyproject_dst / "poetry.lock"
332
+ pyproject_context_path = pyproject_dst / "pyproject.toml"
333
+ if not poetry_lock_context_path.exists():
334
+ shutil.copy(layer.poetry_lock, pyproject_dst)
335
+ if not pyproject_context_path.exists():
336
+ shutil.copy(layer.pyproject, pyproject_dst)
337
+
338
+ delta = POETRY_LOCK_INSTALL_TEMPLATE.substitute(
339
+ PYPROJECT_PATH=pyproject_dst.relative_to(context_path),
340
+ POETRY_INSTALL_ARGS=extra_args,
341
+ SECRET_MOUNT=secret_mounts,
342
+ )
343
+ dockerfile += delta
181
344
  return dockerfile
182
345
 
183
346
 
347
+ class DockerIgnoreHandler:
348
+ @staticmethod
349
+ async def handle(layer: DockerIgnore, context_path: Path, _: str):
350
+ shutil.copy(layer.path, context_path)
351
+
352
+
184
353
  class CopyConfigHandler:
185
354
  @staticmethod
186
- async def handle(layer: CopyConfig, context_path: Path, dockerfile: str) -> str:
355
+ async def handle(
356
+ layer: CopyConfig, context_path: Path, dockerfile: str, docker_ignore_patterns: list[str] = []
357
+ ) -> str:
187
358
  # Copy the source config file or directory to the context path
188
- abs_path = layer.context_source.absolute()
189
- dest_path = context_path / abs_path.name
190
- image_dest_path = layer.image_dest + "/" + abs_path.name
191
- if layer.context_source.is_file():
359
+ if layer.src.is_absolute() or ".." in str(layer.src):
360
+ rel_path = PurePath(*layer.src.parts[1:])
361
+ dst_path = context_path / "_flyte_abs_context" / rel_path
362
+ else:
363
+ dst_path = context_path / layer.src
364
+
365
+ dst_path.parent.mkdir(parents=True, exist_ok=True)
366
+ abs_path = layer.src.absolute()
367
+
368
+ if layer.src.is_file():
192
369
  # Copy the file
193
- shutil.copy(abs_path, dest_path)
194
- elif layer.context_source.is_dir():
370
+ shutil.copy(abs_path, dst_path)
371
+ elif layer.src.is_dir():
195
372
  # Copy the entire directory
196
- shutil.copytree(abs_path, dest_path)
373
+ shutil.copytree(
374
+ abs_path, dst_path, dirs_exist_ok=True, ignore=shutil.ignore_patterns(*docker_ignore_patterns)
375
+ )
197
376
  else:
198
- raise ValueError(f"Source path is neither file nor directory: {layer.context_source}")
377
+ logger.error(f"Source path not exists: {layer.src}")
378
+ return dockerfile
199
379
 
200
380
  # Add a copy command to the dockerfile
201
- dockerfile += f"\nCOPY {abs_path.name} {image_dest_path}\n"
202
-
381
+ dockerfile += f"\nCOPY {dst_path.relative_to(context_path)} {layer.dst}\n"
203
382
  return dockerfile
204
383
 
205
384
 
206
385
  class CommandsHandler:
207
386
  @staticmethod
208
- async def handle(layer: Commands, context_path: Path, dockerfile: str) -> str:
387
+ async def handle(layer: Commands, _: Path, dockerfile: str) -> str:
209
388
  # Append raw commands to the dockerfile
389
+ secret_mounts = _get_secret_mounts_layer(layer.secret_mounts)
210
390
  for command in layer.commands:
211
- dockerfile += f"\nRUN {command}\n"
391
+ dockerfile += f"\nRUN {secret_mounts} {command}\n"
212
392
 
213
393
  return dockerfile
214
394
 
215
395
 
216
396
  class WorkDirHandler:
217
397
  @staticmethod
218
- async def handle(layer: WorkDir, context_path: Path, dockerfile: str) -> str:
398
+ async def handle(layer: WorkDir, _: Path, dockerfile: str) -> str:
219
399
  # cd to the workdir
220
400
  dockerfile += f"\nWORKDIR {layer.workdir}\n"
221
401
 
222
402
  return dockerfile
223
403
 
224
404
 
225
- async def _process_layer(layer: Layer, context_path: Path, dockerfile: str) -> str:
405
+ def _get_secret_commands(layers: typing.Tuple[Layer, ...]) -> typing.List[str]:
406
+ commands = []
407
+
408
+ def _get_secret_command(secret: str | Secret) -> typing.List[str]:
409
+ if isinstance(secret, str):
410
+ secret = Secret(key=secret)
411
+ secret_id = hash(secret)
412
+ secret_env_key = "_".join([k.upper() for k in filter(None, (secret.group, secret.key))])
413
+ if os.getenv(secret_env_key):
414
+ return ["--secret", f"id={secret_id},env={secret_env_key}"]
415
+ secret_file_name = "_".join(list(filter(None, (secret.group, secret.key))))
416
+ secret_file_path = f"/etc/secrets/{secret_file_name}"
417
+ if not os.path.exists(secret_file_path):
418
+ raise FileNotFoundError(f"Secret not found in Env Var {secret_env_key} or file {secret_file_path}")
419
+ return ["--secret", f"id={secret_id},src={secret_file_path}"]
420
+
421
+ for layer in layers:
422
+ if isinstance(layer, (PipOption, AptPackages, Commands)):
423
+ if layer.secret_mounts:
424
+ for secret_mount in layer.secret_mounts:
425
+ commands.extend(_get_secret_command(secret_mount))
426
+ return commands
427
+
428
+
429
+ def _get_secret_mounts_layer(secrets: typing.Tuple[str | Secret, ...] | None) -> str:
430
+ if secrets is None:
431
+ return ""
432
+ secret_mounts_layer = ""
433
+ for s in secrets:
434
+ secret = Secret(key=s) if isinstance(s, str) else s
435
+ secret_id = hash(secret)
436
+ if secret.mount:
437
+ secret_mounts_layer += f"--mount=type=secret,id={secret_id},target={secret.mount}"
438
+ elif secret.as_env_var:
439
+ secret_mounts_layer += f"--mount=type=secret,id={secret_id},env={secret.as_env_var}"
440
+ else:
441
+ secret_default_env_key = "_".join(list(filter(None, (secret.group, secret.key))))
442
+ secret_mounts_layer += f"--mount=type=secret,id={secret_id},env={secret_default_env_key}"
443
+
444
+ return secret_mounts_layer
445
+
446
+
447
+ async def _process_layer(
448
+ layer: Layer, context_path: Path, dockerfile: str, docker_ignore_patterns: list[str] = []
449
+ ) -> str:
226
450
  match layer:
451
+ case PythonWheels():
452
+ # Handle Python wheels
453
+ dockerfile = await PythonWheelHandler.handle(layer, context_path, dockerfile)
454
+
455
+ case UVScript():
456
+ # Handle UV script
457
+ from flyte._utils import parse_uv_script_file
458
+
459
+ header = parse_uv_script_file(layer.script)
460
+ if header.dependencies:
461
+ pip = PipPackages(
462
+ packages=_ensure_tuple(header.dependencies) if header.dependencies else None,
463
+ secret_mounts=layer.secret_mounts,
464
+ index_url=layer.index_url,
465
+ extra_args=layer.extra_args,
466
+ pre=layer.pre,
467
+ extra_index_urls=layer.extra_index_urls,
468
+ )
469
+ dockerfile = await PipAndRequirementsHandler.handle(pip, context_path, dockerfile)
470
+ if header.pyprojects:
471
+ # To get the version of the project.
472
+ dockerfile = await AptPackagesHandler.handle(AptPackages(packages=("git",)), context_path, dockerfile)
473
+
474
+ for project_path in header.pyprojects:
475
+ uv_project = UVProject(
476
+ pyproject=Path(project_path) / "pyproject.toml",
477
+ uvlock=Path(project_path) / "uv.lock",
478
+ project_install_mode="install_project",
479
+ secret_mounts=layer.secret_mounts,
480
+ pre=layer.pre,
481
+ extra_args=layer.extra_args,
482
+ )
483
+ dockerfile = await UVProjectHandler.handle(
484
+ uv_project, context_path, dockerfile, docker_ignore_patterns
485
+ )
486
+
227
487
  case Requirements() | PipPackages():
228
488
  # Handle pip packages and requirements
229
489
  dockerfile = await PipAndRequirementsHandler.handle(layer, context_path, dockerfile)
@@ -234,16 +494,28 @@ async def _process_layer(layer: Layer, context_path: Path, dockerfile: str) -> s
234
494
 
235
495
  case UVProject():
236
496
  # Handle UV project
237
- dockerfile = await UVProjectHandler.handle(layer, context_path, dockerfile)
497
+ dockerfile = await UVProjectHandler.handle(layer, context_path, dockerfile, docker_ignore_patterns)
498
+
499
+ case PoetryProject():
500
+ # Handle Poetry project
501
+ dockerfile = await PoetryProjectHandler.handel(layer, context_path, dockerfile, docker_ignore_patterns)
502
+
503
+ case PoetryProject():
504
+ # Handle Poetry project
505
+ dockerfile = await PoetryProjectHandler.handel(layer, context_path, dockerfile, docker_ignore_patterns)
238
506
 
239
507
  case CopyConfig():
240
508
  # Handle local files and folders
241
- dockerfile = await CopyConfigHandler.handle(layer, context_path, dockerfile)
509
+ dockerfile = await CopyConfigHandler.handle(layer, context_path, dockerfile, docker_ignore_patterns)
242
510
 
243
511
  case Commands():
244
512
  # Handle commands
245
513
  dockerfile = await CommandsHandler.handle(layer, context_path, dockerfile)
246
514
 
515
+ case DockerIgnore():
516
+ # Handle dockerignore
517
+ await DockerIgnoreHandler.handle(layer, context_path, dockerfile)
518
+
247
519
  case WorkDir():
248
520
  # Handle workdir
249
521
  dockerfile = await WorkDirHandler.handle(layer, context_path, dockerfile)
@@ -262,21 +534,25 @@ async def _process_layer(layer: Layer, context_path: Path, dockerfile: str) -> s
262
534
  return dockerfile
263
535
 
264
536
 
265
- class DockerImageBuilder:
537
+ class DockerImageBuilder(ImageBuilder):
266
538
  """Image builder using Docker and buildkit."""
267
539
 
268
540
  builder_type: ClassVar = "docker"
269
541
  _builder_name: ClassVar = "flytex"
270
542
 
271
- async def build_image(self, image: Image, dry_run: bool = False) -> str:
272
- if image.is_final:
273
- if image._layers:
274
- raise ValueError("Image is a default image and should already be built")
543
+ def get_checkers(self) -> Optional[typing.List[typing.Type[ImageChecker]]]:
544
+ # Can get a public token for docker.io but ghcr requires a pat, so harder to get the manifest anonymously
545
+ return [LocalDockerCommandImageChecker, LocalPodmanCommandImageChecker, DockerAPIImageChecker]
275
546
 
547
+ async def build_image(self, image: Image, dry_run: bool = False) -> str:
276
548
  if image.dockerfile:
277
549
  # If a dockerfile is provided, use it directly
278
550
  return await self._build_from_dockerfile(image, push=True)
279
551
 
552
+ if len(image._layers) == 0:
553
+ logger.warning("No layers to build, returning the image URI as is.")
554
+ return image.uri
555
+
280
556
  return await self._build_image(
281
557
  image,
282
558
  push=True,
@@ -287,18 +563,30 @@ class DockerImageBuilder:
287
563
  """
288
564
  Build the image from a provided Dockerfile.
289
565
  """
566
+ assert image.dockerfile # for mypy
567
+ await DockerImageBuilder._ensure_buildx_builder()
568
+
290
569
  command = [
291
570
  "docker",
571
+ "buildx",
292
572
  "build",
573
+ "--builder",
574
+ DockerImageBuilder._builder_name,
575
+ "-f",
576
+ str(image.dockerfile),
293
577
  "--tag",
294
578
  f"{image.uri}",
295
579
  "--platform",
296
580
  ",".join(image.platform),
297
- ".",
581
+ str(image.dockerfile.parent.absolute()), # Use the parent directory of the Dockerfile as the context
298
582
  ]
299
583
 
300
584
  if image.registry and push:
301
585
  command.append("--push")
586
+ else:
587
+ command.append("--load")
588
+
589
+ command.extend(_get_secret_commands(layers=image._layers))
302
590
 
303
591
  concat_command = " ".join(command)
304
592
  logger.debug(f"Build command: {concat_command}")
@@ -353,6 +641,7 @@ class DockerImageBuilder:
353
641
  - start from the base image
354
642
  - use python to create a default venv and export variables
355
643
 
644
+
356
645
  Then for the layers
357
646
  - for each layer
358
647
  - find the appropriate layer handler
@@ -374,8 +663,11 @@ class DockerImageBuilder:
374
663
  PYTHON_VERSION=f"{image.python_version[0]}.{image.python_version[1]}",
375
664
  )
376
665
 
666
+ # Get .dockerignore file patterns first
667
+ docker_ignore_patterns = get_and_list_dockerignore(image)
668
+
377
669
  for layer in image._layers:
378
- dockerfile = await _process_layer(layer, tmp_path, dockerfile)
670
+ dockerfile = await _process_layer(layer, tmp_path, dockerfile, docker_ignore_patterns)
379
671
 
380
672
  dockerfile += DOCKER_FILE_BASE_FOOTER.substitute(F_IMG_ID=image.uri)
381
673
 
@@ -393,11 +685,22 @@ class DockerImageBuilder:
393
685
  f"{image.uri}",
394
686
  "--platform",
395
687
  ",".join(image.platform),
396
- "--push" if push else "--load",
397
688
  ]
398
689
 
690
+ cache_from = os.getenv(FLYTE_DOCKER_BUILDER_CACHE_FROM)
691
+ cache_to = os.getenv(FLYTE_DOCKER_BUILDER_CACHE_TO)
692
+ if cache_from and cache_to:
693
+ command[3:3] = [
694
+ f"--cache-from={cache_from}",
695
+ f"--cache-to={cache_to}",
696
+ ]
697
+
399
698
  if image.registry and push:
400
699
  command.append("--push")
700
+ else:
701
+ command.append("--load")
702
+
703
+ command.extend(_get_secret_commands(layers=image._layers))
401
704
  command.append(tmp_dir)
402
705
 
403
706
  concat_command = " ".join(command)
@@ -411,6 +714,10 @@ class DockerImageBuilder:
411
714
  else:
412
715
  click.secho(f"Run command: {concat_command} ", fg="blue")
413
716
 
414
- await asyncio.to_thread(subprocess.run, command, check=True)
717
+ try:
718
+ await asyncio.to_thread(subprocess.run, command, check=True)
719
+ except subprocess.CalledProcessError as e:
720
+ logger.error(f"Failed to build image: {e}")
721
+ raise RuntimeError(f"Failed to build image: {e}")
415
722
 
416
723
  return image.uri