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.
- flyte/__init__.py +83 -30
- flyte/_bin/connect.py +61 -0
- flyte/_bin/debug.py +38 -0
- flyte/_bin/runtime.py +87 -19
- flyte/_bin/serve.py +351 -0
- flyte/_build.py +3 -2
- flyte/_cache/cache.py +6 -5
- flyte/_cache/local_cache.py +216 -0
- flyte/_code_bundle/_ignore.py +31 -5
- flyte/_code_bundle/_packaging.py +42 -11
- flyte/_code_bundle/_utils.py +57 -34
- flyte/_code_bundle/bundle.py +130 -27
- flyte/_constants.py +1 -0
- flyte/_context.py +21 -5
- flyte/_custom_context.py +73 -0
- flyte/_debug/constants.py +37 -0
- flyte/_debug/utils.py +17 -0
- flyte/_debug/vscode.py +315 -0
- flyte/_deploy.py +396 -75
- flyte/_deployer.py +109 -0
- flyte/_environment.py +94 -11
- flyte/_excepthook.py +37 -0
- flyte/_group.py +2 -1
- flyte/_hash.py +1 -16
- flyte/_image.py +544 -234
- flyte/_initialize.py +443 -294
- flyte/_interface.py +40 -5
- flyte/_internal/controllers/__init__.py +22 -8
- flyte/_internal/controllers/_local_controller.py +159 -35
- flyte/_internal/controllers/_trace.py +18 -10
- flyte/_internal/controllers/remote/__init__.py +38 -9
- flyte/_internal/controllers/remote/_action.py +82 -12
- flyte/_internal/controllers/remote/_client.py +6 -2
- flyte/_internal/controllers/remote/_controller.py +290 -64
- flyte/_internal/controllers/remote/_core.py +155 -95
- flyte/_internal/controllers/remote/_informer.py +40 -20
- flyte/_internal/controllers/remote/_service_protocol.py +2 -2
- flyte/_internal/imagebuild/__init__.py +2 -10
- flyte/_internal/imagebuild/docker_builder.py +391 -84
- flyte/_internal/imagebuild/image_builder.py +111 -55
- flyte/_internal/imagebuild/remote_builder.py +409 -0
- flyte/_internal/imagebuild/utils.py +79 -0
- flyte/_internal/resolvers/_app_env_module.py +92 -0
- flyte/_internal/resolvers/_task_module.py +5 -38
- flyte/_internal/resolvers/app_env.py +26 -0
- flyte/_internal/resolvers/common.py +8 -1
- flyte/_internal/resolvers/default.py +2 -2
- flyte/_internal/runtime/convert.py +322 -33
- flyte/_internal/runtime/entrypoints.py +106 -18
- flyte/_internal/runtime/io.py +71 -23
- flyte/_internal/runtime/resources_serde.py +21 -7
- flyte/_internal/runtime/reuse.py +125 -0
- flyte/_internal/runtime/rusty.py +196 -0
- flyte/_internal/runtime/task_serde.py +239 -66
- flyte/_internal/runtime/taskrunner.py +48 -8
- flyte/_internal/runtime/trigger_serde.py +162 -0
- flyte/_internal/runtime/types_serde.py +7 -16
- flyte/_keyring/file.py +115 -0
- flyte/_link.py +30 -0
- flyte/_logging.py +241 -42
- flyte/_map.py +312 -0
- flyte/_metrics.py +59 -0
- flyte/_module.py +74 -0
- flyte/_pod.py +30 -0
- flyte/_resources.py +296 -33
- flyte/_retry.py +1 -7
- flyte/_reusable_environment.py +72 -7
- flyte/_run.py +461 -132
- flyte/_secret.py +47 -11
- flyte/_serve.py +333 -0
- flyte/_task.py +245 -56
- flyte/_task_environment.py +219 -97
- flyte/_task_plugins.py +47 -0
- flyte/_tools.py +8 -8
- flyte/_trace.py +15 -24
- flyte/_trigger.py +1027 -0
- flyte/_utils/__init__.py +12 -1
- flyte/_utils/asyn.py +3 -1
- flyte/_utils/async_cache.py +139 -0
- flyte/_utils/coro_management.py +5 -4
- flyte/_utils/description_parser.py +19 -0
- flyte/_utils/docker_credentials.py +173 -0
- flyte/_utils/helpers.py +45 -19
- flyte/_utils/module_loader.py +123 -0
- flyte/_utils/org_discovery.py +57 -0
- flyte/_utils/uv_script_parser.py +8 -1
- flyte/_version.py +16 -3
- flyte/app/__init__.py +27 -0
- flyte/app/_app_environment.py +362 -0
- flyte/app/_connector_environment.py +40 -0
- flyte/app/_deploy.py +130 -0
- flyte/app/_parameter.py +343 -0
- flyte/app/_runtime/__init__.py +3 -0
- flyte/app/_runtime/app_serde.py +383 -0
- flyte/app/_types.py +113 -0
- flyte/app/extras/__init__.py +9 -0
- flyte/app/extras/_auth_middleware.py +217 -0
- flyte/app/extras/_fastapi.py +93 -0
- flyte/app/extras/_model_loader/__init__.py +3 -0
- flyte/app/extras/_model_loader/config.py +7 -0
- flyte/app/extras/_model_loader/loader.py +288 -0
- flyte/cli/__init__.py +12 -0
- flyte/cli/_abort.py +28 -0
- flyte/cli/_build.py +114 -0
- flyte/cli/_common.py +493 -0
- flyte/cli/_create.py +371 -0
- flyte/cli/_delete.py +45 -0
- flyte/cli/_deploy.py +401 -0
- flyte/cli/_gen.py +316 -0
- flyte/cli/_get.py +446 -0
- flyte/cli/_option.py +33 -0
- {union/_cli → flyte/cli}/_params.py +152 -153
- flyte/cli/_plugins.py +209 -0
- flyte/cli/_prefetch.py +292 -0
- flyte/cli/_run.py +690 -0
- flyte/cli/_serve.py +338 -0
- flyte/cli/_update.py +86 -0
- flyte/cli/_user.py +20 -0
- flyte/cli/main.py +246 -0
- flyte/config/__init__.py +3 -0
- flyte/config/_config.py +248 -0
- flyte/config/_internal.py +73 -0
- flyte/config/_reader.py +225 -0
- flyte/connectors/__init__.py +11 -0
- flyte/connectors/_connector.py +330 -0
- flyte/connectors/_server.py +194 -0
- flyte/connectors/utils.py +159 -0
- flyte/errors.py +134 -2
- flyte/extend.py +24 -0
- flyte/extras/_container.py +69 -56
- flyte/git/__init__.py +3 -0
- flyte/git/_config.py +279 -0
- flyte/io/__init__.py +8 -1
- flyte/io/{structured_dataset → _dataframe}/__init__.py +32 -30
- flyte/io/{structured_dataset → _dataframe}/basic_dfs.py +75 -68
- flyte/io/{structured_dataset/structured_dataset.py → _dataframe/dataframe.py} +207 -242
- flyte/io/_dir.py +575 -113
- flyte/io/_file.py +587 -141
- flyte/io/_hashing_io.py +342 -0
- flyte/io/extend.py +7 -0
- flyte/models.py +635 -0
- flyte/prefetch/__init__.py +22 -0
- flyte/prefetch/_hf_model.py +563 -0
- flyte/remote/__init__.py +14 -3
- flyte/remote/_action.py +879 -0
- flyte/remote/_app.py +346 -0
- flyte/remote/_auth_metadata.py +42 -0
- flyte/remote/_client/_protocols.py +62 -4
- flyte/remote/_client/auth/_auth_utils.py +19 -0
- flyte/remote/_client/auth/_authenticators/base.py +8 -2
- flyte/remote/_client/auth/_authenticators/device_code.py +4 -5
- flyte/remote/_client/auth/_authenticators/factory.py +4 -0
- flyte/remote/_client/auth/_authenticators/passthrough.py +79 -0
- flyte/remote/_client/auth/_authenticators/pkce.py +17 -18
- flyte/remote/_client/auth/_channel.py +47 -18
- flyte/remote/_client/auth/_client_config.py +5 -3
- flyte/remote/_client/auth/_keyring.py +15 -2
- flyte/remote/_client/auth/_token_client.py +3 -3
- flyte/remote/_client/controlplane.py +206 -18
- flyte/remote/_common.py +66 -0
- flyte/remote/_data.py +107 -22
- flyte/remote/_logs.py +116 -33
- flyte/remote/_project.py +21 -19
- flyte/remote/_run.py +164 -631
- flyte/remote/_secret.py +72 -29
- flyte/remote/_task.py +387 -46
- flyte/remote/_trigger.py +368 -0
- flyte/remote/_user.py +43 -0
- flyte/report/_report.py +10 -6
- flyte/storage/__init__.py +13 -1
- flyte/storage/_config.py +237 -0
- flyte/storage/_parallel_reader.py +289 -0
- flyte/storage/_storage.py +268 -59
- flyte/syncify/__init__.py +56 -0
- flyte/syncify/_api.py +414 -0
- flyte/types/__init__.py +39 -0
- flyte/types/_interface.py +22 -7
- flyte/{io/pickle/transformer.py → types/_pickle.py} +37 -9
- flyte/types/_string_literals.py +8 -9
- flyte/types/_type_engine.py +230 -129
- flyte/types/_utils.py +1 -1
- flyte-2.0.0b46.data/scripts/debug.py +38 -0
- flyte-2.0.0b46.data/scripts/runtime.py +194 -0
- flyte-2.0.0b46.dist-info/METADATA +352 -0
- flyte-2.0.0b46.dist-info/RECORD +221 -0
- flyte-2.0.0b46.dist-info/entry_points.txt +8 -0
- flyte-2.0.0b46.dist-info/licenses/LICENSE +201 -0
- flyte/_api_commons.py +0 -3
- flyte/_cli/_common.py +0 -287
- flyte/_cli/_create.py +0 -42
- flyte/_cli/_delete.py +0 -23
- flyte/_cli/_deploy.py +0 -140
- flyte/_cli/_get.py +0 -235
- flyte/_cli/_run.py +0 -152
- flyte/_cli/main.py +0 -72
- flyte/_datastructures.py +0 -342
- flyte/_internal/controllers/pbhash.py +0 -39
- flyte/_protos/common/authorization_pb2.py +0 -66
- flyte/_protos/common/authorization_pb2.pyi +0 -108
- flyte/_protos/common/authorization_pb2_grpc.py +0 -4
- flyte/_protos/common/identifier_pb2.py +0 -71
- flyte/_protos/common/identifier_pb2.pyi +0 -82
- flyte/_protos/common/identifier_pb2_grpc.py +0 -4
- flyte/_protos/common/identity_pb2.py +0 -48
- flyte/_protos/common/identity_pb2.pyi +0 -72
- flyte/_protos/common/identity_pb2_grpc.py +0 -4
- flyte/_protos/common/list_pb2.py +0 -36
- flyte/_protos/common/list_pb2.pyi +0 -69
- flyte/_protos/common/list_pb2_grpc.py +0 -4
- flyte/_protos/common/policy_pb2.py +0 -37
- flyte/_protos/common/policy_pb2.pyi +0 -27
- flyte/_protos/common/policy_pb2_grpc.py +0 -4
- flyte/_protos/common/role_pb2.py +0 -37
- flyte/_protos/common/role_pb2.pyi +0 -53
- flyte/_protos/common/role_pb2_grpc.py +0 -4
- flyte/_protos/common/runtime_version_pb2.py +0 -28
- flyte/_protos/common/runtime_version_pb2.pyi +0 -24
- flyte/_protos/common/runtime_version_pb2_grpc.py +0 -4
- flyte/_protos/logs/dataplane/payload_pb2.py +0 -96
- flyte/_protos/logs/dataplane/payload_pb2.pyi +0 -168
- flyte/_protos/logs/dataplane/payload_pb2_grpc.py +0 -4
- flyte/_protos/secret/definition_pb2.py +0 -49
- flyte/_protos/secret/definition_pb2.pyi +0 -93
- flyte/_protos/secret/definition_pb2_grpc.py +0 -4
- flyte/_protos/secret/payload_pb2.py +0 -62
- flyte/_protos/secret/payload_pb2.pyi +0 -94
- flyte/_protos/secret/payload_pb2_grpc.py +0 -4
- flyte/_protos/secret/secret_pb2.py +0 -38
- flyte/_protos/secret/secret_pb2.pyi +0 -6
- flyte/_protos/secret/secret_pb2_grpc.py +0 -198
- flyte/_protos/secret/secret_pb2_grpc_grpc.py +0 -198
- flyte/_protos/validate/validate/validate_pb2.py +0 -76
- flyte/_protos/workflow/node_execution_service_pb2.py +0 -26
- flyte/_protos/workflow/node_execution_service_pb2.pyi +0 -4
- flyte/_protos/workflow/node_execution_service_pb2_grpc.py +0 -32
- flyte/_protos/workflow/queue_service_pb2.py +0 -106
- flyte/_protos/workflow/queue_service_pb2.pyi +0 -141
- flyte/_protos/workflow/queue_service_pb2_grpc.py +0 -172
- flyte/_protos/workflow/run_definition_pb2.py +0 -128
- flyte/_protos/workflow/run_definition_pb2.pyi +0 -310
- flyte/_protos/workflow/run_definition_pb2_grpc.py +0 -4
- flyte/_protos/workflow/run_logs_service_pb2.py +0 -41
- flyte/_protos/workflow/run_logs_service_pb2.pyi +0 -28
- flyte/_protos/workflow/run_logs_service_pb2_grpc.py +0 -69
- flyte/_protos/workflow/run_service_pb2.py +0 -133
- flyte/_protos/workflow/run_service_pb2.pyi +0 -175
- flyte/_protos/workflow/run_service_pb2_grpc.py +0 -412
- flyte/_protos/workflow/state_service_pb2.py +0 -58
- flyte/_protos/workflow/state_service_pb2.pyi +0 -71
- flyte/_protos/workflow/state_service_pb2_grpc.py +0 -138
- flyte/_protos/workflow/task_definition_pb2.py +0 -72
- flyte/_protos/workflow/task_definition_pb2.pyi +0 -65
- flyte/_protos/workflow/task_definition_pb2_grpc.py +0 -4
- flyte/_protos/workflow/task_service_pb2.py +0 -44
- flyte/_protos/workflow/task_service_pb2.pyi +0 -31
- flyte/_protos/workflow/task_service_pb2_grpc.py +0 -104
- flyte/io/_dataframe.py +0 -0
- flyte/io/pickle/__init__.py +0 -0
- flyte/remote/_console.py +0 -18
- flyte-0.0.1b0.dist-info/METADATA +0 -179
- flyte-0.0.1b0.dist-info/RECORD +0 -390
- flyte-0.0.1b0.dist-info/entry_points.txt +0 -3
- union/__init__.py +0 -54
- union/_api_commons.py +0 -3
- union/_bin/__init__.py +0 -0
- union/_bin/runtime.py +0 -113
- union/_build.py +0 -25
- union/_cache/__init__.py +0 -12
- union/_cache/cache.py +0 -141
- union/_cache/defaults.py +0 -9
- union/_cache/policy_function_body.py +0 -42
- union/_cli/__init__.py +0 -0
- union/_cli/_common.py +0 -263
- union/_cli/_create.py +0 -40
- union/_cli/_delete.py +0 -23
- union/_cli/_deploy.py +0 -120
- union/_cli/_get.py +0 -162
- union/_cli/_run.py +0 -150
- union/_cli/main.py +0 -72
- union/_code_bundle/__init__.py +0 -8
- union/_code_bundle/_ignore.py +0 -113
- union/_code_bundle/_packaging.py +0 -187
- union/_code_bundle/_utils.py +0 -342
- union/_code_bundle/bundle.py +0 -176
- union/_context.py +0 -146
- union/_datastructures.py +0 -295
- union/_deploy.py +0 -185
- union/_doc.py +0 -29
- union/_docstring.py +0 -26
- union/_environment.py +0 -43
- union/_group.py +0 -31
- union/_hash.py +0 -23
- union/_image.py +0 -760
- union/_initialize.py +0 -585
- union/_interface.py +0 -84
- union/_internal/__init__.py +0 -3
- union/_internal/controllers/__init__.py +0 -77
- union/_internal/controllers/_local_controller.py +0 -77
- union/_internal/controllers/pbhash.py +0 -39
- union/_internal/controllers/remote/__init__.py +0 -40
- union/_internal/controllers/remote/_action.py +0 -131
- union/_internal/controllers/remote/_client.py +0 -43
- union/_internal/controllers/remote/_controller.py +0 -169
- union/_internal/controllers/remote/_core.py +0 -341
- union/_internal/controllers/remote/_informer.py +0 -260
- union/_internal/controllers/remote/_service_protocol.py +0 -44
- union/_internal/imagebuild/__init__.py +0 -11
- union/_internal/imagebuild/docker_builder.py +0 -416
- union/_internal/imagebuild/image_builder.py +0 -243
- union/_internal/imagebuild/remote_builder.py +0 -0
- union/_internal/resolvers/__init__.py +0 -0
- union/_internal/resolvers/_task_module.py +0 -31
- union/_internal/resolvers/common.py +0 -24
- union/_internal/resolvers/default.py +0 -27
- union/_internal/runtime/__init__.py +0 -0
- union/_internal/runtime/convert.py +0 -163
- union/_internal/runtime/entrypoints.py +0 -121
- union/_internal/runtime/io.py +0 -136
- union/_internal/runtime/resources_serde.py +0 -134
- union/_internal/runtime/task_serde.py +0 -202
- union/_internal/runtime/taskrunner.py +0 -179
- union/_internal/runtime/types_serde.py +0 -53
- union/_logging.py +0 -124
- union/_protos/__init__.py +0 -0
- union/_protos/common/authorization_pb2.py +0 -66
- union/_protos/common/authorization_pb2.pyi +0 -106
- union/_protos/common/authorization_pb2_grpc.py +0 -4
- union/_protos/common/identifier_pb2.py +0 -71
- union/_protos/common/identifier_pb2.pyi +0 -82
- union/_protos/common/identifier_pb2_grpc.py +0 -4
- union/_protos/common/identity_pb2.py +0 -48
- union/_protos/common/identity_pb2.pyi +0 -72
- union/_protos/common/identity_pb2_grpc.py +0 -4
- union/_protos/common/list_pb2.py +0 -36
- union/_protos/common/list_pb2.pyi +0 -69
- union/_protos/common/list_pb2_grpc.py +0 -4
- union/_protos/common/policy_pb2.py +0 -37
- union/_protos/common/policy_pb2.pyi +0 -27
- union/_protos/common/policy_pb2_grpc.py +0 -4
- union/_protos/common/role_pb2.py +0 -37
- union/_protos/common/role_pb2.pyi +0 -51
- union/_protos/common/role_pb2_grpc.py +0 -4
- union/_protos/common/runtime_version_pb2.py +0 -28
- union/_protos/common/runtime_version_pb2.pyi +0 -24
- union/_protos/common/runtime_version_pb2_grpc.py +0 -4
- union/_protos/logs/dataplane/payload_pb2.py +0 -96
- union/_protos/logs/dataplane/payload_pb2.pyi +0 -168
- union/_protos/logs/dataplane/payload_pb2_grpc.py +0 -4
- union/_protos/secret/definition_pb2.py +0 -49
- union/_protos/secret/definition_pb2.pyi +0 -93
- union/_protos/secret/definition_pb2_grpc.py +0 -4
- union/_protos/secret/payload_pb2.py +0 -62
- union/_protos/secret/payload_pb2.pyi +0 -94
- union/_protos/secret/payload_pb2_grpc.py +0 -4
- union/_protos/secret/secret_pb2.py +0 -38
- union/_protos/secret/secret_pb2.pyi +0 -6
- union/_protos/secret/secret_pb2_grpc.py +0 -198
- union/_protos/validate/validate/validate_pb2.py +0 -76
- union/_protos/workflow/node_execution_service_pb2.py +0 -26
- union/_protos/workflow/node_execution_service_pb2.pyi +0 -4
- union/_protos/workflow/node_execution_service_pb2_grpc.py +0 -32
- union/_protos/workflow/queue_service_pb2.py +0 -75
- union/_protos/workflow/queue_service_pb2.pyi +0 -103
- union/_protos/workflow/queue_service_pb2_grpc.py +0 -172
- union/_protos/workflow/run_definition_pb2.py +0 -100
- union/_protos/workflow/run_definition_pb2.pyi +0 -256
- union/_protos/workflow/run_definition_pb2_grpc.py +0 -4
- union/_protos/workflow/run_logs_service_pb2.py +0 -41
- union/_protos/workflow/run_logs_service_pb2.pyi +0 -28
- union/_protos/workflow/run_logs_service_pb2_grpc.py +0 -69
- union/_protos/workflow/run_service_pb2.py +0 -133
- union/_protos/workflow/run_service_pb2.pyi +0 -173
- union/_protos/workflow/run_service_pb2_grpc.py +0 -412
- union/_protos/workflow/state_service_pb2.py +0 -58
- union/_protos/workflow/state_service_pb2.pyi +0 -69
- union/_protos/workflow/state_service_pb2_grpc.py +0 -138
- union/_protos/workflow/task_definition_pb2.py +0 -72
- union/_protos/workflow/task_definition_pb2.pyi +0 -65
- union/_protos/workflow/task_definition_pb2_grpc.py +0 -4
- union/_protos/workflow/task_service_pb2.py +0 -44
- union/_protos/workflow/task_service_pb2.pyi +0 -31
- union/_protos/workflow/task_service_pb2_grpc.py +0 -104
- union/_resources.py +0 -226
- union/_retry.py +0 -32
- union/_reusable_environment.py +0 -25
- union/_run.py +0 -374
- union/_secret.py +0 -61
- union/_task.py +0 -354
- union/_task_environment.py +0 -186
- union/_timeout.py +0 -47
- union/_tools.py +0 -27
- union/_utils/__init__.py +0 -11
- union/_utils/asyn.py +0 -119
- union/_utils/file_handling.py +0 -71
- union/_utils/helpers.py +0 -46
- union/_utils/lazy_module.py +0 -54
- union/_utils/uv_script_parser.py +0 -49
- union/_version.py +0 -21
- union/connectors/__init__.py +0 -0
- union/errors.py +0 -128
- union/extras/__init__.py +0 -5
- union/extras/_container.py +0 -263
- union/io/__init__.py +0 -11
- union/io/_dataframe.py +0 -0
- union/io/_dir.py +0 -425
- union/io/_file.py +0 -418
- union/io/pickle/__init__.py +0 -0
- union/io/pickle/transformer.py +0 -117
- union/io/structured_dataset/__init__.py +0 -122
- union/io/structured_dataset/basic_dfs.py +0 -219
- union/io/structured_dataset/structured_dataset.py +0 -1057
- union/py.typed +0 -0
- union/remote/__init__.py +0 -23
- union/remote/_client/__init__.py +0 -0
- union/remote/_client/_protocols.py +0 -129
- union/remote/_client/auth/__init__.py +0 -12
- union/remote/_client/auth/_authenticators/__init__.py +0 -0
- union/remote/_client/auth/_authenticators/base.py +0 -391
- union/remote/_client/auth/_authenticators/client_credentials.py +0 -73
- union/remote/_client/auth/_authenticators/device_code.py +0 -120
- union/remote/_client/auth/_authenticators/external_command.py +0 -77
- union/remote/_client/auth/_authenticators/factory.py +0 -200
- union/remote/_client/auth/_authenticators/pkce.py +0 -515
- union/remote/_client/auth/_channel.py +0 -184
- union/remote/_client/auth/_client_config.py +0 -83
- union/remote/_client/auth/_default_html.py +0 -32
- union/remote/_client/auth/_grpc_utils/__init__.py +0 -0
- union/remote/_client/auth/_grpc_utils/auth_interceptor.py +0 -204
- union/remote/_client/auth/_grpc_utils/default_metadata_interceptor.py +0 -144
- union/remote/_client/auth/_keyring.py +0 -154
- union/remote/_client/auth/_token_client.py +0 -258
- union/remote/_client/auth/errors.py +0 -16
- union/remote/_client/controlplane.py +0 -86
- union/remote/_data.py +0 -149
- union/remote/_logs.py +0 -74
- union/remote/_project.py +0 -86
- union/remote/_run.py +0 -820
- union/remote/_secret.py +0 -132
- union/remote/_task.py +0 -193
- union/report/__init__.py +0 -3
- union/report/_report.py +0 -178
- union/report/_template.html +0 -124
- union/storage/__init__.py +0 -24
- union/storage/_remote_fs.py +0 -34
- union/storage/_storage.py +0 -247
- union/storage/_utils.py +0 -5
- union/types/__init__.py +0 -11
- union/types/_renderer.py +0 -162
- union/types/_string_literals.py +0 -120
- union/types/_type_engine.py +0 -2131
- union/types/_utils.py +0 -80
- /flyte/{_cli → _debug}/__init__.py +0 -0
- /flyte/{_protos → _keyring}/__init__.py +0 -0
- {flyte-0.0.1b0.dist-info → flyte-2.0.0b46.dist-info}/WHEEL +0 -0
- {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
|
+
"""
|
flyte/_utils/coro_management.py
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|