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/_utils/__init__.py CHANGED
@@ -4,17 +4,28 @@ Internal utility functions.
4
4
  Except for logging, modules in this package should not depend on any other part of the repo.
5
5
  """
6
6
 
7
+ from .async_cache import AsyncLRUCache
7
8
  from .coro_management import run_coros
8
9
  from .file_handling import filehash_update, update_hasher_for_source
9
- from .helpers import get_cwd_editable_install
10
+ from .helpers import get_cwd_editable_install, str2bool
10
11
  from .lazy_module import lazy_module
12
+ from .module_loader import adjust_sys_path, load_python_modules
13
+ from .org_discovery import hostname_from_url, org_from_endpoint, sanitize_endpoint
11
14
  from .uv_script_parser import parse_uv_script_file
12
15
 
13
16
  __all__ = [
17
+ "AsyncLRUCache",
18
+ "adjust_sys_path",
19
+ "description_parser",
14
20
  "filehash_update",
15
21
  "get_cwd_editable_install",
22
+ "hostname_from_url",
16
23
  "lazy_module",
24
+ "load_python_modules",
25
+ "org_from_endpoint",
17
26
  "parse_uv_script_file",
18
27
  "run_coros",
28
+ "sanitize_endpoint",
29
+ "str2bool",
19
30
  "update_hasher_for_source",
20
31
  ]
flyte/_utils/asyn.py CHANGED
@@ -9,6 +9,8 @@ async def async_add(a: int, b: int) -> int:
9
9
  result = run_sync(async_add, a=10, b=12)
10
10
  """
11
11
 
12
+ from __future__ import annotations
13
+
12
14
  import asyncio
13
15
  import atexit
14
16
  import functools
@@ -88,7 +90,7 @@ class _TaskRunner:
88
90
 
89
91
 
90
92
  class _AsyncLoopManager:
91
- def __init__(self):
93
+ def __init__(self: _AsyncLoopManager):
92
94
  self._runner_map: dict[str, _TaskRunner] = {}
93
95
 
94
96
  def run_sync(self, coro_func: Callable[..., Awaitable[T]], *args, **kwargs) -> T:
@@ -0,0 +1,139 @@
1
+ import asyncio
2
+ import time
3
+ from collections import OrderedDict
4
+ from typing import Awaitable, Callable, Dict, Generic, Optional, TypeVar
5
+
6
+ K = TypeVar("K")
7
+ V = TypeVar("V")
8
+
9
+
10
+ class AsyncLRUCache(Generic[K, V]):
11
+ """
12
+ A high-performance async-compatible LRU cache.
13
+
14
+ Examples:
15
+ ```python
16
+ # Create a cache instance
17
+ cache = AsyncLRUCache[str, dict](maxsize=100)
18
+
19
+ async def fetch_data(user_id: str) -> dict:
20
+ # Define the expensive operation as a local function
21
+ async def get_user_data():
22
+ await asyncio.sleep(1) # Simulating network/DB delay
23
+ return {"id": user_id, "name": f"User {user_id}"}
24
+
25
+ # Use the cache
26
+ return await cache.get(f"user:{user_id}", get_user_data)
27
+ ```
28
+ This cache can be used from async coroutines and handles concurrent access safely.
29
+ """
30
+
31
+ def __init__(self, maxsize: int = 128, ttl: Optional[float] = None):
32
+ """
33
+ Initialize the async LRU cache.
34
+
35
+ Args:
36
+ maxsize: Maximum number of items to keep in the cache
37
+ ttl: Time-to-live for cache entries in seconds, or None for no expiration
38
+ """
39
+ self._cache: OrderedDict[K, tuple[V, float]] = OrderedDict()
40
+ self._maxsize = maxsize
41
+ self._ttl = ttl
42
+ self._locks: Dict[K, asyncio.Lock] = {}
43
+ self._access_lock = asyncio.Lock()
44
+
45
+ async def get(self, key: K, value_func: Callable[[], V | Awaitable[V]]) -> V:
46
+ """
47
+ Get a value from the cache, computing it if necessary.
48
+
49
+ Args:
50
+ key: The cache key
51
+ value_func: Function or coroutine to compute the value if not cached
52
+
53
+ Returns:
54
+ The cached or computed value
55
+ """
56
+ # Fast path: check if key exists and is not expired
57
+ if key in self._cache:
58
+ value, timestamp = self._cache[key]
59
+ if self._ttl is None or time.time() - timestamp < self._ttl:
60
+ # Move the accessed item to the end (most recently used)
61
+ async with self._access_lock:
62
+ self._cache.move_to_end(key)
63
+ return value
64
+
65
+ # Slow path: compute the value
66
+ # Get or create a lock for this key to prevent redundant computation
67
+ async with self._access_lock:
68
+ lock = self._locks.get(key)
69
+ if lock is None:
70
+ lock = asyncio.Lock()
71
+ self._locks[key] = lock
72
+
73
+ async with lock:
74
+ # Check again in case another coroutine computed the value while we waited
75
+ if key in self._cache:
76
+ value, timestamp = self._cache[key]
77
+ if self._ttl is None or time.time() - timestamp < self._ttl:
78
+ async with self._access_lock:
79
+ self._cache.move_to_end(key)
80
+ return value
81
+
82
+ # Compute the value
83
+ if asyncio.iscoroutinefunction(value_func):
84
+ value = await value_func()
85
+ else:
86
+ value = value_func() # type: ignore
87
+
88
+ # Store in cache
89
+ async with self._access_lock:
90
+ self._cache[key] = (value, time.time())
91
+ # Evict least recently used items if needed
92
+ while len(self._cache) > self._maxsize:
93
+ self._cache.popitem(last=False)
94
+ # Clean up the lock
95
+ self._locks.pop(key, None)
96
+
97
+ return value
98
+
99
+ async def set(self, key: K, value: V) -> None:
100
+ """
101
+ Explicitly set a value in the cache.
102
+
103
+ Args:
104
+ key: The cache key
105
+ value: The value to cache
106
+ """
107
+ async with self._access_lock:
108
+ self._cache[key] = (value, time.time())
109
+ # Evict least recently used items if needed
110
+ while len(self._cache) > self._maxsize:
111
+ self._cache.popitem(last=False)
112
+
113
+ async def invalidate(self, key: K) -> None:
114
+ """Remove a specific key from the cache."""
115
+ async with self._access_lock:
116
+ self._cache.pop(key, None)
117
+
118
+ async def clear(self) -> None:
119
+ """Clear the entire cache."""
120
+ async with self._access_lock:
121
+ self._cache.clear()
122
+ self._locks.clear()
123
+
124
+ async def contains(self, key: K) -> bool:
125
+ """Check if a key exists in the cache and is not expired."""
126
+ if key not in self._cache:
127
+ return False
128
+
129
+ if self._ttl is None:
130
+ return True
131
+
132
+ _, timestamp = self._cache[key]
133
+ return time.time() - timestamp < self._ttl
134
+
135
+
136
+ # Example usage:
137
+ """
138
+
139
+ """
@@ -5,21 +5,22 @@ import typing
5
5
  async def run_coros(*coros: typing.Coroutine, return_when: str = asyncio.FIRST_COMPLETED):
6
6
  """
7
7
  Run a list of coroutines concurrently and wait for the first one to finish or exit.
8
- When the first one finishes, cancel all other tasks.
8
+ When the first one finishes, cancel all other tasks. This helper function does not propagate CancelledError, but
9
+ will cancel pending tasks.
9
10
 
10
11
  :param coros:
11
12
  :param return_when:
12
13
  :return:
13
14
  """
14
- tasks: typing.List[asyncio.Task[typing.Never]] = [asyncio.create_task(c) for c in coros]
15
+ # tasks: typing.List[asyncio.Task[typing.Never]] = [asyncio.create_task(c) for c in coros] # Python 3.11+
16
+ tasks: typing.List[asyncio.Task] = [asyncio.create_task(c) for c in coros]
15
17
  done, pending = await asyncio.wait(tasks, return_when=return_when)
16
18
 
17
19
  for t in pending: # type: asyncio.Task
18
20
  t.cancel() # Cancel all tasks that didn't finish first
19
21
 
22
+ # Check for exceptions only in the completed tasks
20
23
  for t in done:
21
24
  err = t.exception()
22
25
  if err:
23
26
  raise err
24
- else:
25
- print(f"Task result: {t.result()}")
@@ -0,0 +1,19 @@
1
+ def parse_description(description: str, max_length: int = 255):
2
+ """
3
+ Parse and truncate a description string to fit within a maximum length.
4
+
5
+ If the description exceeds max_length, it will be truncated and suffixed with
6
+ "...(tr.)" to indicate truncation.
7
+
8
+ Args:
9
+ description: The description string to parse.
10
+ max_length: Maximum allowed length for the description. Defaults to 255.
11
+
12
+ Returns:
13
+ The parsed description string, truncated if necessary.
14
+ """
15
+ if len(description) <= max_length:
16
+ return description
17
+ if max_length >= 8:
18
+ return description[: max_length - 8] + "...(tr.)"
19
+ return description[:max_length]
@@ -0,0 +1,173 @@
1
+ """Helper functions for creating Docker registry credentials for image pull secrets."""
2
+
3
+ import base64
4
+ import json
5
+ import logging
6
+ import os
7
+ import subprocess
8
+ from pathlib import Path
9
+ from typing import Any
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+ _CONFIG_JSON = "config.json"
14
+ _DEFAULT_CONFIG_PATH = f"~/.docker/{_CONFIG_JSON}"
15
+ _CRED_HELPERS = "credHelpers"
16
+ _CREDS_STORE = "credsStore"
17
+
18
+
19
+ def _load_docker_config(config_path: str | Path | None = None) -> dict[str, Any]:
20
+ """
21
+ Load Docker config from specified path.
22
+
23
+ Args:
24
+ config_path: Path to Docker config file. If None, uses DOCKER_CONFIG env var
25
+ or defaults to ~/.docker/config.json
26
+
27
+ Returns:
28
+ Dictionary containing Docker config
29
+
30
+ Raises:
31
+ FileNotFoundError: If the config file does not exist
32
+ json.JSONDecodeError: If the config file is not valid JSON
33
+ """
34
+ if not config_path:
35
+ docker_config_env = os.environ.get("DOCKER_CONFIG")
36
+ if docker_config_env:
37
+ config_path = Path(docker_config_env) / _CONFIG_JSON
38
+ else:
39
+ config_path = Path(_DEFAULT_CONFIG_PATH).expanduser()
40
+ else:
41
+ config_path = Path(config_path).expanduser()
42
+
43
+ with open(config_path) as f:
44
+ return json.load(f)
45
+
46
+
47
+ def _get_credential_helper(config: dict[str, Any], registry: str | None = None) -> str | None:
48
+ """Get credential helper for registry or global default."""
49
+ if registry and _CRED_HELPERS in config and registry in config[_CRED_HELPERS]:
50
+ return config[_CRED_HELPERS].get(registry)
51
+ return config.get(_CREDS_STORE)
52
+
53
+
54
+ def _get_credentials_from_helper(helper: str, registry: str) -> tuple[str, str] | None:
55
+ """
56
+ Get credentials from system credential helper.
57
+
58
+ Args:
59
+ helper: Name of the credential helper (e.g., "osxkeychain", "wincred")
60
+ registry: Registry hostname to get credentials for
61
+
62
+ Returns:
63
+ Tuple of (username, password) or None if credentials cannot be retrieved
64
+ """
65
+ helper_cmd = f"docker-credential-{helper}"
66
+
67
+ try:
68
+ process = subprocess.Popen(
69
+ [helper_cmd, "get"],
70
+ stdin=subprocess.PIPE,
71
+ stdout=subprocess.PIPE,
72
+ stderr=subprocess.PIPE,
73
+ text=True,
74
+ )
75
+ output, error = process.communicate(input=registry)
76
+
77
+ if process.returncode != 0:
78
+ logger.error(f"Credential helper error: {error}")
79
+ return None
80
+
81
+ creds = json.loads(output)
82
+ return creds.get("Username"), creds.get("Secret")
83
+ except FileNotFoundError:
84
+ logger.error(f"Credential helper {helper_cmd} not found in PATH")
85
+ return None
86
+ except Exception as e:
87
+ logger.error(f"Error getting credentials: {e!s}")
88
+ return None
89
+
90
+
91
+ def create_dockerconfigjson_from_config(
92
+ registries: list[str] | None = None,
93
+ docker_config_path: str | Path | None = None,
94
+ ) -> str:
95
+ """
96
+ Create a dockerconfigjson string from existing Docker config.
97
+
98
+ This function extracts Docker registry credentials from the user's Docker config file
99
+ and creates a JSON string containing only the credentials for the specified registries.
100
+ It handles credentials stored directly in the config file as well as those managed by
101
+ credential helpers.
102
+
103
+ Args:
104
+ registries: List of registries to extract credentials for. If None, all registries
105
+ from the config will be used.
106
+ docker_config_path: Path to the Docker config file. If None, the function will look
107
+ for the config file in the standard locations.
108
+
109
+ Returns:
110
+ JSON string in dockerconfigjson format: {"auths": {"registry": {"auth": "..."}}}
111
+
112
+ Raises:
113
+ FileNotFoundError: If Docker config file cannot be found
114
+ ValueError: If no credentials can be extracted
115
+ """
116
+ config = _load_docker_config(docker_config_path)
117
+
118
+ # Create new config structure with empty auths
119
+ new_config: dict[str, Any] = {"auths": {}}
120
+
121
+ # Use specified registries or all from config
122
+ target_registries = registries or list(config.get("auths", {}).keys())
123
+
124
+ if not target_registries:
125
+ raise ValueError("No registries found in Docker config and none specified")
126
+
127
+ for registry in target_registries:
128
+ registry_config = config.get("auths", {}).get(registry, {})
129
+ if registry_config.get("auth"):
130
+ # Direct auth token exists
131
+ new_config["auths"][registry] = {"auth": registry_config["auth"]}
132
+ else:
133
+ # Try to get credentials from helper
134
+ helper = _get_credential_helper(config, registry)
135
+ if helper:
136
+ creds = _get_credentials_from_helper(helper, registry)
137
+ if creds:
138
+ username, password = creds
139
+ auth_string = f"{username}:{password}"
140
+ new_config["auths"][registry] = {"auth": base64.b64encode(auth_string.encode()).decode()}
141
+ else:
142
+ logger.warning(f"Could not retrieve credentials for {registry} from credential helper")
143
+ else:
144
+ logger.warning(f"No credentials found for {registry}")
145
+
146
+ if not new_config["auths"]:
147
+ raise ValueError(f"No credentials could be extracted for registries: {', '.join(target_registries)}")
148
+
149
+ return json.dumps(new_config)
150
+
151
+
152
+ def create_dockerconfigjson_from_credentials(
153
+ registry: str,
154
+ username: str,
155
+ password: str,
156
+ ) -> str:
157
+ """
158
+ Create a dockerconfigjson string from explicit credentials.
159
+
160
+ Args:
161
+ registry: Registry hostname (e.g., "ghcr.io", "docker.io")
162
+ username: Username or token name for the registry
163
+ password: Password or access token for the registry
164
+
165
+ Returns:
166
+ JSON string in dockerconfigjson format: {"auths": {"registry": {"auth": "..."}}}
167
+ """
168
+ auth_string = f"{username}:{password}"
169
+ auth_token = base64.b64encode(auth_string.encode()).decode()
170
+
171
+ config = {"auths": {registry: {"auth": auth_token}}}
172
+
173
+ return json.dumps(config)
flyte/_utils/helpers.py CHANGED
@@ -1,6 +1,7 @@
1
1
  import os
2
2
  import string
3
3
  import typing
4
+ from contextlib import contextmanager
4
5
  from pathlib import Path
5
6
 
6
7
 
@@ -51,7 +52,33 @@ def base36_encode(byte_data: bytes) -> str:
51
52
  return "".join(reversed(base36))
52
53
 
53
54
 
54
- # does not work at all in the setuptools case. see old flytekit editable installs
55
+ def _iter_editable():
56
+ """
57
+ Yield (project_name, source_path) for every editable distribution
58
+ visible to the current interpreter
59
+ """
60
+ import json
61
+ import pathlib
62
+ from importlib.metadata import distributions
63
+
64
+ for dist in distributions():
65
+ # PEP-610 / PEP-660 (preferred, wheel-style editables)
66
+ direct = dist.read_text("direct_url.json")
67
+ if direct:
68
+ data = json.loads(direct)
69
+ if data.get("dir_info", {}).get("editable"): # spec key
70
+ # todo: will need testing on windows
71
+ yield dist.metadata["Name"], pathlib.Path(data["url"][7:]) # strip file://
72
+ continue
73
+
74
+ # Legacy setuptools-develop / pip-e (egg-link)
75
+ for file in dist.files or (): # importlib.metadata 3.8+
76
+ if file.suffix == ".egg-link":
77
+ with open(dist.locate_file(file), "r") as f:
78
+ line = f.readline()
79
+ yield dist.metadata["Name"], pathlib.Path(line.strip())
80
+
81
+
55
82
  def get_cwd_editable_install() -> typing.Optional[Path]:
56
83
  """
57
84
  This helper function is incomplete since it hasn't been tested with all the package managers out there,
@@ -64,28 +91,13 @@ def get_cwd_editable_install() -> typing.Optional[Path]:
64
91
 
65
92
  :return:
66
93
  """
67
- import site
68
94
 
69
95
  from flyte._logging import logger
70
96
 
71
- egg_links = [Path(p) for p in Path(site.getsitepackages()[0]).glob("*.egg-link")]
72
- pth_files = [Path(p) for p in Path(site.getsitepackages()[0]).glob("*.pth")]
73
-
74
- if not egg_links and not pth_files:
75
- logger.debug("No editable installs found.")
76
- return None
77
-
78
97
  editable_installs = []
79
- egg_links.extend(pth_files)
80
- for file in egg_links:
81
- with open(file, "r") as f:
82
- line = f.readline()
83
- if line:
84
- # Check if the first line is a directory
85
- p = Path(line)
86
- if p.is_dir():
87
- editable_installs.append(p)
88
- logger.debug(f"Editable installs: {editable_installs}")
98
+ for name, path in _iter_editable():
99
+ logger.debug(f"Detected editable install: {name} at {path}")
100
+ editable_installs.append(path)
89
101
 
90
102
  # check to see if the current working directory is in any of the editable installs
91
103
  # including if the current folder is the root folder, one level up from the src and contains
@@ -106,3 +118,17 @@ def get_cwd_editable_install() -> typing.Optional[Path]:
106
118
  return install # note we want the install folder, not the parent
107
119
 
108
120
  return None
121
+
122
+
123
+ @contextmanager
124
+ def _selector_policy():
125
+ import asyncio
126
+
127
+ original_policy = asyncio.get_event_loop_policy()
128
+ try:
129
+ if os.name == "nt" and hasattr(asyncio, "WindowsSelectorEventLoopPolicy"):
130
+ asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
131
+
132
+ yield
133
+ finally:
134
+ asyncio.set_event_loop_policy(original_policy)
@@ -0,0 +1,123 @@
1
+ import importlib.util
2
+ import os
3
+ import sys
4
+ from pathlib import Path
5
+ from types import ModuleType
6
+ from typing import List, Tuple
7
+
8
+ import flyte.errors
9
+ from flyte._constants import FLYTE_SYS_PATH
10
+ from flyte._logging import logger
11
+
12
+
13
+ def load_python_modules(
14
+ path: Path, root_dir: Path, recursive: bool = False
15
+ ) -> Tuple[List[ModuleType], List[Tuple[Path, str]]]:
16
+ """
17
+ Load all Python modules from a path and return list of loaded module names.
18
+
19
+ :param path: File or directory path
20
+ :param root_dir: Root directory to search for modules
21
+ :param recursive: If True, load modules recursively from subdirectories
22
+ :return: List of loaded module names, and list of file paths that failed to load
23
+ """
24
+ from rich.progress import BarColumn, Progress, TextColumn, TimeElapsedColumn, TimeRemainingColumn
25
+
26
+ loaded_modules = []
27
+ failed_paths = []
28
+
29
+ if path.is_file() and path.suffix == ".py":
30
+ rel_path = path.resolve().relative_to(root_dir)
31
+ mod = (".".join(rel_path.parts))[:-3]
32
+ imported_module = importlib.import_module(mod)
33
+ loaded_modules.append(imported_module)
34
+
35
+ elif path.is_dir():
36
+ # Directory case - find all Python files
37
+ pattern = "**/*.py" if recursive else "*.py"
38
+ python_files = list(path.glob(pattern))
39
+
40
+ # Filter out __init__.py files
41
+ python_files = [f for f in python_files if f.name != "__init__.py"]
42
+
43
+ if not python_files:
44
+ # If no .py files found, try importing as a module
45
+ try:
46
+ rel_path = path.resolve().relative_to(root_dir)
47
+ mod = ".".join(rel_path.parts)
48
+ imported_module = importlib.import_module(mod)
49
+ loaded_modules.append(imported_module)
50
+ except (ValueError, ModuleNotFoundError):
51
+ pass
52
+ else:
53
+ with Progress(
54
+ TextColumn("[progress.description]{task.description}"),
55
+ BarColumn(),
56
+ "[progress.percentage]{task.percentage:>3.0f}%",
57
+ TimeElapsedColumn(),
58
+ TimeRemainingColumn(),
59
+ TextColumn("• {task.fields[current_file]}"),
60
+ ) as progress:
61
+ task = progress.add_task(f"Loading {len(python_files)} files", total=len(python_files), current_file="")
62
+
63
+ for file_path in python_files:
64
+ progress.update(task, advance=1, current_file=file_path.name)
65
+
66
+ try:
67
+ rel_path = file_path.resolve().relative_to(root_dir)
68
+ mod = (".".join(rel_path.parts))[:-3]
69
+ imported_module = importlib.import_module(mod)
70
+ loaded_modules.append(imported_module)
71
+ except flyte.errors.ModuleLoadError as e:
72
+ failed_paths.append((file_path, str(e)))
73
+
74
+ progress.update(task, current_file="[green]Done[/green]")
75
+
76
+ return loaded_modules, failed_paths
77
+
78
+
79
+ def _load_module_from_file(file_path: Path) -> str | None:
80
+ """
81
+ Load a Python module from a file path.
82
+
83
+ :param file_path: Path to the Python file
84
+ :return: Module name if successfully loaded, None otherwise
85
+ """
86
+ try:
87
+ # Use the file stem as module name
88
+ module_name = file_path.stem
89
+
90
+ # Load the module specification
91
+ spec = importlib.util.spec_from_file_location(module_name, file_path)
92
+ if spec is None or spec.loader is None:
93
+ return None
94
+
95
+ # Create and execute the module
96
+ module = importlib.util.module_from_spec(spec)
97
+ sys.modules[module_name] = module
98
+ module_path = os.path.dirname(os.path.abspath(file_path))
99
+ sys.path.append(module_path)
100
+ spec.loader.exec_module(module)
101
+
102
+ return module_name
103
+
104
+ except Exception as e:
105
+ raise flyte.errors.ModuleLoadError(f"Failed to load module from {file_path}: {e}") from e
106
+
107
+
108
+ def adjust_sys_path(additional_paths: List[str] | None = None):
109
+ """
110
+ Adjust sys.path to include local sys.path entries under the root directory.
111
+ """
112
+ if "." not in sys.path or os.getcwd() not in sys.path:
113
+ sys.path.insert(0, ".")
114
+ logger.info(f"Added {os.getcwd()} to sys.path")
115
+ for p in os.environ.get(FLYTE_SYS_PATH, "").split(":"):
116
+ if p and p not in sys.path:
117
+ sys.path.insert(0, p)
118
+ logger.info(f"Added {p} to sys.path")
119
+ if additional_paths:
120
+ for p in additional_paths:
121
+ if p and p not in sys.path:
122
+ sys.path.insert(0, p)
123
+ logger.info(f"Added {p} to sys.path")
@@ -0,0 +1,57 @@
1
+ def hostname_from_url(url: str) -> str:
2
+ """Parse a URL and return the hostname part."""
3
+
4
+ # Handle dns:/// format specifically (gRPC convention)
5
+ if url.startswith("dns:///"):
6
+ return url[7:] # Skip the "dns:///" prefix
7
+
8
+ # Handle standard URL formats
9
+ import urllib.parse
10
+
11
+ parsed = urllib.parse.urlparse(url)
12
+ return parsed.netloc or parsed.path.lstrip("/").rsplit("/")[0]
13
+
14
+
15
+ def org_from_endpoint(endpoint: str | None) -> str | None:
16
+ """
17
+ Extracts the organization from the endpoint URL. The organization is assumed to be the first part of the domain.
18
+ This is temporary until we have a proper organization discovery mechanism through APIs.
19
+
20
+ :param endpoint: The endpoint URL
21
+ :return: The organization name or None if not found
22
+ """
23
+ if not endpoint:
24
+ return None
25
+
26
+ hostname = hostname_from_url(endpoint)
27
+ domain_parts = hostname.split(".")
28
+ if len(domain_parts) > 2:
29
+ # Assuming the organization is the first part of the domain
30
+ return domain_parts[0]
31
+ return None
32
+
33
+
34
+ def sanitize_endpoint(endpoint: str | None) -> str | None:
35
+ """
36
+ Sanitize the endpoint URL by ensuring it has a valid scheme.
37
+ :param endpoint: The endpoint URL to sanitize
38
+ :return: Sanitized endpoint URL or None if the input was None
39
+ """
40
+ if not endpoint:
41
+ return None
42
+ if "://" not in endpoint:
43
+ endpoint = f"dns:///{endpoint}"
44
+ else:
45
+ if endpoint.startswith("https://"):
46
+ # If the endpoint starts with dns:///, we assume it's a gRPC endpoint
47
+ endpoint = f"dns:///{endpoint[8:]}"
48
+ elif endpoint.startswith("http://"):
49
+ # If the endpoint starts with http://, we assume it's a REST endpoint
50
+ endpoint = f"dns:///{endpoint[7:]}"
51
+ elif not endpoint.startswith("dns:///"):
52
+ raise RuntimeError(
53
+ f"Invalid endpoint {endpoint}, expected format is "
54
+ f"dns:///<hostname> or https://<hostname> or http://<hostname>"
55
+ )
56
+ endpoint = endpoint.removesuffix("/")
57
+ return endpoint