flyte 2.0.0b13__py3-none-any.whl → 2.0.0b30__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- flyte/__init__.py +18 -2
- flyte/_bin/debug.py +38 -0
- flyte/_bin/runtime.py +62 -8
- flyte/_cache/cache.py +4 -2
- flyte/_cache/local_cache.py +216 -0
- flyte/_code_bundle/_ignore.py +12 -4
- flyte/_code_bundle/_packaging.py +13 -9
- flyte/_code_bundle/_utils.py +18 -10
- flyte/_code_bundle/bundle.py +17 -9
- flyte/_constants.py +1 -0
- flyte/_context.py +4 -1
- flyte/_custom_context.py +73 -0
- flyte/_debug/constants.py +38 -0
- flyte/_debug/utils.py +17 -0
- flyte/_debug/vscode.py +307 -0
- flyte/_deploy.py +235 -61
- flyte/_environment.py +20 -6
- flyte/_excepthook.py +1 -1
- flyte/_hash.py +1 -16
- flyte/_image.py +178 -81
- flyte/_initialize.py +132 -51
- flyte/_interface.py +39 -2
- flyte/_internal/controllers/__init__.py +4 -5
- flyte/_internal/controllers/_local_controller.py +70 -29
- flyte/_internal/controllers/_trace.py +1 -1
- flyte/_internal/controllers/remote/__init__.py +0 -2
- flyte/_internal/controllers/remote/_action.py +14 -16
- flyte/_internal/controllers/remote/_client.py +1 -1
- flyte/_internal/controllers/remote/_controller.py +68 -70
- flyte/_internal/controllers/remote/_core.py +127 -99
- flyte/_internal/controllers/remote/_informer.py +19 -10
- flyte/_internal/controllers/remote/_service_protocol.py +7 -7
- flyte/_internal/imagebuild/docker_builder.py +181 -69
- flyte/_internal/imagebuild/image_builder.py +0 -5
- flyte/_internal/imagebuild/remote_builder.py +155 -64
- flyte/_internal/imagebuild/utils.py +51 -2
- flyte/_internal/resolvers/_task_module.py +5 -38
- flyte/_internal/resolvers/default.py +2 -2
- flyte/_internal/runtime/convert.py +110 -21
- flyte/_internal/runtime/entrypoints.py +27 -1
- flyte/_internal/runtime/io.py +21 -8
- flyte/_internal/runtime/resources_serde.py +20 -6
- flyte/_internal/runtime/reuse.py +1 -1
- flyte/_internal/runtime/rusty.py +20 -5
- flyte/_internal/runtime/task_serde.py +34 -19
- flyte/_internal/runtime/taskrunner.py +22 -4
- flyte/_internal/runtime/trigger_serde.py +160 -0
- flyte/_internal/runtime/types_serde.py +1 -1
- flyte/_keyring/__init__.py +0 -0
- flyte/_keyring/file.py +115 -0
- flyte/_logging.py +201 -39
- flyte/_map.py +111 -14
- flyte/_module.py +70 -0
- flyte/_pod.py +4 -3
- flyte/_resources.py +213 -31
- flyte/_run.py +110 -39
- flyte/_task.py +75 -16
- flyte/_task_environment.py +105 -29
- flyte/_task_plugins.py +4 -2
- flyte/_trace.py +5 -0
- flyte/_trigger.py +1000 -0
- flyte/_utils/__init__.py +2 -1
- flyte/_utils/asyn.py +3 -1
- flyte/_utils/coro_management.py +2 -1
- flyte/_utils/docker_credentials.py +173 -0
- flyte/_utils/module_loader.py +17 -2
- flyte/_version.py +3 -3
- flyte/cli/_abort.py +3 -3
- flyte/cli/_build.py +3 -6
- flyte/cli/_common.py +78 -7
- flyte/cli/_create.py +182 -4
- flyte/cli/_delete.py +23 -1
- flyte/cli/_deploy.py +63 -16
- flyte/cli/_get.py +79 -34
- flyte/cli/_params.py +26 -10
- flyte/cli/_plugins.py +209 -0
- flyte/cli/_run.py +151 -26
- flyte/cli/_serve.py +64 -0
- flyte/cli/_update.py +37 -0
- flyte/cli/_user.py +17 -0
- flyte/cli/main.py +30 -4
- flyte/config/_config.py +10 -6
- flyte/config/_internal.py +1 -0
- flyte/config/_reader.py +29 -8
- flyte/connectors/__init__.py +11 -0
- flyte/connectors/_connector.py +270 -0
- flyte/connectors/_server.py +197 -0
- flyte/connectors/utils.py +135 -0
- flyte/errors.py +22 -2
- flyte/extend.py +8 -1
- flyte/extras/_container.py +6 -1
- flyte/git/__init__.py +3 -0
- flyte/git/_config.py +21 -0
- flyte/io/__init__.py +2 -0
- flyte/io/_dataframe/__init__.py +2 -0
- flyte/io/_dataframe/basic_dfs.py +17 -8
- flyte/io/_dataframe/dataframe.py +98 -132
- flyte/io/_dir.py +575 -113
- flyte/io/_file.py +582 -139
- flyte/io/_hashing_io.py +342 -0
- flyte/models.py +74 -15
- flyte/remote/__init__.py +6 -1
- flyte/remote/_action.py +34 -26
- flyte/remote/_client/_protocols.py +39 -4
- flyte/remote/_client/auth/_authenticators/device_code.py +4 -5
- flyte/remote/_client/auth/_authenticators/pkce.py +1 -1
- flyte/remote/_client/auth/_channel.py +10 -6
- flyte/remote/_client/controlplane.py +17 -5
- flyte/remote/_console.py +3 -2
- flyte/remote/_data.py +6 -6
- flyte/remote/_logs.py +3 -3
- flyte/remote/_run.py +64 -8
- flyte/remote/_secret.py +26 -17
- flyte/remote/_task.py +75 -33
- flyte/remote/_trigger.py +306 -0
- flyte/remote/_user.py +33 -0
- flyte/report/_report.py +1 -1
- flyte/storage/__init__.py +6 -1
- flyte/storage/_config.py +5 -1
- flyte/storage/_parallel_reader.py +274 -0
- flyte/storage/_storage.py +200 -103
- flyte/types/__init__.py +16 -0
- flyte/types/_interface.py +2 -2
- flyte/types/_pickle.py +35 -8
- flyte/types/_string_literals.py +8 -9
- flyte/types/_type_engine.py +40 -70
- flyte/types/_utils.py +1 -1
- flyte-2.0.0b30.data/scripts/debug.py +38 -0
- {flyte-2.0.0b13.data → flyte-2.0.0b30.data}/scripts/runtime.py +62 -8
- {flyte-2.0.0b13.dist-info → flyte-2.0.0b30.dist-info}/METADATA +11 -3
- flyte-2.0.0b30.dist-info/RECORD +192 -0
- {flyte-2.0.0b13.dist-info → flyte-2.0.0b30.dist-info}/entry_points.txt +3 -0
- 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 -93
- flyte/_protos/common/identifier_pb2.pyi +0 -110
- 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 -71
- 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/imagebuilder/definition_pb2.py +0 -59
- flyte/_protos/imagebuilder/definition_pb2.pyi +0 -140
- flyte/_protos/imagebuilder/definition_pb2_grpc.py +0 -4
- flyte/_protos/imagebuilder/payload_pb2.py +0 -32
- flyte/_protos/imagebuilder/payload_pb2.pyi +0 -21
- flyte/_protos/imagebuilder/payload_pb2_grpc.py +0 -4
- flyte/_protos/imagebuilder/service_pb2.py +0 -29
- flyte/_protos/imagebuilder/service_pb2.pyi +0 -5
- flyte/_protos/imagebuilder/service_pb2_grpc.py +0 -66
- flyte/_protos/logs/dataplane/payload_pb2.py +0 -100
- flyte/_protos/logs/dataplane/payload_pb2.pyi +0 -177
- 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/common_pb2.py +0 -27
- flyte/_protos/workflow/common_pb2.pyi +0 -14
- flyte/_protos/workflow/common_pb2_grpc.py +0 -4
- flyte/_protos/workflow/environment_pb2.py +0 -29
- flyte/_protos/workflow/environment_pb2.pyi +0 -12
- flyte/_protos/workflow/environment_pb2_grpc.py +0 -4
- 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 -109
- flyte/_protos/workflow/queue_service_pb2.pyi +0 -166
- flyte/_protos/workflow/queue_service_pb2_grpc.py +0 -172
- flyte/_protos/workflow/run_definition_pb2.py +0 -121
- flyte/_protos/workflow/run_definition_pb2.pyi +0 -327
- 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 -137
- flyte/_protos/workflow/run_service_pb2.pyi +0 -185
- flyte/_protos/workflow/run_service_pb2_grpc.py +0 -446
- flyte/_protos/workflow/state_service_pb2.py +0 -67
- flyte/_protos/workflow/state_service_pb2.pyi +0 -76
- flyte/_protos/workflow/state_service_pb2_grpc.py +0 -138
- flyte/_protos/workflow/task_definition_pb2.py +0 -79
- flyte/_protos/workflow/task_definition_pb2.pyi +0 -81
- flyte/_protos/workflow/task_definition_pb2_grpc.py +0 -4
- flyte/_protos/workflow/task_service_pb2.py +0 -60
- flyte/_protos/workflow/task_service_pb2.pyi +0 -59
- flyte/_protos/workflow/task_service_pb2_grpc.py +0 -138
- flyte-2.0.0b13.dist-info/RECORD +0 -239
- /flyte/{_protos → _debug}/__init__.py +0 -0
- {flyte-2.0.0b13.dist-info → flyte-2.0.0b30.dist-info}/WHEEL +0 -0
- {flyte-2.0.0b13.dist-info → flyte-2.0.0b30.dist-info}/licenses/LICENSE +0 -0
- {flyte-2.0.0b13.dist-info → flyte-2.0.0b30.dist-info}/top_level.txt +0 -0
flyte/__init__.py
CHANGED
|
@@ -9,15 +9,17 @@ import sys
|
|
|
9
9
|
from ._build import build
|
|
10
10
|
from ._cache import Cache, CachePolicy, CacheRequest
|
|
11
11
|
from ._context import ctx
|
|
12
|
+
from ._custom_context import custom_context, get_custom_context
|
|
12
13
|
from ._deploy import build_images, deploy
|
|
13
14
|
from ._environment import Environment
|
|
14
15
|
from ._excepthook import custom_excepthook
|
|
15
16
|
from ._group import group
|
|
16
17
|
from ._image import Image
|
|
17
|
-
from ._initialize import init, init_from_config
|
|
18
|
+
from ._initialize import current_domain, init, init_from_config
|
|
19
|
+
from ._logging import logger
|
|
18
20
|
from ._map import map
|
|
19
21
|
from ._pod import PodTemplate
|
|
20
|
-
from ._resources import GPU, TPU, Device, Resources
|
|
22
|
+
from ._resources import AMD_GPU, GPU, HABANA_GAUDI, TPU, Device, DeviceClass, Neuron, Resources
|
|
21
23
|
from ._retry import RetryStrategy
|
|
22
24
|
from ._reusable_environment import ReusePolicy
|
|
23
25
|
from ._run import run, with_runcontext
|
|
@@ -25,6 +27,7 @@ from ._secret import Secret, SecretRequest
|
|
|
25
27
|
from ._task_environment import TaskEnvironment
|
|
26
28
|
from ._timeout import Timeout, TimeoutType
|
|
27
29
|
from ._trace import trace
|
|
30
|
+
from ._trigger import Cron, FixedRate, Trigger, TriggerTime
|
|
28
31
|
from ._version import __version__
|
|
29
32
|
|
|
30
33
|
sys.excepthook = custom_excepthook
|
|
@@ -59,14 +62,20 @@ def version() -> str:
|
|
|
59
62
|
|
|
60
63
|
|
|
61
64
|
__all__ = [
|
|
65
|
+
"AMD_GPU",
|
|
62
66
|
"GPU",
|
|
67
|
+
"HABANA_GAUDI",
|
|
63
68
|
"TPU",
|
|
64
69
|
"Cache",
|
|
65
70
|
"CachePolicy",
|
|
66
71
|
"CacheRequest",
|
|
72
|
+
"Cron",
|
|
67
73
|
"Device",
|
|
74
|
+
"DeviceClass",
|
|
68
75
|
"Environment",
|
|
76
|
+
"FixedRate",
|
|
69
77
|
"Image",
|
|
78
|
+
"Neuron",
|
|
70
79
|
"PodTemplate",
|
|
71
80
|
"Resources",
|
|
72
81
|
"RetryStrategy",
|
|
@@ -76,16 +85,23 @@ __all__ = [
|
|
|
76
85
|
"TaskEnvironment",
|
|
77
86
|
"Timeout",
|
|
78
87
|
"TimeoutType",
|
|
88
|
+
"Trigger",
|
|
89
|
+
"TriggerTime",
|
|
79
90
|
"__version__",
|
|
80
91
|
"build",
|
|
81
92
|
"build_images",
|
|
82
93
|
"ctx",
|
|
94
|
+
"current_domain",
|
|
95
|
+
"custom_context",
|
|
83
96
|
"deploy",
|
|
97
|
+
"get_custom_context",
|
|
84
98
|
"group",
|
|
85
99
|
"init",
|
|
86
100
|
"init_from_config",
|
|
101
|
+
"logger",
|
|
87
102
|
"map",
|
|
88
103
|
"run",
|
|
89
104
|
"trace",
|
|
105
|
+
"version",
|
|
90
106
|
"with_runcontext",
|
|
91
107
|
]
|
flyte/_bin/debug.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
@click.group()
|
|
5
|
+
def _debug():
|
|
6
|
+
"""Debug commands for Flyte."""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@_debug.command("resume")
|
|
10
|
+
@click.option("--pid", "-m", type=int, required=True, help="PID of the vscode server.")
|
|
11
|
+
def resume(pid):
|
|
12
|
+
"""
|
|
13
|
+
Resume a Flyte task for debugging purposes.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
pid (int): PID of the vscode server.
|
|
17
|
+
"""
|
|
18
|
+
import os
|
|
19
|
+
import signal
|
|
20
|
+
|
|
21
|
+
print("Terminating server and resuming task.")
|
|
22
|
+
answer = (
|
|
23
|
+
input(
|
|
24
|
+
"This operation will kill the server. All unsaved data will be lost,"
|
|
25
|
+
" and you will no longer be able to connect to it. Do you really want to terminate? (Y/N): "
|
|
26
|
+
)
|
|
27
|
+
.strip()
|
|
28
|
+
.upper()
|
|
29
|
+
)
|
|
30
|
+
if answer == "Y":
|
|
31
|
+
os.kill(pid, signal.SIGTERM)
|
|
32
|
+
print("The server has been terminated and the task has been resumed.")
|
|
33
|
+
else:
|
|
34
|
+
print("Operation canceled.")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
if __name__ == "__main__":
|
|
38
|
+
_debug()
|
flyte/_bin/runtime.py
CHANGED
|
@@ -12,6 +12,9 @@ from typing import Any, List
|
|
|
12
12
|
|
|
13
13
|
import click
|
|
14
14
|
|
|
15
|
+
from flyte._utils.helpers import str2bool
|
|
16
|
+
from flyte.models import PathRewrite
|
|
17
|
+
|
|
15
18
|
# Todo: work with pvditt to make these the names
|
|
16
19
|
# ACTION_NAME = "_U_ACTION_NAME"
|
|
17
20
|
# RUN_NAME = "_U_RUN_NAME"
|
|
@@ -21,14 +24,16 @@ import click
|
|
|
21
24
|
|
|
22
25
|
ACTION_NAME = "ACTION_NAME"
|
|
23
26
|
RUN_NAME = "RUN_NAME"
|
|
24
|
-
PROJECT_NAME = "
|
|
25
|
-
DOMAIN_NAME = "
|
|
27
|
+
PROJECT_NAME = "FLYTE_INTERNAL_EXECUTION_PROJECT"
|
|
28
|
+
DOMAIN_NAME = "FLYTE_INTERNAL_EXECUTION_DOMAIN"
|
|
26
29
|
ORG_NAME = "_U_ORG_NAME"
|
|
27
30
|
ENDPOINT_OVERRIDE = "_U_EP_OVERRIDE"
|
|
31
|
+
INSECURE_SKIP_VERIFY_OVERRIDE = "_U_INSECURE_SKIP_VERIFY"
|
|
28
32
|
RUN_OUTPUT_BASE_DIR = "_U_RUN_BASE"
|
|
33
|
+
FLYTE_ENABLE_VSCODE_KEY = "_F_E_VS"
|
|
29
34
|
|
|
30
|
-
# TODO: Remove this after proper auth is implemented
|
|
31
35
|
_UNION_EAGER_API_KEY_ENV_VAR = "_UNION_EAGER_API_KEY"
|
|
36
|
+
_F_PATH_REWRITE = "_F_PATH_REWRITE"
|
|
32
37
|
|
|
33
38
|
|
|
34
39
|
@click.group()
|
|
@@ -49,6 +54,8 @@ def _pass_through():
|
|
|
49
54
|
@click.option("--project", envvar=PROJECT_NAME, required=False)
|
|
50
55
|
@click.option("--domain", envvar=DOMAIN_NAME, required=False)
|
|
51
56
|
@click.option("--org", envvar=ORG_NAME, required=False)
|
|
57
|
+
@click.option("--debug", envvar=FLYTE_ENABLE_VSCODE_KEY, type=click.BOOL, required=False)
|
|
58
|
+
@click.option("--interactive-mode", type=click.BOOL, required=False)
|
|
52
59
|
@click.option("--image-cache", required=False)
|
|
53
60
|
@click.option("--tgz", required=False)
|
|
54
61
|
@click.option("--pkl", required=False)
|
|
@@ -59,12 +66,16 @@ def _pass_through():
|
|
|
59
66
|
type=click.UNPROCESSED,
|
|
60
67
|
nargs=-1,
|
|
61
68
|
)
|
|
69
|
+
@click.pass_context
|
|
62
70
|
def main(
|
|
71
|
+
ctx: click.Context,
|
|
63
72
|
run_name: str,
|
|
64
73
|
name: str,
|
|
65
74
|
project: str,
|
|
66
75
|
domain: str,
|
|
67
76
|
org: str,
|
|
77
|
+
debug: bool,
|
|
78
|
+
interactive_mode: bool,
|
|
68
79
|
image_cache: str,
|
|
69
80
|
version: str,
|
|
70
81
|
inputs: str,
|
|
@@ -87,6 +98,7 @@ def main(
|
|
|
87
98
|
import flyte
|
|
88
99
|
import flyte._utils as utils
|
|
89
100
|
import flyte.errors
|
|
101
|
+
import flyte.storage as storage
|
|
90
102
|
from flyte._initialize import init
|
|
91
103
|
from flyte._internal.controllers import create_controller
|
|
92
104
|
from flyte._internal.imagebuild.image_builder import ImageCache
|
|
@@ -109,6 +121,13 @@ def main(
|
|
|
109
121
|
if name.startswith("{{"):
|
|
110
122
|
name = os.getenv("ACTION_NAME", "")
|
|
111
123
|
|
|
124
|
+
logger.warning(f"Flyte runtime started for action {name} with run name {run_name}")
|
|
125
|
+
|
|
126
|
+
if debug and name == "a0":
|
|
127
|
+
from flyte._debug.vscode import _start_vscode_server
|
|
128
|
+
|
|
129
|
+
asyncio.run(_start_vscode_server(ctx))
|
|
130
|
+
|
|
112
131
|
# Figure out how to connect
|
|
113
132
|
# This detection of api key is a hack for now.
|
|
114
133
|
controller_kwargs: dict[str, Any] = {"insecure": False}
|
|
@@ -122,19 +141,40 @@ def main(
|
|
|
122
141
|
controller_kwargs["insecure"] = True
|
|
123
142
|
logger.debug(f"Using controller endpoint: {ep} with kwargs: {controller_kwargs}")
|
|
124
143
|
|
|
125
|
-
|
|
126
|
-
|
|
144
|
+
# Check for insecure_skip_verify override (e.g. for self-signed certs)
|
|
145
|
+
insecure_skip_verify_str = os.getenv(INSECURE_SKIP_VERIFY_OVERRIDE, "")
|
|
146
|
+
if str2bool(insecure_skip_verify_str):
|
|
147
|
+
controller_kwargs["insecure_skip_verify"] = True
|
|
148
|
+
logger.info("SSL certificate verification disabled (insecure_skip_verify=True)")
|
|
149
|
+
|
|
150
|
+
bundle = None
|
|
151
|
+
if tgz or pkl:
|
|
152
|
+
bundle = CodeBundle(tgz=tgz, pkl=pkl, destination=dest, computed_version=version)
|
|
153
|
+
init(org=org, project=project, domain=domain, image_builder="remote", **controller_kwargs)
|
|
127
154
|
# Controller is created with the same kwargs as init, so that it can be used to run tasks
|
|
128
155
|
controller = create_controller(ct="remote", **controller_kwargs)
|
|
129
156
|
|
|
130
157
|
ic = ImageCache.from_transport(image_cache) if image_cache else None
|
|
131
158
|
|
|
159
|
+
path_rewrite_cfg = os.getenv(_F_PATH_REWRITE, None)
|
|
160
|
+
path_rewrite = None
|
|
161
|
+
if path_rewrite_cfg:
|
|
162
|
+
potential_path_rewrite = PathRewrite.from_str(path_rewrite_cfg)
|
|
163
|
+
if storage.exists_sync(potential_path_rewrite.new_prefix):
|
|
164
|
+
path_rewrite = potential_path_rewrite
|
|
165
|
+
logger.info(f"Path rewrite configured for {path_rewrite.new_prefix}")
|
|
166
|
+
else:
|
|
167
|
+
logger.error(
|
|
168
|
+
f"Path rewrite failed for path {potential_path_rewrite.new_prefix}, "
|
|
169
|
+
f"not found, reverting to original path {potential_path_rewrite.old_prefix}"
|
|
170
|
+
)
|
|
171
|
+
|
|
132
172
|
# Create a coroutine to load the task and run it
|
|
133
173
|
task_coroutine = load_and_run_task(
|
|
134
174
|
resolver=resolver,
|
|
135
175
|
resolver_args=resolver_args,
|
|
136
176
|
action=ActionID(name=name, run_name=run_name, project=project, domain=domain, org=org),
|
|
137
|
-
raw_data_path=RawDataPath(path=raw_data_path),
|
|
177
|
+
raw_data_path=RawDataPath(path=raw_data_path, path_rewrite=path_rewrite),
|
|
138
178
|
checkpoints=Checkpoints(checkpoint_path, prev_checkpoint),
|
|
139
179
|
code_bundle=bundle,
|
|
140
180
|
input_path=inputs,
|
|
@@ -143,6 +183,7 @@ def main(
|
|
|
143
183
|
version=version,
|
|
144
184
|
controller=controller,
|
|
145
185
|
image_cache=ic,
|
|
186
|
+
interactive_mode=interactive_mode or debug,
|
|
146
187
|
)
|
|
147
188
|
# Create a coroutine to watch for errors
|
|
148
189
|
controller_failure = controller.watch_for_errors()
|
|
@@ -151,10 +192,23 @@ def main(
|
|
|
151
192
|
async def _run_and_stop():
|
|
152
193
|
loop = asyncio.get_event_loop()
|
|
153
194
|
loop.set_exception_handler(flyte.errors.silence_grpc_polling_error)
|
|
154
|
-
|
|
155
|
-
|
|
195
|
+
try:
|
|
196
|
+
await utils.run_coros(controller_failure, task_coroutine)
|
|
197
|
+
await controller.stop()
|
|
198
|
+
except flyte.errors.RuntimeSystemError as e:
|
|
199
|
+
logger.error(f"Runtime system error: {e}")
|
|
200
|
+
from flyte._internal.runtime.convert import convert_from_native_to_error
|
|
201
|
+
from flyte._internal.runtime.io import upload_error
|
|
202
|
+
|
|
203
|
+
logger.error(f"Flyte runtime failed for action {name} with run name {run_name}, error: {e}")
|
|
204
|
+
err = convert_from_native_to_error(e)
|
|
205
|
+
path = await upload_error(err.err, outputs_path)
|
|
206
|
+
logger.error(f"Run {run_name} Action {name} failed with error: {err}. Uploaded error to {path}")
|
|
207
|
+
await controller.stop()
|
|
208
|
+
raise
|
|
156
209
|
|
|
157
210
|
asyncio.run(_run_and_stop())
|
|
211
|
+
logger.warning(f"Flyte runtime completed for action {name} with run name {run_name}")
|
|
158
212
|
|
|
159
213
|
|
|
160
214
|
if __name__ == "__main__":
|
flyte/_cache/cache.py
CHANGED
|
@@ -77,14 +77,16 @@ class Cache:
|
|
|
77
77
|
def __post_init__(self):
|
|
78
78
|
if self.behavior not in get_args(CacheBehavior):
|
|
79
79
|
raise ValueError(f"Invalid cache behavior: {self.behavior}. Must be one of ['auto', 'override', 'disable']")
|
|
80
|
-
if self.behavior == "disable":
|
|
81
|
-
return
|
|
82
80
|
|
|
81
|
+
# Still setup _ignore_inputs when cache is disabled to prevent _ignored_inputs attribute not found error
|
|
83
82
|
if isinstance(self.ignored_inputs, str):
|
|
84
83
|
self._ignored_inputs = (self.ignored_inputs,)
|
|
85
84
|
else:
|
|
86
85
|
self._ignored_inputs = self.ignored_inputs
|
|
87
86
|
|
|
87
|
+
if self.behavior == "disable":
|
|
88
|
+
return
|
|
89
|
+
|
|
88
90
|
# Normalize policies so that self._policies is always a list
|
|
89
91
|
if self.policies is None:
|
|
90
92
|
from flyte._cache.defaults import get_default_policies
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import sqlite3
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
try:
|
|
5
|
+
import aiosqlite
|
|
6
|
+
|
|
7
|
+
HAS_AIOSQLITE = True
|
|
8
|
+
except ImportError:
|
|
9
|
+
HAS_AIOSQLITE = False
|
|
10
|
+
|
|
11
|
+
from flyteidl2.task import common_pb2
|
|
12
|
+
|
|
13
|
+
from flyte._internal.runtime import convert
|
|
14
|
+
from flyte._logging import logger
|
|
15
|
+
from flyte.config import auto
|
|
16
|
+
|
|
17
|
+
DEFAULT_CACHE_DIR = "~/.flyte"
|
|
18
|
+
CACHE_LOCATION = "local-cache/cache.db"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class LocalTaskCache(object):
|
|
22
|
+
"""
|
|
23
|
+
This class implements a persistent store able to cache the result of local task executions.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
_conn: "aiosqlite.Connection | None" = None
|
|
27
|
+
_conn_sync: sqlite3.Connection | None = None
|
|
28
|
+
_initialized: bool = False
|
|
29
|
+
|
|
30
|
+
@staticmethod
|
|
31
|
+
def _get_cache_path() -> str:
|
|
32
|
+
"""Get the cache database path, creating directory if needed."""
|
|
33
|
+
config = auto()
|
|
34
|
+
if config.source:
|
|
35
|
+
cache_dir = config.source.parent
|
|
36
|
+
else:
|
|
37
|
+
cache_dir = Path(DEFAULT_CACHE_DIR).expanduser()
|
|
38
|
+
|
|
39
|
+
cache_path = cache_dir / CACHE_LOCATION
|
|
40
|
+
# Ensure the directory exists
|
|
41
|
+
cache_path.parent.mkdir(parents=True, exist_ok=True)
|
|
42
|
+
logger.info(f"Use local cache path: {cache_path}")
|
|
43
|
+
return str(cache_path)
|
|
44
|
+
|
|
45
|
+
@staticmethod
|
|
46
|
+
async def initialize():
|
|
47
|
+
"""Initialize the cache with database connection."""
|
|
48
|
+
if not LocalTaskCache._initialized:
|
|
49
|
+
if HAS_AIOSQLITE:
|
|
50
|
+
await LocalTaskCache._initialize_async()
|
|
51
|
+
else:
|
|
52
|
+
LocalTaskCache._initialize_sync()
|
|
53
|
+
|
|
54
|
+
@staticmethod
|
|
55
|
+
async def _initialize_async():
|
|
56
|
+
"""Initialize async cache connection."""
|
|
57
|
+
db_path = LocalTaskCache._get_cache_path()
|
|
58
|
+
conn = await aiosqlite.connect(db_path)
|
|
59
|
+
await conn.execute("""
|
|
60
|
+
CREATE TABLE IF NOT EXISTS task_cache (
|
|
61
|
+
key TEXT PRIMARY KEY,
|
|
62
|
+
value BLOB
|
|
63
|
+
)
|
|
64
|
+
""")
|
|
65
|
+
await conn.commit()
|
|
66
|
+
LocalTaskCache._conn = conn
|
|
67
|
+
LocalTaskCache._initialized = True
|
|
68
|
+
|
|
69
|
+
@staticmethod
|
|
70
|
+
def _initialize_sync():
|
|
71
|
+
"""Initialize sync cache connection."""
|
|
72
|
+
db_path = LocalTaskCache._get_cache_path()
|
|
73
|
+
conn = sqlite3.connect(db_path)
|
|
74
|
+
conn.execute("""
|
|
75
|
+
CREATE TABLE IF NOT EXISTS task_cache (
|
|
76
|
+
key TEXT PRIMARY KEY,
|
|
77
|
+
value BLOB
|
|
78
|
+
)
|
|
79
|
+
""")
|
|
80
|
+
conn.commit()
|
|
81
|
+
LocalTaskCache._conn_sync = conn
|
|
82
|
+
LocalTaskCache._initialized = True
|
|
83
|
+
|
|
84
|
+
@staticmethod
|
|
85
|
+
async def clear():
|
|
86
|
+
"""Clear all cache entries."""
|
|
87
|
+
if not LocalTaskCache._initialized:
|
|
88
|
+
await LocalTaskCache.initialize()
|
|
89
|
+
|
|
90
|
+
if HAS_AIOSQLITE:
|
|
91
|
+
await LocalTaskCache._clear_async()
|
|
92
|
+
else:
|
|
93
|
+
LocalTaskCache._clear_sync()
|
|
94
|
+
|
|
95
|
+
@staticmethod
|
|
96
|
+
async def _clear_async():
|
|
97
|
+
"""Clear all cache entries (async)."""
|
|
98
|
+
if LocalTaskCache._conn is None:
|
|
99
|
+
raise RuntimeError("Cache not properly initialized")
|
|
100
|
+
await LocalTaskCache._conn.execute("DELETE FROM task_cache")
|
|
101
|
+
await LocalTaskCache._conn.commit()
|
|
102
|
+
|
|
103
|
+
@staticmethod
|
|
104
|
+
def _clear_sync():
|
|
105
|
+
"""Clear all cache entries (sync)."""
|
|
106
|
+
if LocalTaskCache._conn_sync is None:
|
|
107
|
+
raise RuntimeError("Cache not properly initialized")
|
|
108
|
+
LocalTaskCache._conn_sync.execute("DELETE FROM task_cache")
|
|
109
|
+
LocalTaskCache._conn_sync.commit()
|
|
110
|
+
|
|
111
|
+
@staticmethod
|
|
112
|
+
async def get(cache_key: str) -> convert.Outputs | None:
|
|
113
|
+
if not LocalTaskCache._initialized:
|
|
114
|
+
await LocalTaskCache.initialize()
|
|
115
|
+
|
|
116
|
+
if HAS_AIOSQLITE:
|
|
117
|
+
return await LocalTaskCache._get_async(cache_key)
|
|
118
|
+
else:
|
|
119
|
+
return LocalTaskCache._get_sync(cache_key)
|
|
120
|
+
|
|
121
|
+
@staticmethod
|
|
122
|
+
async def _get_async(cache_key: str) -> convert.Outputs | None:
|
|
123
|
+
"""Get cache entry (async)."""
|
|
124
|
+
if LocalTaskCache._conn is None:
|
|
125
|
+
raise RuntimeError("Cache not properly initialized")
|
|
126
|
+
|
|
127
|
+
async with LocalTaskCache._conn.execute("SELECT value FROM task_cache WHERE key = ?", (cache_key,)) as cursor:
|
|
128
|
+
row = await cursor.fetchone()
|
|
129
|
+
if row:
|
|
130
|
+
outputs_bytes = row[0]
|
|
131
|
+
outputs = common_pb2.Outputs()
|
|
132
|
+
outputs.ParseFromString(outputs_bytes)
|
|
133
|
+
return convert.Outputs(proto_outputs=outputs)
|
|
134
|
+
return None
|
|
135
|
+
|
|
136
|
+
@staticmethod
|
|
137
|
+
def _get_sync(cache_key: str) -> convert.Outputs | None:
|
|
138
|
+
"""Get cache entry (sync)."""
|
|
139
|
+
if LocalTaskCache._conn_sync is None:
|
|
140
|
+
raise RuntimeError("Cache not properly initialized")
|
|
141
|
+
|
|
142
|
+
cursor = LocalTaskCache._conn_sync.execute("SELECT value FROM task_cache WHERE key = ?", (cache_key,))
|
|
143
|
+
row = cursor.fetchone()
|
|
144
|
+
if row:
|
|
145
|
+
outputs_bytes = row[0]
|
|
146
|
+
outputs = common_pb2.Outputs()
|
|
147
|
+
outputs.ParseFromString(outputs_bytes)
|
|
148
|
+
return convert.Outputs(proto_outputs=outputs)
|
|
149
|
+
return None
|
|
150
|
+
|
|
151
|
+
@staticmethod
|
|
152
|
+
async def set(
|
|
153
|
+
cache_key: str,
|
|
154
|
+
value: convert.Outputs,
|
|
155
|
+
) -> None:
|
|
156
|
+
if not LocalTaskCache._initialized:
|
|
157
|
+
await LocalTaskCache.initialize()
|
|
158
|
+
|
|
159
|
+
if HAS_AIOSQLITE:
|
|
160
|
+
await LocalTaskCache._set_async(cache_key, value)
|
|
161
|
+
else:
|
|
162
|
+
LocalTaskCache._set_sync(cache_key, value)
|
|
163
|
+
|
|
164
|
+
@staticmethod
|
|
165
|
+
async def _set_async(
|
|
166
|
+
cache_key: str,
|
|
167
|
+
value: convert.Outputs,
|
|
168
|
+
) -> None:
|
|
169
|
+
"""Set cache entry (async)."""
|
|
170
|
+
if LocalTaskCache._conn is None:
|
|
171
|
+
raise RuntimeError("Cache not properly initialized")
|
|
172
|
+
|
|
173
|
+
output_bytes = value.proto_outputs.SerializeToString()
|
|
174
|
+
await LocalTaskCache._conn.execute(
|
|
175
|
+
"INSERT OR REPLACE INTO task_cache (key, value) VALUES (?, ?)", (cache_key, output_bytes)
|
|
176
|
+
)
|
|
177
|
+
await LocalTaskCache._conn.commit()
|
|
178
|
+
|
|
179
|
+
@staticmethod
|
|
180
|
+
def _set_sync(
|
|
181
|
+
cache_key: str,
|
|
182
|
+
value: convert.Outputs,
|
|
183
|
+
) -> None:
|
|
184
|
+
"""Set cache entry (sync)."""
|
|
185
|
+
if LocalTaskCache._conn_sync is None:
|
|
186
|
+
raise RuntimeError("Cache not properly initialized")
|
|
187
|
+
|
|
188
|
+
output_bytes = value.proto_outputs.SerializeToString()
|
|
189
|
+
LocalTaskCache._conn_sync.execute(
|
|
190
|
+
"INSERT OR REPLACE INTO task_cache (key, value) VALUES (?, ?)", (cache_key, output_bytes)
|
|
191
|
+
)
|
|
192
|
+
LocalTaskCache._conn_sync.commit()
|
|
193
|
+
|
|
194
|
+
@staticmethod
|
|
195
|
+
async def close():
|
|
196
|
+
"""Close the database connection."""
|
|
197
|
+
if HAS_AIOSQLITE:
|
|
198
|
+
await LocalTaskCache._close_async()
|
|
199
|
+
else:
|
|
200
|
+
LocalTaskCache._close_sync()
|
|
201
|
+
|
|
202
|
+
@staticmethod
|
|
203
|
+
async def _close_async():
|
|
204
|
+
"""Close async database connection."""
|
|
205
|
+
if LocalTaskCache._conn:
|
|
206
|
+
await LocalTaskCache._conn.close()
|
|
207
|
+
LocalTaskCache._conn = None
|
|
208
|
+
LocalTaskCache._initialized = False
|
|
209
|
+
|
|
210
|
+
@staticmethod
|
|
211
|
+
def _close_sync():
|
|
212
|
+
"""Close sync database connection."""
|
|
213
|
+
if LocalTaskCache._conn_sync:
|
|
214
|
+
LocalTaskCache._conn_sync.close()
|
|
215
|
+
LocalTaskCache._conn_sync = None
|
|
216
|
+
LocalTaskCache._initialized = False
|
flyte/_code_bundle/_ignore.py
CHANGED
|
@@ -79,12 +79,19 @@ class StandardIgnore(Ignore):
|
|
|
79
79
|
by fed with custom ignore patterns from cli."""
|
|
80
80
|
|
|
81
81
|
def __init__(self, root: Path, patterns: Optional[List[str]] = None):
|
|
82
|
-
super().__init__(root)
|
|
82
|
+
super().__init__(root.resolve())
|
|
83
83
|
self.patterns = patterns if patterns else STANDARD_IGNORE_PATTERNS
|
|
84
84
|
|
|
85
85
|
def _is_ignored(self, path: pathlib.Path) -> bool:
|
|
86
|
+
# Convert to relative path for pattern matching
|
|
87
|
+
try:
|
|
88
|
+
rel_path = path.relative_to(self.root)
|
|
89
|
+
except ValueError:
|
|
90
|
+
# If path is not under root, don't ignore it
|
|
91
|
+
return False
|
|
92
|
+
|
|
86
93
|
for pattern in self.patterns:
|
|
87
|
-
if fnmatch(str(
|
|
94
|
+
if fnmatch(str(rel_path), pattern):
|
|
88
95
|
return True
|
|
89
96
|
return False
|
|
90
97
|
|
|
@@ -105,9 +112,10 @@ class IgnoreGroup(Ignore):
|
|
|
105
112
|
|
|
106
113
|
def list_ignored(self) -> List[str]:
|
|
107
114
|
ignored = []
|
|
108
|
-
for dir, _, files in self.root
|
|
115
|
+
for dir, _, files in os.walk(self.root):
|
|
116
|
+
dir_path = Path(dir)
|
|
109
117
|
for file in files:
|
|
110
|
-
abs_path =
|
|
118
|
+
abs_path = dir_path / file
|
|
111
119
|
if self.is_ignored(abs_path):
|
|
112
120
|
ignored.append(str(abs_path.relative_to(self.root)))
|
|
113
121
|
return ignored
|
flyte/_code_bundle/_packaging.py
CHANGED
|
@@ -14,10 +14,9 @@ import typing
|
|
|
14
14
|
from typing import List, Optional, Tuple, Union
|
|
15
15
|
|
|
16
16
|
import click
|
|
17
|
-
from rich import print as rich_print
|
|
18
17
|
from rich.tree import Tree
|
|
19
18
|
|
|
20
|
-
from flyte._logging import logger
|
|
19
|
+
from flyte._logging import _get_console, logger
|
|
21
20
|
|
|
22
21
|
from ._ignore import Ignore, IgnoreGroup
|
|
23
22
|
from ._utils import CopyFiles, _filehash_update, _pathhash_update, ls_files, tar_strip_file_attributes
|
|
@@ -27,21 +26,21 @@ FAST_FILEENDING = ".tar.gz"
|
|
|
27
26
|
|
|
28
27
|
|
|
29
28
|
def print_ls_tree(source: os.PathLike, ls: typing.List[str]):
|
|
30
|
-
|
|
29
|
+
logger.info("Files to be copied for fast registration...")
|
|
31
30
|
|
|
32
31
|
tree_root = Tree(
|
|
33
|
-
f":open_file_folder:
|
|
32
|
+
f"File structure:\n:open_file_folder: {source}",
|
|
34
33
|
guide_style="bold bright_blue",
|
|
35
34
|
)
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
source_path = pathlib.Path(source).resolve()
|
|
36
|
+
trees = {source_path: tree_root}
|
|
38
37
|
for f in ls:
|
|
39
38
|
fpp = pathlib.Path(f)
|
|
40
39
|
if fpp.parent not in trees:
|
|
41
40
|
# add trees for all intermediate folders
|
|
42
41
|
current = tree_root
|
|
43
|
-
current_path = pathlib.Path(source)
|
|
44
|
-
for subdir in fpp.parent.relative_to(
|
|
42
|
+
current_path = source_path # pathlib.Path(source)
|
|
43
|
+
for subdir in fpp.parent.relative_to(source_path).parts:
|
|
45
44
|
current_path = current_path / subdir
|
|
46
45
|
if current_path not in trees:
|
|
47
46
|
current = current.add(f"{subdir}", guide_style="bold bright_blue")
|
|
@@ -49,7 +48,12 @@ def print_ls_tree(source: os.PathLike, ls: typing.List[str]):
|
|
|
49
48
|
else:
|
|
50
49
|
current = trees[current_path]
|
|
51
50
|
trees[fpp.parent].add(f"{fpp.name}", guide_style="bold bright_blue")
|
|
52
|
-
|
|
51
|
+
|
|
52
|
+
console = _get_console()
|
|
53
|
+
with console.capture() as capture:
|
|
54
|
+
console.print(tree_root, overflow="ignore", no_wrap=True, crop=False)
|
|
55
|
+
logger.info(f"Root directory: [link=file://{source}]{source}[/link]")
|
|
56
|
+
logger.info(capture.get(), extra={"console": console})
|
|
53
57
|
|
|
54
58
|
|
|
55
59
|
def _compress_tarball(source: pathlib.Path, output: pathlib.Path) -> None:
|
flyte/_code_bundle/_utils.py
CHANGED
|
@@ -156,7 +156,7 @@ def list_all_files(source_path: pathlib.Path, deref_symlinks, ignore_group: Opti
|
|
|
156
156
|
|
|
157
157
|
# This is needed to prevent infinite recursion when walking with followlinks
|
|
158
158
|
visited_inodes = set()
|
|
159
|
-
for root, dirnames, files in
|
|
159
|
+
for root, dirnames, files in os.walk(source_path, topdown=True, followlinks=deref_symlinks):
|
|
160
160
|
dirnames[:] = [d for d in dirnames if d not in EXCLUDE_DIRS]
|
|
161
161
|
if deref_symlinks:
|
|
162
162
|
inode = os.stat(root).st_ino
|
|
@@ -167,7 +167,7 @@ def list_all_files(source_path: pathlib.Path, deref_symlinks, ignore_group: Opti
|
|
|
167
167
|
ff = []
|
|
168
168
|
files.sort()
|
|
169
169
|
for fname in files:
|
|
170
|
-
abspath = (root / fname).absolute()
|
|
170
|
+
abspath = (pathlib.Path(root) / fname).absolute()
|
|
171
171
|
# Only consider files that exist (e.g. disregard symlinks that point to non-existent files)
|
|
172
172
|
if not os.path.exists(abspath):
|
|
173
173
|
logger.info(f"Skipping non-existent file {abspath}")
|
|
@@ -193,15 +193,15 @@ def list_all_files(source_path: pathlib.Path, deref_symlinks, ignore_group: Opti
|
|
|
193
193
|
def _file_is_in_directory(file: str, directory: str) -> bool:
|
|
194
194
|
"""Return True if file is in directory and in its children."""
|
|
195
195
|
try:
|
|
196
|
-
return
|
|
197
|
-
except
|
|
198
|
-
#
|
|
199
|
-
logger.debug(f"{file} and {directory}
|
|
196
|
+
return pathlib.Path(file).resolve().is_relative_to(pathlib.Path(directory).resolve())
|
|
197
|
+
except OSError as e:
|
|
198
|
+
# OSError can be raised if paths cannot be resolved (permissions, broken symlinks, etc.)
|
|
199
|
+
logger.debug(f"Failed to resolve paths for {file} and {directory}: {e!s}")
|
|
200
200
|
return False
|
|
201
201
|
|
|
202
202
|
|
|
203
203
|
def list_imported_modules_as_files(source_path: str, modules: List[ModuleType]) -> List[str]:
|
|
204
|
-
"""
|
|
204
|
+
"""Lists the files of modules that have been loaded. The files are only included if:
|
|
205
205
|
|
|
206
206
|
1. Not a site-packages. These are installed packages and not user files.
|
|
207
207
|
2. Not in the sys.base_prefix or sys.prefix. These are also installed and not user files.
|
|
@@ -211,7 +211,7 @@ def list_imported_modules_as_files(source_path: str, modules: List[ModuleType])
|
|
|
211
211
|
import flyte
|
|
212
212
|
from flyte._utils.lazy_module import is_imported
|
|
213
213
|
|
|
214
|
-
files =
|
|
214
|
+
files = set()
|
|
215
215
|
flyte_root = os.path.dirname(flyte.__file__)
|
|
216
216
|
|
|
217
217
|
# These directories contain installed packages or modules from the Python standard library.
|
|
@@ -240,11 +240,19 @@ def list_imported_modules_as_files(source_path: str, modules: List[ModuleType])
|
|
|
240
240
|
|
|
241
241
|
if not _file_is_in_directory(mod_file, source_path):
|
|
242
242
|
# Only upload files where the module file in the source directory
|
|
243
|
+
# print log line for files that have common ancestor with source_path, but not in it.
|
|
244
|
+
logger.debug(f"{mod_file} is not in {source_path}")
|
|
245
|
+
continue
|
|
246
|
+
|
|
247
|
+
if not pathlib.Path(mod_file).is_file():
|
|
248
|
+
# Some modules have a __file__ attribute that are relative to the base package. Let's skip these,
|
|
249
|
+
# can add more rigorous logic to really pull out the correct file location if we need to.
|
|
250
|
+
logger.debug(f"Skipping {mod_file} from {mod.__name__} because it is not a file")
|
|
243
251
|
continue
|
|
244
252
|
|
|
245
|
-
files.
|
|
253
|
+
files.add(mod_file)
|
|
246
254
|
|
|
247
|
-
return files
|
|
255
|
+
return list(files)
|
|
248
256
|
|
|
249
257
|
|
|
250
258
|
def add_imported_modules_from_source(source_path: str, destination: str, modules: List[ModuleType]):
|