metaflow 2.12.12__py2.py3-none-any.whl → 2.12.14__py2.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.
- metaflow/cli.py +3 -3
- metaflow/client/core.py +2 -0
- metaflow/flowspec.py +2 -1
- metaflow/graph.py +7 -3
- metaflow/metaflow_config.py +11 -2
- metaflow/metaflow_config_funcs.py +51 -3
- metaflow/metaflow_environment.py +36 -21
- metaflow/plugins/argo/argo_workflows.py +9 -0
- metaflow/plugins/argo/argo_workflows_cli.py +2 -2
- metaflow/plugins/argo/argo_workflows_deployer.py +3 -1
- metaflow/plugins/aws/aws_utils.py +16 -0
- metaflow/plugins/aws/batch/batch_decorator.py +1 -0
- metaflow/plugins/aws/step_functions/step_functions_deployer.py +3 -1
- metaflow/plugins/kubernetes/kubernetes.py +26 -0
- metaflow/plugins/kubernetes/kubernetes_decorator.py +15 -0
- metaflow/plugins/pypi/conda_decorator.py +4 -4
- metaflow/plugins/pypi/pypi_decorator.py +13 -4
- metaflow/runner/deployer.py +46 -3
- metaflow/runner/metaflow_runner.py +7 -1
- metaflow/runner/nbdeploy.py +3 -0
- metaflow/runner/nbrun.py +5 -0
- metaflow/runtime.py +26 -13
- metaflow/util.py +19 -0
- metaflow/version.py +1 -1
- {metaflow-2.12.12.dist-info → metaflow-2.12.14.dist-info}/METADATA +2 -2
- {metaflow-2.12.12.dist-info → metaflow-2.12.14.dist-info}/RECORD +30 -30
- {metaflow-2.12.12.dist-info → metaflow-2.12.14.dist-info}/WHEEL +1 -1
- {metaflow-2.12.12.dist-info → metaflow-2.12.14.dist-info}/LICENSE +0 -0
- {metaflow-2.12.12.dist-info → metaflow-2.12.14.dist-info}/entry_points.txt +0 -0
- {metaflow-2.12.12.dist-info → metaflow-2.12.14.dist-info}/top_level.txt +0 -0
metaflow/cli.py
CHANGED
@@ -650,7 +650,7 @@ def resume(
|
|
650
650
|
)
|
651
651
|
|
652
652
|
if step_to_rerun is None:
|
653
|
-
|
653
|
+
steps_to_rerun = set()
|
654
654
|
else:
|
655
655
|
# validate step name
|
656
656
|
if step_to_rerun not in obj.graph.nodes:
|
@@ -660,7 +660,7 @@ def resume(
|
|
660
660
|
step_to_rerun, ",".join(list(obj.graph.nodes.keys()))
|
661
661
|
)
|
662
662
|
)
|
663
|
-
|
663
|
+
steps_to_rerun = {step_to_rerun}
|
664
664
|
|
665
665
|
if run_id:
|
666
666
|
# Run-ids that are provided by the metadata service are always integers.
|
@@ -688,7 +688,7 @@ def resume(
|
|
688
688
|
clone_run_id=origin_run_id,
|
689
689
|
clone_only=clone_only,
|
690
690
|
reentrant=reentrant,
|
691
|
-
|
691
|
+
steps_to_rerun=steps_to_rerun,
|
692
692
|
max_workers=max_workers,
|
693
693
|
max_num_splits=max_num_splits,
|
694
694
|
max_log_size=max_log_size * 1024 * 1024,
|
metaflow/client/core.py
CHANGED
@@ -1027,6 +1027,8 @@ class MetaflowData(object):
|
|
1027
1027
|
self._artifacts = dict((art.id, art) for art in artifacts)
|
1028
1028
|
|
1029
1029
|
def __getattr__(self, name: str):
|
1030
|
+
if name not in self._artifacts:
|
1031
|
+
raise AttributeError(name)
|
1030
1032
|
return self._artifacts[name].data
|
1031
1033
|
|
1032
1034
|
def __contains__(self, var):
|
metaflow/flowspec.py
CHANGED
@@ -17,6 +17,7 @@ from .exception import (
|
|
17
17
|
)
|
18
18
|
from .graph import FlowGraph
|
19
19
|
from .unbounded_foreach import UnboundedForeachInput
|
20
|
+
from .util import to_pod
|
20
21
|
from .metaflow_config import INCLUDE_FOREACH_STACK, MAXIMUM_FOREACH_VALUE_CHARS
|
21
22
|
|
22
23
|
# For Python 3 compatibility
|
@@ -201,7 +202,7 @@ class FlowSpec(metaclass=_FlowSpecMeta):
|
|
201
202
|
"decorators": [
|
202
203
|
{
|
203
204
|
"name": deco.name,
|
204
|
-
"attributes": deco.attributes,
|
205
|
+
"attributes": to_pod(deco.attributes),
|
205
206
|
"statically_defined": deco.statically_defined,
|
206
207
|
}
|
207
208
|
for deco in flow_decorators(self)
|
metaflow/graph.py
CHANGED
@@ -3,6 +3,9 @@ import ast
|
|
3
3
|
import re
|
4
4
|
|
5
5
|
|
6
|
+
from .util import to_pod
|
7
|
+
|
8
|
+
|
6
9
|
def deindent_docstring(doc):
|
7
10
|
if doc:
|
8
11
|
# Find the indent to remove from the docstring. We consider the following possibilities:
|
@@ -72,7 +75,6 @@ class DAGNode(object):
|
|
72
75
|
return "%s.%s" % (expr.value.id, expr.attr)
|
73
76
|
|
74
77
|
def _parse(self, func_ast):
|
75
|
-
|
76
78
|
self.num_args = len(func_ast.args.args)
|
77
79
|
tail = func_ast.body[-1]
|
78
80
|
|
@@ -171,6 +173,8 @@ class FlowGraph(object):
|
|
171
173
|
self.name = flow.__name__
|
172
174
|
self.nodes = self._create_nodes(flow)
|
173
175
|
self.doc = deindent_docstring(flow.__doc__)
|
176
|
+
# nodes sorted in topological order.
|
177
|
+
self.sorted_nodes = []
|
174
178
|
self._traverse_graph()
|
175
179
|
self._postprocess()
|
176
180
|
|
@@ -197,6 +201,7 @@ class FlowGraph(object):
|
|
197
201
|
|
198
202
|
def _traverse_graph(self):
|
199
203
|
def traverse(node, seen, split_parents):
|
204
|
+
self.sorted_nodes.append(node.name)
|
200
205
|
if node.type in ("split", "foreach"):
|
201
206
|
node.split_parents = split_parents
|
202
207
|
split_parents = split_parents + [node.name]
|
@@ -262,7 +267,6 @@ class FlowGraph(object):
|
|
262
267
|
)
|
263
268
|
|
264
269
|
def output_steps(self):
|
265
|
-
|
266
270
|
steps_info = {}
|
267
271
|
graph_structure = []
|
268
272
|
|
@@ -286,7 +290,7 @@ class FlowGraph(object):
|
|
286
290
|
"decorators": [
|
287
291
|
{
|
288
292
|
"name": deco.name,
|
289
|
-
"attributes": deco.attributes,
|
293
|
+
"attributes": to_pod(deco.attributes),
|
290
294
|
"statically_defined": deco.statically_defined,
|
291
295
|
}
|
292
296
|
for deco in node.decorators
|
metaflow/metaflow_config.py
CHANGED
@@ -15,6 +15,17 @@ if sys.platform == "darwin":
|
|
15
15
|
## value, either set `METAFLOW_DEFAULT_DATASTORE` in your configuration file or set
|
16
16
|
## an environment variable called `METAFLOW_DEFAULT_DATASTORE`
|
17
17
|
|
18
|
+
##
|
19
|
+
# Constants (NOTE: these need to live before any from_conf)
|
20
|
+
##
|
21
|
+
|
22
|
+
# Path to the local directory to store artifacts for 'local' datastore.
|
23
|
+
DATASTORE_LOCAL_DIR = ".metaflow"
|
24
|
+
|
25
|
+
# Local configuration file (in .metaflow) containing overrides per-project
|
26
|
+
LOCAL_CONFIG_FILE = "config.json"
|
27
|
+
|
28
|
+
|
18
29
|
###
|
19
30
|
# Default configuration
|
20
31
|
###
|
@@ -42,8 +53,6 @@ USER = from_conf("USER")
|
|
42
53
|
###
|
43
54
|
# Datastore configuration
|
44
55
|
###
|
45
|
-
# Path to the local directory to store artifacts for 'local' datastore.
|
46
|
-
DATASTORE_LOCAL_DIR = ".metaflow"
|
47
56
|
DATASTORE_SYSROOT_LOCAL = from_conf("DATASTORE_SYSROOT_LOCAL")
|
48
57
|
# S3 bucket and prefix to store artifacts for 's3' datastore.
|
49
58
|
DATASTORE_SYSROOT_S3 = from_conf("DATASTORE_SYSROOT_S3")
|
@@ -32,8 +32,40 @@ def init_config():
|
|
32
32
|
return config
|
33
33
|
|
34
34
|
|
35
|
+
def init_local_config():
|
36
|
+
# This function is heavily inspired from LocalStorage.get_datastore_root_from_config
|
37
|
+
# but simplifies certain things and also does not depend on DATASTORE_SYSROOT_LOCAL.
|
38
|
+
#
|
39
|
+
# In other words, since this config is meant to be local to a directory, it does not
|
40
|
+
# check in DATASTORE_SYSROOT_LOCAL but only up the current getcwd() path. This also
|
41
|
+
# prevents nasty circular dependencies :)
|
42
|
+
|
43
|
+
from metaflow.metaflow_config import DATASTORE_LOCAL_DIR, LOCAL_CONFIG_FILE
|
44
|
+
|
45
|
+
current_path = os.getcwd()
|
46
|
+
check_dir = os.path.join(current_path, DATASTORE_LOCAL_DIR)
|
47
|
+
check_dir = os.path.realpath(check_dir)
|
48
|
+
while not os.path.isdir(check_dir):
|
49
|
+
new_path = os.path.dirname(current_path)
|
50
|
+
if new_path == current_path: # No longer making upward progress
|
51
|
+
return {}
|
52
|
+
current_path = new_path
|
53
|
+
check_dir = os.path.join(current_path, DATASTORE_LOCAL_DIR)
|
54
|
+
path_to_config = os.path.join(check_dir, LOCAL_CONFIG_FILE)
|
55
|
+
# We found a directory to look for the config file in
|
56
|
+
if os.path.exists(path_to_config):
|
57
|
+
with open(path_to_config, encoding="utf-8") as f:
|
58
|
+
return json.load(f)
|
59
|
+
return {}
|
60
|
+
|
61
|
+
|
35
62
|
# Initialize defaults required to setup environment variables.
|
36
|
-
|
63
|
+
# (initialized lazily in from_conf since init_local_config requires
|
64
|
+
# some configuration values
|
65
|
+
|
66
|
+
METAFLOW_CONFIG = None
|
67
|
+
|
68
|
+
METAFLOW_LOCAL_CONFIG = None
|
37
69
|
|
38
70
|
_all_configs = {}
|
39
71
|
|
@@ -51,7 +83,13 @@ def config_values(include=0):
|
|
51
83
|
|
52
84
|
def from_conf(name, default=None, validate_fn=None):
|
53
85
|
"""
|
54
|
-
|
86
|
+
Pull value from the environment or configuration.
|
87
|
+
Order is:
|
88
|
+
1. Environment (use any environment variable explicitly set by user)
|
89
|
+
2. Local config (use any value set in the local config file -- so stuff in
|
90
|
+
.metaflow/project.json for example)
|
91
|
+
3. Global config (use any value set in the global config file)
|
92
|
+
4. Default
|
55
93
|
|
56
94
|
Prior to a value being returned, we will validate using validate_fn (if provided).
|
57
95
|
Only non-None values are validated.
|
@@ -59,9 +97,19 @@ def from_conf(name, default=None, validate_fn=None):
|
|
59
97
|
validate_fn should accept (name, value).
|
60
98
|
If the value validates, return None, else raise an MetaflowException.
|
61
99
|
"""
|
100
|
+
global METAFLOW_CONFIG, METAFLOW_LOCAL_CONFIG
|
101
|
+
|
102
|
+
if METAFLOW_CONFIG is None:
|
103
|
+
METAFLOW_CONFIG = init_config()
|
104
|
+
if METAFLOW_LOCAL_CONFIG is None:
|
105
|
+
METAFLOW_LOCAL_CONFIG = init_local_config()
|
106
|
+
|
62
107
|
is_default = True
|
63
108
|
env_name = "METAFLOW_%s" % name
|
64
|
-
value = os.environ.get(
|
109
|
+
value = os.environ.get(
|
110
|
+
env_name,
|
111
|
+
METAFLOW_LOCAL_CONFIG.get(env_name, METAFLOW_CONFIG.get(env_name, default)),
|
112
|
+
)
|
65
113
|
if validate_fn and value is not None:
|
66
114
|
validate_fn(env_name, value)
|
67
115
|
if default is not None:
|
metaflow/metaflow_environment.py
CHANGED
@@ -89,10 +89,16 @@ class MetaflowEnvironment(object):
|
|
89
89
|
It should work silently if everything goes well.
|
90
90
|
"""
|
91
91
|
if datastore_type == "s3":
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
92
|
+
from .plugins.aws.aws_utils import parse_s3_full_path
|
93
|
+
|
94
|
+
bucket, s3_object = parse_s3_full_path(code_package_url)
|
95
|
+
# NOTE: the script quoting is extremely sensitive due to the way shlex.split operates and this being inserted
|
96
|
+
# into a quoted command elsewhere.
|
97
|
+
return "{python} -c '{script}'".format(
|
98
|
+
python=self._python(),
|
99
|
+
script='import boto3, os; boto3.client(\\"s3\\", endpoint_url=os.getenv(\\"METAFLOW_S3_ENDPOINT_URL\\")).download_file(\\"%s\\", \\"%s\\", \\"job.tar\\")'
|
100
|
+
% (bucket, s3_object),
|
101
|
+
)
|
96
102
|
elif datastore_type == "azure":
|
97
103
|
from .plugins.azure.azure_utils import parse_azure_full_path
|
98
104
|
|
@@ -119,25 +125,34 @@ class MetaflowEnvironment(object):
|
|
119
125
|
)
|
120
126
|
|
121
127
|
def _get_install_dependencies_cmd(self, datastore_type):
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
"
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
128
|
+
base_cmd = "{} -m pip install -qqq".format(self._python())
|
129
|
+
|
130
|
+
datastore_packages = {
|
131
|
+
"s3": ["boto3"],
|
132
|
+
"azure": [
|
133
|
+
"azure-identity",
|
134
|
+
"azure-storage-blob",
|
135
|
+
"azure-keyvault-secrets",
|
136
|
+
"simple-azure-blob-downloader",
|
137
|
+
],
|
138
|
+
"gs": [
|
139
|
+
"google-cloud-storage",
|
140
|
+
"google-auth",
|
141
|
+
"simple-gcp-object-downloader",
|
142
|
+
"google-cloud-secret-manager",
|
143
|
+
],
|
144
|
+
}
|
145
|
+
|
146
|
+
if datastore_type not in datastore_packages:
|
136
147
|
raise NotImplementedError(
|
137
|
-
"
|
138
|
-
% datastore_type
|
148
|
+
"Unknown datastore type: {}".format(datastore_type)
|
139
149
|
)
|
140
|
-
|
150
|
+
|
151
|
+
cmd = "{} {}".format(
|
152
|
+
base_cmd, " ".join(datastore_packages[datastore_type] + ["requests"])
|
153
|
+
)
|
154
|
+
# skip pip installs if we know that packages might already be available
|
155
|
+
return "if [ -z $METAFLOW_SKIP_INSTALL_DEPENDENCIES ]; then {}; fi".format(cmd)
|
141
156
|
|
142
157
|
def get_package_commands(self, code_package_url, datastore_type):
|
143
158
|
cmds = [
|
@@ -1658,6 +1658,9 @@ class ArgoWorkflows(object):
|
|
1658
1658
|
|
1659
1659
|
# support Metaflow sandboxes
|
1660
1660
|
env["METAFLOW_INIT_SCRIPT"] = KUBERNETES_SANDBOX_INIT_SCRIPT
|
1661
|
+
env["METAFLOW_KUBERNETES_SANDBOX_INIT_SCRIPT"] = (
|
1662
|
+
KUBERNETES_SANDBOX_INIT_SCRIPT
|
1663
|
+
)
|
1661
1664
|
|
1662
1665
|
# support for @secret
|
1663
1666
|
env["METAFLOW_DEFAULT_SECRETS_BACKEND_TYPE"] = DEFAULT_SECRETS_BACKEND_TYPE
|
@@ -1673,6 +1676,12 @@ class ArgoWorkflows(object):
|
|
1673
1676
|
)
|
1674
1677
|
env["METAFLOW_DATASTORE_SYSROOT_AZURE"] = DATASTORE_SYSROOT_AZURE
|
1675
1678
|
env["METAFLOW_CARD_AZUREROOT"] = CARD_AZUREROOT
|
1679
|
+
env["METAFLOW_ARGO_WORKFLOWS_KUBERNETES_SECRETS"] = (
|
1680
|
+
ARGO_WORKFLOWS_KUBERNETES_SECRETS
|
1681
|
+
)
|
1682
|
+
env["METAFLOW_ARGO_WORKFLOWS_ENV_VARS_TO_SKIP"] = (
|
1683
|
+
ARGO_WORKFLOWS_ENV_VARS_TO_SKIP
|
1684
|
+
)
|
1676
1685
|
|
1677
1686
|
# support for GCP
|
1678
1687
|
env["METAFLOW_DATASTORE_SYSROOT_GS"] = DATASTORE_SYSROOT_GS
|
@@ -172,7 +172,7 @@ def argo_workflows(obj, name=None):
|
|
172
172
|
)
|
173
173
|
@click.option(
|
174
174
|
"--enable-heartbeat-daemon/--no-enable-heartbeat-daemon",
|
175
|
-
default=
|
175
|
+
default=True,
|
176
176
|
show_default=True,
|
177
177
|
help="Use a daemon container to broadcast heartbeats.",
|
178
178
|
)
|
@@ -186,7 +186,7 @@ def argo_workflows(obj, name=None):
|
|
186
186
|
)
|
187
187
|
@click.option(
|
188
188
|
"--enable-error-msg-capture/--no-enable-error-msg-capture",
|
189
|
-
default=
|
189
|
+
default=True,
|
190
190
|
show_default=True,
|
191
191
|
help="Capture stack trace of first failed task in exit hook.",
|
192
192
|
)
|
@@ -226,7 +226,9 @@ def trigger(instance: DeployedFlow, **kwargs):
|
|
226
226
|
)
|
227
227
|
|
228
228
|
command_obj = instance.deployer.spm.get(pid)
|
229
|
-
content = handle_timeout(
|
229
|
+
content = handle_timeout(
|
230
|
+
tfp_runner_attribute, command_obj, instance.deployer.file_read_timeout
|
231
|
+
)
|
230
232
|
|
231
233
|
if command_obj.process.returncode == 0:
|
232
234
|
triggered_run = TriggeredRun(deployer=instance.deployer, content=content)
|
@@ -4,6 +4,22 @@ import requests
|
|
4
4
|
from metaflow.exception import MetaflowException
|
5
5
|
|
6
6
|
|
7
|
+
def parse_s3_full_path(s3_uri):
|
8
|
+
from urllib.parse import urlparse
|
9
|
+
|
10
|
+
# <scheme>://<netloc>/<path>;<params>?<query>#<fragment>
|
11
|
+
scheme, netloc, path, _, _, _ = urlparse(s3_uri)
|
12
|
+
assert scheme == "s3"
|
13
|
+
assert netloc is not None
|
14
|
+
|
15
|
+
bucket = netloc
|
16
|
+
path = path.lstrip("/").rstrip("/")
|
17
|
+
if path == "":
|
18
|
+
path = None
|
19
|
+
|
20
|
+
return bucket, path
|
21
|
+
|
22
|
+
|
7
23
|
def get_ec2_instance_metadata():
|
8
24
|
"""
|
9
25
|
Fetches the EC2 instance metadata through AWS instance metadata service
|
@@ -193,7 +193,9 @@ def trigger(instance: DeployedFlow, **kwargs):
|
|
193
193
|
)
|
194
194
|
|
195
195
|
command_obj = instance.deployer.spm.get(pid)
|
196
|
-
content = handle_timeout(
|
196
|
+
content = handle_timeout(
|
197
|
+
tfp_runner_attribute, command_obj, instance.deployer.file_read_timeout
|
198
|
+
)
|
197
199
|
|
198
200
|
if command_obj.process.returncode == 0:
|
199
201
|
triggered_run = TriggeredRun(deployer=instance.deployer, content=content)
|
@@ -17,6 +17,8 @@ from metaflow.metaflow_config import (
|
|
17
17
|
ARGO_EVENTS_INTERNAL_WEBHOOK_URL,
|
18
18
|
ARGO_EVENTS_SERVICE_ACCOUNT,
|
19
19
|
ARGO_EVENTS_WEBHOOK_AUTH,
|
20
|
+
ARGO_WORKFLOWS_KUBERNETES_SECRETS,
|
21
|
+
ARGO_WORKFLOWS_ENV_VARS_TO_SKIP,
|
20
22
|
AWS_SECRETS_MANAGER_DEFAULT_REGION,
|
21
23
|
AZURE_KEY_VAULT_PREFIX,
|
22
24
|
AZURE_STORAGE_BLOB_SERVICE_ENDPOINT,
|
@@ -280,6 +282,18 @@ class Kubernetes(object):
|
|
280
282
|
.environment_variable(
|
281
283
|
"METAFLOW_INIT_SCRIPT", KUBERNETES_SANDBOX_INIT_SCRIPT
|
282
284
|
)
|
285
|
+
.environment_variable(
|
286
|
+
"METAFLOW_KUBERNETES_SANDBOX_INIT_SCRIPT",
|
287
|
+
KUBERNETES_SANDBOX_INIT_SCRIPT,
|
288
|
+
)
|
289
|
+
.environment_variable(
|
290
|
+
"METAFLOW_ARGO_WORKFLOWS_KUBERNETES_SECRETS",
|
291
|
+
ARGO_WORKFLOWS_KUBERNETES_SECRETS,
|
292
|
+
)
|
293
|
+
.environment_variable(
|
294
|
+
"METAFLOW_ARGO_WORKFLOWS_ENV_VARS_TO_SKIP",
|
295
|
+
ARGO_WORKFLOWS_ENV_VARS_TO_SKIP,
|
296
|
+
)
|
283
297
|
.environment_variable("METAFLOW_OTEL_ENDPOINT", OTEL_ENDPOINT)
|
284
298
|
# Skip setting METAFLOW_DATASTORE_SYSROOT_LOCAL because metadata sync
|
285
299
|
# between the local user instance and the remote Kubernetes pod
|
@@ -565,6 +579,18 @@ class Kubernetes(object):
|
|
565
579
|
.environment_variable(
|
566
580
|
"METAFLOW_INIT_SCRIPT", KUBERNETES_SANDBOX_INIT_SCRIPT
|
567
581
|
)
|
582
|
+
.environment_variable(
|
583
|
+
"METAFLOW_KUBERNETES_SANDBOX_INIT_SCRIPT",
|
584
|
+
KUBERNETES_SANDBOX_INIT_SCRIPT,
|
585
|
+
)
|
586
|
+
.environment_variable(
|
587
|
+
"METAFLOW_ARGO_WORKFLOWS_KUBERNETES_SECRETS",
|
588
|
+
ARGO_WORKFLOWS_KUBERNETES_SECRETS,
|
589
|
+
)
|
590
|
+
.environment_variable(
|
591
|
+
"METAFLOW_ARGO_WORKFLOWS_ENV_VARS_TO_SKIP",
|
592
|
+
ARGO_WORKFLOWS_ENV_VARS_TO_SKIP,
|
593
|
+
)
|
568
594
|
.environment_variable("METAFLOW_OTEL_ENDPOINT", OTEL_ENDPOINT)
|
569
595
|
# Skip setting METAFLOW_DATASTORE_SYSROOT_LOCAL because metadata sync
|
570
596
|
# between the local user instance and the remote Kubernetes pod
|
@@ -97,6 +97,9 @@ class KubernetesDecorator(StepDecorator):
|
|
97
97
|
Shared memory size (in MiB) required for this step
|
98
98
|
port: int, optional
|
99
99
|
Port number to specify in the Kubernetes job object
|
100
|
+
compute_pool : str, optional, default None
|
101
|
+
Compute pool to be used for for this step.
|
102
|
+
If not specified, any accessible compute pool within the perimeter is used.
|
100
103
|
"""
|
101
104
|
|
102
105
|
name = "kubernetes"
|
@@ -121,6 +124,8 @@ class KubernetesDecorator(StepDecorator):
|
|
121
124
|
"persistent_volume_claims": None, # e.g., {"pvc-name": "/mnt/vol", "another-pvc": "/mnt/vol2"}
|
122
125
|
"shared_memory": None,
|
123
126
|
"port": None,
|
127
|
+
"compute_pool": None,
|
128
|
+
"executable": None,
|
124
129
|
}
|
125
130
|
package_url = None
|
126
131
|
package_sha = None
|
@@ -153,6 +158,12 @@ class KubernetesDecorator(StepDecorator):
|
|
153
158
|
self.attributes["node_selector"] = parse_kube_keyvalue_list(
|
154
159
|
self.attributes["node_selector"].split(",")
|
155
160
|
)
|
161
|
+
if self.attributes["compute_pool"]:
|
162
|
+
if self.attributes["node_selector"] is None:
|
163
|
+
self.attributes["node_selector"] = {}
|
164
|
+
self.attributes["node_selector"].update(
|
165
|
+
{"outerbounds.co/compute-pool": self.attributes["compute_pool"]}
|
166
|
+
)
|
156
167
|
|
157
168
|
if self.attributes["tolerations"]:
|
158
169
|
try:
|
@@ -370,9 +381,13 @@ class KubernetesDecorator(StepDecorator):
|
|
370
381
|
cli_args.command_args.append(self.package_sha)
|
371
382
|
cli_args.command_args.append(self.package_url)
|
372
383
|
|
384
|
+
# skip certain keys as CLI arguments
|
385
|
+
_skip_keys = ["compute_pool"]
|
373
386
|
# --namespace is used to specify Metaflow namespace (a different
|
374
387
|
# concept from k8s namespace).
|
375
388
|
for k, v in self.attributes.items():
|
389
|
+
if k in _skip_keys:
|
390
|
+
continue
|
376
391
|
if k == "namespace":
|
377
392
|
cli_args.command_options["k8s_namespace"] = v
|
378
393
|
elif k in {"node_selector"} and v:
|
@@ -100,9 +100,9 @@ class CondaStepDecorator(StepDecorator):
|
|
100
100
|
# --environment=pypi to --environment=conda
|
101
101
|
_supported_virtual_envs.extend(["pypi"])
|
102
102
|
|
103
|
-
# TODO: Hardcoded for now to support
|
103
|
+
# TODO: Hardcoded for now to support the fast bakery environment.
|
104
104
|
# We should introduce a more robust mechanism for appending supported environments, for example from within extensions.
|
105
|
-
_supported_virtual_envs.extend(["
|
105
|
+
_supported_virtual_envs.extend(["fast-bakery"])
|
106
106
|
|
107
107
|
# The --environment= requirement ensures that valid virtual environments are
|
108
108
|
# created for every step to execute it, greatly simplifying the @conda
|
@@ -344,9 +344,9 @@ class CondaFlowDecorator(FlowDecorator):
|
|
344
344
|
# --environment=pypi to --environment=conda
|
345
345
|
_supported_virtual_envs.extend(["pypi"])
|
346
346
|
|
347
|
-
# TODO: Hardcoded for now to support
|
347
|
+
# TODO: Hardcoded for now to support the fast bakery environment.
|
348
348
|
# We should introduce a more robust mechanism for appending supported environments, for example from within extensions.
|
349
|
-
_supported_virtual_envs.extend(["
|
349
|
+
_supported_virtual_envs.extend(["fast-bakery"])
|
350
350
|
|
351
351
|
# The --environment= requirement ensures that valid virtual environments are
|
352
352
|
# created for every step to execute it, greatly simplifying the @conda
|
@@ -24,6 +24,12 @@ class PyPIStepDecorator(StepDecorator):
|
|
24
24
|
name = "pypi"
|
25
25
|
defaults = {"packages": {}, "python": None, "disabled": None} # wheels
|
26
26
|
|
27
|
+
def __init__(self, attributes=None, statically_defined=False):
|
28
|
+
self._user_defined_attributes = (
|
29
|
+
attributes.copy() if attributes is not None else {}
|
30
|
+
)
|
31
|
+
super().__init__(attributes, statically_defined)
|
32
|
+
|
27
33
|
def step_init(self, flow, graph, step, decos, environment, flow_datastore, logger):
|
28
34
|
# The init_environment hook for Environment creates the relevant virtual
|
29
35
|
# environments. The step_init hook sets up the relevant state for that hook to
|
@@ -70,9 +76,9 @@ class PyPIStepDecorator(StepDecorator):
|
|
70
76
|
# --environment=pypi to --environment=conda
|
71
77
|
_supported_virtual_envs.extend(["pypi"])
|
72
78
|
|
73
|
-
# TODO: Hardcoded for now to support
|
79
|
+
# TODO: Hardcoded for now to support the fast bakery environment.
|
74
80
|
# We should introduce a more robust mechanism for appending supported environments, for example from within extensions.
|
75
|
-
_supported_virtual_envs.extend(["
|
81
|
+
_supported_virtual_envs.extend(["fast-bakery"])
|
76
82
|
|
77
83
|
# The --environment= requirement ensures that valid virtual environments are
|
78
84
|
# created for every step to execute it, greatly simplifying the @pypi
|
@@ -88,6 +94,9 @@ class PyPIStepDecorator(StepDecorator):
|
|
88
94
|
)
|
89
95
|
)
|
90
96
|
|
97
|
+
def is_attribute_user_defined(self, name):
|
98
|
+
return name in self._user_defined_attributes
|
99
|
+
|
91
100
|
|
92
101
|
class PyPIFlowDecorator(FlowDecorator):
|
93
102
|
"""
|
@@ -123,9 +132,9 @@ class PyPIFlowDecorator(FlowDecorator):
|
|
123
132
|
# --environment=pypi to --environment=conda
|
124
133
|
_supported_virtual_envs.extend(["pypi"])
|
125
134
|
|
126
|
-
# TODO: Hardcoded for now to support
|
135
|
+
# TODO: Hardcoded for now to support the fast bakery environment.
|
127
136
|
# We should introduce a more robust mechanism for appending supported environments, for example from within extensions.
|
128
|
-
_supported_virtual_envs.extend(["
|
137
|
+
_supported_virtual_envs.extend(["fast-bakery"])
|
129
138
|
|
130
139
|
# The --environment= requirement ensures that valid virtual environments are
|
131
140
|
# created for every step to execute it, greatly simplifying the @conda
|
metaflow/runner/deployer.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
import os
|
2
2
|
import sys
|
3
3
|
import json
|
4
|
+
import time
|
4
5
|
import importlib
|
5
6
|
import functools
|
6
7
|
import tempfile
|
@@ -11,7 +12,9 @@ from metaflow.runner.subprocess_manager import CommandManager, SubprocessManager
|
|
11
12
|
from metaflow.runner.utils import read_from_file_when_ready
|
12
13
|
|
13
14
|
|
14
|
-
def handle_timeout(
|
15
|
+
def handle_timeout(
|
16
|
+
tfp_runner_attribute, command_obj: CommandManager, file_read_timeout: int
|
17
|
+
):
|
15
18
|
"""
|
16
19
|
Handle the timeout for a running subprocess command that reads a file
|
17
20
|
and raises an error with appropriate logs if a TimeoutError occurs.
|
@@ -35,7 +38,9 @@ def handle_timeout(tfp_runner_attribute, command_obj: CommandManager):
|
|
35
38
|
stdout and stderr logs.
|
36
39
|
"""
|
37
40
|
try:
|
38
|
-
content = read_from_file_when_ready(
|
41
|
+
content = read_from_file_when_ready(
|
42
|
+
tfp_runner_attribute.name, timeout=file_read_timeout
|
43
|
+
)
|
39
44
|
return content
|
40
45
|
except TimeoutError as e:
|
41
46
|
stdout_log = open(command_obj.log_files["stdout"]).read()
|
@@ -102,6 +107,8 @@ class Deployer(object):
|
|
102
107
|
cwd : Optional[str], default None
|
103
108
|
The directory to run the subprocess in; if not specified, the current
|
104
109
|
directory is used.
|
110
|
+
file_read_timeout : int, default 3600
|
111
|
+
The timeout until which we try to read the deployer attribute file.
|
105
112
|
**kwargs : Any
|
106
113
|
Additional arguments that you would pass to `python myflow.py` before
|
107
114
|
the deployment command.
|
@@ -114,6 +121,7 @@ class Deployer(object):
|
|
114
121
|
profile: Optional[str] = None,
|
115
122
|
env: Optional[Dict] = None,
|
116
123
|
cwd: Optional[str] = None,
|
124
|
+
file_read_timeout: int = 3600,
|
117
125
|
**kwargs
|
118
126
|
):
|
119
127
|
self.flow_file = flow_file
|
@@ -121,6 +129,7 @@ class Deployer(object):
|
|
121
129
|
self.profile = profile
|
122
130
|
self.env = env
|
123
131
|
self.cwd = cwd
|
132
|
+
self.file_read_timeout = file_read_timeout
|
124
133
|
self.top_level_kwargs = kwargs
|
125
134
|
|
126
135
|
from metaflow.plugins import DEPLOYER_IMPL_PROVIDERS
|
@@ -155,6 +164,7 @@ class Deployer(object):
|
|
155
164
|
profile=self.profile,
|
156
165
|
env=self.env,
|
157
166
|
cwd=self.cwd,
|
167
|
+
file_read_timeout=self.file_read_timeout,
|
158
168
|
**self.top_level_kwargs
|
159
169
|
)
|
160
170
|
|
@@ -197,6 +207,33 @@ class TriggeredRun(object):
|
|
197
207
|
else:
|
198
208
|
setattr(self.__class__, k, property(fget=lambda _, v=v: v))
|
199
209
|
|
210
|
+
def wait_for_run(self, timeout=None):
|
211
|
+
"""
|
212
|
+
Wait for the `run` property to become available.
|
213
|
+
|
214
|
+
Parameters
|
215
|
+
----------
|
216
|
+
timeout : int, optional
|
217
|
+
Maximum time to wait for the `run` to become available, in seconds. If None, wait indefinitely.
|
218
|
+
|
219
|
+
Raises
|
220
|
+
------
|
221
|
+
TimeoutError
|
222
|
+
If the `run` is not available within the specified timeout.
|
223
|
+
"""
|
224
|
+
start_time = time.time()
|
225
|
+
check_interval = 5
|
226
|
+
while True:
|
227
|
+
if self.run is not None:
|
228
|
+
return self.run
|
229
|
+
|
230
|
+
if timeout is not None and (time.time() - start_time) > timeout:
|
231
|
+
raise TimeoutError(
|
232
|
+
"Timed out waiting for the run object to become available."
|
233
|
+
)
|
234
|
+
|
235
|
+
time.sleep(check_interval)
|
236
|
+
|
200
237
|
@property
|
201
238
|
def run(self):
|
202
239
|
"""
|
@@ -268,6 +305,8 @@ class DeployerImpl(object):
|
|
268
305
|
cwd : Optional[str], default None
|
269
306
|
The directory to run the subprocess in; if not specified, the current
|
270
307
|
directory is used.
|
308
|
+
file_read_timeout : int, default 3600
|
309
|
+
The timeout until which we try to read the deployer attribute file.
|
271
310
|
**kwargs : Any
|
272
311
|
Additional arguments that you would pass to `python myflow.py` before
|
273
312
|
the deployment command.
|
@@ -282,6 +321,7 @@ class DeployerImpl(object):
|
|
282
321
|
profile: Optional[str] = None,
|
283
322
|
env: Optional[Dict] = None,
|
284
323
|
cwd: Optional[str] = None,
|
324
|
+
file_read_timeout: int = 3600,
|
285
325
|
**kwargs
|
286
326
|
):
|
287
327
|
if self.TYPE is None:
|
@@ -299,6 +339,7 @@ class DeployerImpl(object):
|
|
299
339
|
self.profile = profile
|
300
340
|
self.env = env
|
301
341
|
self.cwd = cwd
|
342
|
+
self.file_read_timeout = file_read_timeout
|
302
343
|
|
303
344
|
self.env_vars = os.environ.copy()
|
304
345
|
self.env_vars.update(self.env or {})
|
@@ -349,7 +390,9 @@ class DeployerImpl(object):
|
|
349
390
|
)
|
350
391
|
|
351
392
|
command_obj = self.spm.get(pid)
|
352
|
-
content = handle_timeout(
|
393
|
+
content = handle_timeout(
|
394
|
+
tfp_runner_attribute, command_obj, self.file_read_timeout
|
395
|
+
)
|
353
396
|
content = json.loads(content)
|
354
397
|
self.name = content.get("name")
|
355
398
|
self.flow_name = content.get("flow_name")
|
@@ -211,6 +211,8 @@ class Runner(object):
|
|
211
211
|
cwd : Optional[str], default None
|
212
212
|
The directory to run the subprocess in; if not specified, the current
|
213
213
|
directory is used.
|
214
|
+
file_read_timeout : int, default 3600
|
215
|
+
The timeout until which we try to read the runner attribute file.
|
214
216
|
**kwargs : Any
|
215
217
|
Additional arguments that you would pass to `python myflow.py` before
|
216
218
|
the `run` command.
|
@@ -223,6 +225,7 @@ class Runner(object):
|
|
223
225
|
profile: Optional[str] = None,
|
224
226
|
env: Optional[Dict] = None,
|
225
227
|
cwd: Optional[str] = None,
|
228
|
+
file_read_timeout: int = 3600,
|
226
229
|
**kwargs
|
227
230
|
):
|
228
231
|
# these imports are required here and not at the top
|
@@ -248,6 +251,7 @@ class Runner(object):
|
|
248
251
|
self.env_vars["METAFLOW_PROFILE"] = profile
|
249
252
|
|
250
253
|
self.cwd = cwd
|
254
|
+
self.file_read_timeout = file_read_timeout
|
251
255
|
self.spm = SubprocessManager()
|
252
256
|
self.top_level_kwargs = kwargs
|
253
257
|
self.api = MetaflowAPI.from_cli(self.flow_file, start)
|
@@ -270,7 +274,9 @@ class Runner(object):
|
|
270
274
|
clear_and_set_os_environ(self.old_env)
|
271
275
|
|
272
276
|
# Set the correct metadata from the runner_attribute file corresponding to this run.
|
273
|
-
content = read_from_file_when_ready(
|
277
|
+
content = read_from_file_when_ready(
|
278
|
+
tfp_runner_attribute.name, timeout=self.file_read_timeout
|
279
|
+
)
|
274
280
|
metadata_for_flow, pathspec = content.rsplit(":", maxsplit=1)
|
275
281
|
metadata(metadata_for_flow)
|
276
282
|
run_object = Run(pathspec, _namespace_check=False)
|
metaflow/runner/nbdeploy.py
CHANGED
@@ -61,6 +61,7 @@ class NBDeployer(object):
|
|
61
61
|
profile: Optional[str] = None,
|
62
62
|
env: Optional[Dict] = None,
|
63
63
|
base_dir: str = DEFAULT_DIR,
|
64
|
+
file_read_timeout: int = 3600,
|
64
65
|
**kwargs,
|
65
66
|
):
|
66
67
|
try:
|
@@ -78,6 +79,7 @@ class NBDeployer(object):
|
|
78
79
|
self.profile = profile
|
79
80
|
self.env = env
|
80
81
|
self.cwd = base_dir
|
82
|
+
self.file_read_timeout = file_read_timeout
|
81
83
|
self.top_level_kwargs = kwargs
|
82
84
|
|
83
85
|
self.env_vars = os.environ.copy()
|
@@ -112,6 +114,7 @@ class NBDeployer(object):
|
|
112
114
|
profile=self.profile,
|
113
115
|
env=self.env_vars,
|
114
116
|
cwd=self.cwd,
|
117
|
+
file_read_timeout=self.file_read_timeout,
|
115
118
|
**kwargs,
|
116
119
|
)
|
117
120
|
|
metaflow/runner/nbrun.py
CHANGED
@@ -45,6 +45,8 @@ class NBRunner(object):
|
|
45
45
|
base_dir : Optional[str], default None
|
46
46
|
The directory to run the subprocess in; if not specified, a temporary
|
47
47
|
directory is used.
|
48
|
+
file_read_timeout : int, default 3600
|
49
|
+
The timeout until which we try to read the runner attribute file.
|
48
50
|
**kwargs : Any
|
49
51
|
Additional arguments that you would pass to `python myflow.py` before
|
50
52
|
the `run` command.
|
@@ -58,6 +60,7 @@ class NBRunner(object):
|
|
58
60
|
profile: Optional[str] = None,
|
59
61
|
env: Optional[Dict] = None,
|
60
62
|
base_dir: str = DEFAULT_DIR,
|
63
|
+
file_read_timeout: int = 3600,
|
61
64
|
**kwargs,
|
62
65
|
):
|
63
66
|
try:
|
@@ -82,6 +85,7 @@ class NBRunner(object):
|
|
82
85
|
self.env_vars["METAFLOW_PROFILE"] = profile
|
83
86
|
|
84
87
|
self.base_dir = base_dir
|
88
|
+
self.file_read_timeout = file_read_timeout
|
85
89
|
|
86
90
|
if not self.cell:
|
87
91
|
raise ValueError("Couldn't find a cell.")
|
@@ -104,6 +108,7 @@ class NBRunner(object):
|
|
104
108
|
profile=profile,
|
105
109
|
env=self.env_vars,
|
106
110
|
cwd=self.base_dir,
|
111
|
+
file_read_timeout=self.file_read_timeout,
|
107
112
|
**kwargs,
|
108
113
|
)
|
109
114
|
|
metaflow/runtime.py
CHANGED
@@ -76,7 +76,7 @@ class NativeRuntime(object):
|
|
76
76
|
clone_run_id=None,
|
77
77
|
clone_only=False,
|
78
78
|
reentrant=False,
|
79
|
-
|
79
|
+
steps_to_rerun=None,
|
80
80
|
max_workers=MAX_WORKERS,
|
81
81
|
max_num_splits=MAX_NUM_SPLITS,
|
82
82
|
max_log_size=MAX_LOG_SIZE,
|
@@ -110,12 +110,21 @@ class NativeRuntime(object):
|
|
110
110
|
|
111
111
|
self._clone_run_id = clone_run_id
|
112
112
|
self._clone_only = clone_only
|
113
|
-
self._clone_steps = {} if clone_steps is None else clone_steps
|
114
113
|
self._cloned_tasks = []
|
115
114
|
self._cloned_task_index = set()
|
116
115
|
self._reentrant = reentrant
|
117
116
|
self._run_url = None
|
118
117
|
|
118
|
+
# If steps_to_rerun is specified, we will not clone them in resume mode.
|
119
|
+
self._steps_to_rerun = steps_to_rerun or {}
|
120
|
+
# sorted_nodes are in topological order already, so we only need to
|
121
|
+
# iterate through the nodes once to get a stable set of rerun steps.
|
122
|
+
for step_name in self._graph.sorted_nodes:
|
123
|
+
if step_name in self._steps_to_rerun:
|
124
|
+
out_funcs = self._graph[step_name].out_funcs or []
|
125
|
+
for next_step in out_funcs:
|
126
|
+
self._steps_to_rerun.add(next_step)
|
127
|
+
|
119
128
|
self._origin_ds_set = None
|
120
129
|
if clone_run_id:
|
121
130
|
# resume logic
|
@@ -166,7 +175,7 @@ class NativeRuntime(object):
|
|
166
175
|
else:
|
167
176
|
may_clone = all(self._is_cloned[path] for path in input_paths)
|
168
177
|
|
169
|
-
if step in self.
|
178
|
+
if step in self._steps_to_rerun:
|
170
179
|
may_clone = False
|
171
180
|
|
172
181
|
if step == "_parameters":
|
@@ -301,16 +310,14 @@ class NativeRuntime(object):
|
|
301
310
|
)
|
302
311
|
except Exception as e:
|
303
312
|
self._logger(
|
304
|
-
"Cloning
|
305
|
-
self._clone_run_id, step_name, task_id, str(e)
|
313
|
+
"Cloning {}/{}/{}/{} failed with error: {}".format(
|
314
|
+
self._flow.name, self._clone_run_id, step_name, task_id, str(e)
|
306
315
|
)
|
307
316
|
)
|
308
317
|
|
309
318
|
def clone_original_run(self, generate_task_obj=False, verbose=True):
|
310
319
|
self._logger(
|
311
|
-
"
|
312
|
-
self._flow.name, self._clone_run_id
|
313
|
-
),
|
320
|
+
"Cloning {}/{}".format(self._flow.name, self._clone_run_id),
|
314
321
|
system_msg=True,
|
315
322
|
)
|
316
323
|
|
@@ -336,7 +343,11 @@ class NativeRuntime(object):
|
|
336
343
|
_, step_name, task_id = task_ds.pathspec.split("/")
|
337
344
|
pathspec_index = task_ds.pathspec_index
|
338
345
|
|
339
|
-
if
|
346
|
+
if (
|
347
|
+
task_ds["_task_ok"]
|
348
|
+
and step_name != "_parameters"
|
349
|
+
and (step_name not in self._steps_to_rerun)
|
350
|
+
):
|
340
351
|
# "_unbounded_foreach" is a special flag to indicate that the transition is an unbounded foreach.
|
341
352
|
# Both parent and splitted children tasks will have this flag set. The splitted control/mapper tasks
|
342
353
|
# have no "foreach_param" because UBF is always followed by a join step.
|
@@ -390,7 +401,9 @@ class NativeRuntime(object):
|
|
390
401
|
) in inputs
|
391
402
|
]
|
392
403
|
_, _ = futures.wait(all_tasks)
|
393
|
-
self._logger(
|
404
|
+
self._logger(
|
405
|
+
"{}/{} cloned!".format(self._flow.name, self._clone_run_id), system_msg=True
|
406
|
+
)
|
394
407
|
self._params_task.mark_resume_done()
|
395
408
|
|
396
409
|
def execute(self):
|
@@ -1149,13 +1162,13 @@ class Task(object):
|
|
1149
1162
|
self._should_skip_cloning = task_completed
|
1150
1163
|
if self._should_skip_cloning:
|
1151
1164
|
self.log(
|
1152
|
-
"
|
1165
|
+
"Skipping cloning of previously run task %s"
|
1166
|
+
% self.clone_origin,
|
1153
1167
|
system_msg=True,
|
1154
1168
|
)
|
1155
1169
|
else:
|
1156
1170
|
self.log(
|
1157
|
-
"Cloning
|
1158
|
-
% self.clone_origin,
|
1171
|
+
"Cloning previously run task %s" % self.clone_origin,
|
1159
1172
|
system_msg=True,
|
1160
1173
|
)
|
1161
1174
|
else:
|
metaflow/util.py
CHANGED
@@ -426,6 +426,25 @@ def tar_safe_extract(tar, path=".", members=None, *, numeric_owner=False):
|
|
426
426
|
tar.extractall(path, members, numeric_owner=numeric_owner)
|
427
427
|
|
428
428
|
|
429
|
+
def to_pod(value):
|
430
|
+
"""
|
431
|
+
Convert a python object to plain-old-data (POD) format.
|
432
|
+
|
433
|
+
Parameters
|
434
|
+
----------
|
435
|
+
value : Any
|
436
|
+
Value to convert to POD format. The value can be a string, number, list,
|
437
|
+
dictionary, or a nested structure of these types.
|
438
|
+
"""
|
439
|
+
if isinstance(value, (str, int, float)):
|
440
|
+
return value
|
441
|
+
if isinstance(value, dict):
|
442
|
+
return {to_pod(k): to_pod(v) for k, v in value.items()}
|
443
|
+
if isinstance(value, (list, set, tuple)):
|
444
|
+
return [to_pod(v) for v in value]
|
445
|
+
return str(value)
|
446
|
+
|
447
|
+
|
429
448
|
if sys.version_info[:2] > (3, 5):
|
430
449
|
from metaflow._vendor.packaging.version import parse as version_parse
|
431
450
|
else:
|
metaflow/version.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
metaflow_version = "2.12.
|
1
|
+
metaflow_version = "2.12.14"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: metaflow
|
3
|
-
Version: 2.12.
|
3
|
+
Version: 2.12.14
|
4
4
|
Summary: Metaflow: More Data Science, Less Engineering
|
5
5
|
Author: Metaflow Developers
|
6
6
|
Author-email: help@metaflow.org
|
@@ -26,7 +26,7 @@ License-File: LICENSE
|
|
26
26
|
Requires-Dist: requests
|
27
27
|
Requires-Dist: boto3
|
28
28
|
Provides-Extra: stubs
|
29
|
-
Requires-Dist: metaflow-stubs==2.12.
|
29
|
+
Requires-Dist: metaflow-stubs==2.12.14; extra == "stubs"
|
30
30
|
|
31
31
|

|
32
32
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
metaflow/R.py,sha256=CqVfIatvmjciuICNnoyyNGrwE7Va9iXfLdFbQa52hwA,3958
|
2
2
|
metaflow/__init__.py,sha256=1TJeSmvQVELJpVGh9s9GoB6aBIK0_zEl9mpW4eSz64Q,5971
|
3
3
|
metaflow/cards.py,sha256=tP1_RrtmqdFh741pqE4t98S7SA0MtGRlGvRICRZF1Mg,426
|
4
|
-
metaflow/cli.py,sha256=
|
4
|
+
metaflow/cli.py,sha256=fa6dx6F2PKtq0oeMxRM2efA9LyE108jGZQvYCtiUS94,34274
|
5
5
|
metaflow/cli_args.py,sha256=lcgBGNTvfaiPxiUnejAe60Upt9swG6lRy1_3OqbU6MY,2616
|
6
6
|
metaflow/clone_util.py,sha256=XfUX0vssu_hPlyZfhFl1AOnKkLqvt33Qp8xNrmdocGg,2057
|
7
7
|
metaflow/cmd_with_io.py,sha256=kl53HkAIyv0ecpItv08wZYczv7u3msD1VCcciqigqf0,588
|
@@ -10,15 +10,15 @@ metaflow/decorators.py,sha256=hbJwRlZuLbl6t7fOsTiH7Sux4Lx6zgEEpldIXoM5TQc,21540
|
|
10
10
|
metaflow/event_logger.py,sha256=joTVRqZPL87nvah4ZOwtqWX8NeraM_CXKXXGVpKGD8o,780
|
11
11
|
metaflow/events.py,sha256=ahjzkSbSnRCK9RZ-9vTfUviz_6gMvSO9DGkJ86X80-k,5300
|
12
12
|
metaflow/exception.py,sha256=KC1LHJQzzYkWib0DeQ4l_A2r8VaudywsSqIQuq1RDZU,4954
|
13
|
-
metaflow/flowspec.py,sha256=
|
14
|
-
metaflow/graph.py,sha256=
|
13
|
+
metaflow/flowspec.py,sha256=0-NnCKiX4oJ21Spb5PJkl2IvHoJxBcpi8CQrZEGB2Ag,27178
|
14
|
+
metaflow/graph.py,sha256=HFJ7V_bPSht_NHIm8BejrSqOX2fyBQpVOczRCliRw08,11975
|
15
15
|
metaflow/includefile.py,sha256=yHczcZ_U0SrasxSNhZb3DIBzx8UZnrJCl3FzvpEQLOA,19753
|
16
16
|
metaflow/integrations.py,sha256=LlsaoePRg03DjENnmLxZDYto3NwWc9z_PtU6nJxLldg,1480
|
17
17
|
metaflow/lint.py,sha256=5rj1MlpluxyPTSINjtMoJ7viotyNzfjtBJSAihlAwMU,10870
|
18
|
-
metaflow/metaflow_config.py,sha256=
|
19
|
-
metaflow/metaflow_config_funcs.py,sha256=
|
18
|
+
metaflow/metaflow_config.py,sha256=4PUd2-JWJs35SaobDgMg4RdpZaKjhEqPUFh2f0pjqnU,22853
|
19
|
+
metaflow/metaflow_config_funcs.py,sha256=5GlvoafV6SxykwfL8D12WXSfwjBN_NsyuKE_Q3gjGVE,6738
|
20
20
|
metaflow/metaflow_current.py,sha256=5Kri7fzj-rtIJVr5xh5kPKwZ0T73_4egZybzlDR-fgc,7136
|
21
|
-
metaflow/metaflow_environment.py,sha256=
|
21
|
+
metaflow/metaflow_environment.py,sha256=xTVowXgia8puUcA0rcNn7iJpFao7e_TRp30RAnB_TW4,7910
|
22
22
|
metaflow/metaflow_profile.py,sha256=jKPEW-hmAQO-htSxb9hXaeloLacAh41A35rMZH6G8pA,418
|
23
23
|
metaflow/metaflow_version.py,sha256=mPQ6g_3XjNdi0NrxDzwlW8ZH0nMyYpwqmJ04P7TIdP0,4774
|
24
24
|
metaflow/monitor.py,sha256=T0NMaBPvXynlJAO_avKtk8OIIRMyEuMAyF8bIp79aZU,5323
|
@@ -28,14 +28,14 @@ metaflow/parameters.py,sha256=l8qnhBG9C4wf_FkXWjq5sapUA6npLdR7pyB0PPQ-KF0,15712
|
|
28
28
|
metaflow/procpoll.py,sha256=U2tE4iK_Mwj2WDyVTx_Uglh6xZ-jixQOo4wrM9OOhxg,2859
|
29
29
|
metaflow/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
30
30
|
metaflow/pylint_wrapper.py,sha256=zzBY9YaSUZOGH-ypDKAv2B_7XcoyMZj-zCoCrmYqNRc,2865
|
31
|
-
metaflow/runtime.py,sha256=
|
31
|
+
metaflow/runtime.py,sha256=fbBObJJciagHWPzR3T7x9e_jez_RBnLZIHsXMvYnW_M,68875
|
32
32
|
metaflow/tagging_util.py,sha256=ctyf0Q1gBi0RyZX6J0e9DQGNkNHblV_CITfy66axXB4,2346
|
33
33
|
metaflow/task.py,sha256=uJHl8K4n3jNllWHSsG1vAZtDza0U2QbQcdg9GS_YPBE,28660
|
34
34
|
metaflow/tuple_util.py,sha256=_G5YIEhuugwJ_f6rrZoelMFak3DqAR2tt_5CapS1XTY,830
|
35
35
|
metaflow/unbounded_foreach.py,sha256=p184WMbrMJ3xKYHwewj27ZhRUsSj_kw1jlye5gA9xJk,387
|
36
|
-
metaflow/util.py,sha256=
|
36
|
+
metaflow/util.py,sha256=olAvJK3y1it_k99MhLulTaAJo7OFVt5rnrD-ulIFLCU,13616
|
37
37
|
metaflow/vendor.py,sha256=FchtA9tH22JM-eEtJ2c9FpUdMn8sSb1VHuQS56EcdZk,5139
|
38
|
-
metaflow/version.py,sha256=
|
38
|
+
metaflow/version.py,sha256=JzanIZpE_JtUO3SVPb7SA7dd6HhMMiUj4SEW50Ilz7I,29
|
39
39
|
metaflow/_vendor/__init__.py,sha256=y_CiwUD3l4eAKvTVDZeqgVujMy31cAM1qjAB-HfI-9s,353
|
40
40
|
metaflow/_vendor/typing_extensions.py,sha256=0nUs5p1A_UrZigrAVBoOEM6TxU37zzPDUtiij1ZwpNc,110417
|
41
41
|
metaflow/_vendor/zipp.py,sha256=ajztOH-9I7KA_4wqDYygtHa6xUBVZgFpmZ8FE74HHHI,8425
|
@@ -110,7 +110,7 @@ metaflow/_vendor/v3_6/importlib_metadata/_meta.py,sha256=_F48Hu_jFxkfKWz5wcYS8vO
|
|
110
110
|
metaflow/_vendor/v3_6/importlib_metadata/_text.py,sha256=HCsFksZpJLeTP3NEk_ngrAeXVRRtTrtyh9eOABoRP4A,2166
|
111
111
|
metaflow/_vendor/v3_6/importlib_metadata/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
112
112
|
metaflow/client/__init__.py,sha256=1GtQB4Y_CBkzaxg32L1syNQSlfj762wmLrfrDxGi1b8,226
|
113
|
-
metaflow/client/core.py,sha256=
|
113
|
+
metaflow/client/core.py,sha256=L4COrMyQgSAWkXIPoXFFQ3OqGnoHfePsBZdJQvShU1E,74162
|
114
114
|
metaflow/client/filecache.py,sha256=Wy0yhhCqC1JZgebqi7z52GCwXYnkAqMZHTtxThvwBgM,15229
|
115
115
|
metaflow/cmd/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
116
116
|
metaflow/cmd/configure_cmd.py,sha256=o-DKnUf2FBo_HiMVyoyzQaGBSMtpbEPEdFTQZ0hkU-k,33396
|
@@ -174,22 +174,22 @@ metaflow/plugins/airflow/sensors/s3_sensor.py,sha256=iDReG-7FKnumrtQg-HY6cCUAAqN
|
|
174
174
|
metaflow/plugins/argo/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
175
175
|
metaflow/plugins/argo/argo_client.py,sha256=MKKhMCbWOPzf6z5zQQiyDRHHkAXcO7ipboDZDqAAvOk,15849
|
176
176
|
metaflow/plugins/argo/argo_events.py,sha256=_C1KWztVqgi3zuH57pInaE9OzABc2NnncC-zdwOMZ-w,5909
|
177
|
-
metaflow/plugins/argo/argo_workflows.py,sha256=
|
178
|
-
metaflow/plugins/argo/argo_workflows_cli.py,sha256=
|
177
|
+
metaflow/plugins/argo/argo_workflows.py,sha256=6xUkz1LKdCLbl-O-D83Y2G5mCKYcIciKts3x1PNAzCk,170173
|
178
|
+
metaflow/plugins/argo/argo_workflows_cli.py,sha256=ydCf7P0lR8KaZIwYTtV_hRw1J49okE0wtEW8Kcj2c4c,35704
|
179
179
|
metaflow/plugins/argo/argo_workflows_decorator.py,sha256=yprszMdbE3rBTcEA9VR0IEnPjTprUauZBc4SBb-Q7sA,7878
|
180
|
-
metaflow/plugins/argo/argo_workflows_deployer.py,sha256=
|
180
|
+
metaflow/plugins/argo/argo_workflows_deployer.py,sha256=wSSZtThn_VPvE_Wu6NB1L0Q86LmBJh9g009v_lpvBPM,8125
|
181
181
|
metaflow/plugins/argo/capture_error.py,sha256=Ys9dscGrTpW-ZCirLBU0gD9qBM0BjxyxGlUMKcwewQc,1852
|
182
182
|
metaflow/plugins/argo/daemon.py,sha256=dJOS_UUISXBYffi3oGVKPwq4Pa4P_nGBGL15piPaPto,1776
|
183
183
|
metaflow/plugins/argo/generate_input_paths.py,sha256=loYsI6RFX9LlFsHb7Fe-mzlTTtRdySoOu7sYDy-uXK0,881
|
184
184
|
metaflow/plugins/argo/jobset_input_paths.py,sha256=_JhZWngA6p9Q_O2fx3pdzKI0WE-HPRHz_zFvY2pHPTQ,525
|
185
185
|
metaflow/plugins/aws/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
186
186
|
metaflow/plugins/aws/aws_client.py,sha256=mO8UD6pxFaOnxDb3hTP3HB7Gqb_ZxoR-76LT683WHvI,4036
|
187
|
-
metaflow/plugins/aws/aws_utils.py,sha256=
|
187
|
+
metaflow/plugins/aws/aws_utils.py,sha256=dk92IRZ2QTF3PicBOtZMMOmS_FIncFqZPeL9EbCtXak,7310
|
188
188
|
metaflow/plugins/aws/batch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
189
189
|
metaflow/plugins/aws/batch/batch.py,sha256=e9ssahWM18GnipPK2sqYB-ztx9w7Eoo7YtWyEtufYxs,17787
|
190
190
|
metaflow/plugins/aws/batch/batch_cli.py,sha256=6PTbyajRgdy0XmjyJLBTdKdiOB84dcovQQ8sFXlJqko,11749
|
191
191
|
metaflow/plugins/aws/batch/batch_client.py,sha256=s9ZHhxQPPoBQijLUgn6_16QOaD4-22U_44uJbp-yLkI,28565
|
192
|
-
metaflow/plugins/aws/batch/batch_decorator.py,sha256=
|
192
|
+
metaflow/plugins/aws/batch/batch_decorator.py,sha256=kwgxEPCEoI6eZIpU5PuL442Ohg4_BfvwowoYgAnCzKE,17520
|
193
193
|
metaflow/plugins/aws/secrets_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
194
194
|
metaflow/plugins/aws/secrets_manager/aws_secrets_manager_secrets_provider.py,sha256=JtFUVu00Cg0FzAizgrPLXmrMqsT7YeQMkQlgeivUxcE,7986
|
195
195
|
metaflow/plugins/aws/step_functions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -202,7 +202,7 @@ metaflow/plugins/aws/step_functions/step_functions.py,sha256=8tBs4pvdIVgNoZRm0Dz
|
|
202
202
|
metaflow/plugins/aws/step_functions/step_functions_cli.py,sha256=4CYw5xQwPaEV_iudF4_m4SSRS5LEq5UgrzVlCB6xT_U,25763
|
203
203
|
metaflow/plugins/aws/step_functions/step_functions_client.py,sha256=DKpNwAIWElvWjFANs5Ku3rgzjxFoqAD6k-EF8Xhkg3Q,4754
|
204
204
|
metaflow/plugins/aws/step_functions/step_functions_decorator.py,sha256=9hw_MX36RyFp6IowuAYaJzJg9UC5KCe1FNt1PcG7_J0,3791
|
205
|
-
metaflow/plugins/aws/step_functions/step_functions_deployer.py,sha256=
|
205
|
+
metaflow/plugins/aws/step_functions/step_functions_deployer.py,sha256=WrfQjvXnnInXwSePwoLUMb2EjqFG4RK1krO_8bW0qGI,7218
|
206
206
|
metaflow/plugins/azure/__init__.py,sha256=GuuhTVC-zSdyAf79a1wiERMq0Zts7fwVT7t9fAf234A,100
|
207
207
|
metaflow/plugins/azure/azure_credential.py,sha256=JmdGEbVzgxy8ucqnQDdTTI_atyMX9WSZUw3qYOo7RhE,2174
|
208
208
|
metaflow/plugins/azure/azure_exceptions.py,sha256=NnbwpUC23bc61HZjJmeXztY0tBNn_Y_VpIpDDuYWIZ0,433
|
@@ -279,10 +279,10 @@ metaflow/plugins/gcp/gs_tail.py,sha256=Jl_wvnzU7dub07A-DOAuP5FeccNIrPM-CeL1xKFs1
|
|
279
279
|
metaflow/plugins/gcp/gs_utils.py,sha256=ZmIGFse1qYyvAVrwga23PQUzF6dXEDLLsZ2F-YRmvow,2030
|
280
280
|
metaflow/plugins/gcp/includefile_support.py,sha256=vIDeR-MiJuUh-2S2pV7Z7FBkhIWwtHXaRrj76MWGRiY,3869
|
281
281
|
metaflow/plugins/kubernetes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
282
|
-
metaflow/plugins/kubernetes/kubernetes.py,sha256=
|
282
|
+
metaflow/plugins/kubernetes/kubernetes.py,sha256=cr3TheUasxIBEwFZ3GEVbctaf8gW57BM5BDk80ikjPI,31063
|
283
283
|
metaflow/plugins/kubernetes/kubernetes_cli.py,sha256=qBDdr1Lvtt-RO9pB-9_HTOPdzAmDvvJ0aiQ1OoCcrMU,10892
|
284
284
|
metaflow/plugins/kubernetes/kubernetes_client.py,sha256=GKg-gT3qhXMRQV-sG1YyoOf3Z32NXr_wwEN2ytMVSEg,2471
|
285
|
-
metaflow/plugins/kubernetes/kubernetes_decorator.py,sha256=
|
285
|
+
metaflow/plugins/kubernetes/kubernetes_decorator.py,sha256=CXStYHomuJJK_Yocpdo6OJadEQv5hDfSpO7GPL61ltw,25322
|
286
286
|
metaflow/plugins/kubernetes/kubernetes_job.py,sha256=Cfkee8LbXC17jSXWoeNdomQRvF_8YSeXNg1gvxm6E_M,31806
|
287
287
|
metaflow/plugins/kubernetes/kubernetes_jobsets.py,sha256=OBmLtX-ZUDQdCCfftUmRMernfmTNMwdTxPoCAp_NmwE,40957
|
288
288
|
metaflow/plugins/metadata/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
@@ -290,11 +290,11 @@ metaflow/plugins/metadata/local.py,sha256=YhLJC5zjVJrvQFIyQ92ZBByiUmhCC762RUX7IT
|
|
290
290
|
metaflow/plugins/metadata/service.py,sha256=ihq5F7KQZlxvYwzH_-jyP2aWN_I96i2vp92j_d697s8,20204
|
291
291
|
metaflow/plugins/pypi/__init__.py,sha256=0YFZpXvX7HCkyBFglatual7XGifdA1RwC3U4kcizyak,1037
|
292
292
|
metaflow/plugins/pypi/bootstrap.py,sha256=Tvc4_QKIx-A8j5Aq8ccWZrrxNM8csN40rK8HmxDx-Z8,5106
|
293
|
-
metaflow/plugins/pypi/conda_decorator.py,sha256=
|
293
|
+
metaflow/plugins/pypi/conda_decorator.py,sha256=TGs80qWk0zMtO0JfqB2cVMSt52YIxCd7hsS4KxSLMZ4,14707
|
294
294
|
metaflow/plugins/pypi/conda_environment.py,sha256=tR6xvDx9zqlW0oF7j5dRebb0o_CHEdHVlaT4LNMJOAA,19307
|
295
295
|
metaflow/plugins/pypi/micromamba.py,sha256=67FiIZZz0Kig9EcN7bZLObsE6Z1MFyo4Dp93fd3Grcc,12178
|
296
296
|
metaflow/plugins/pypi/pip.py,sha256=7B06mPOs5MvY33xbzPVYZlBr1iKMYaN-n8uulL9zSVg,13649
|
297
|
-
metaflow/plugins/pypi/pypi_decorator.py,sha256=
|
297
|
+
metaflow/plugins/pypi/pypi_decorator.py,sha256=h5cAnxkWjmj4Ad4q0AkABKwhHQHYfeexy12yMaaLgXQ,6443
|
298
298
|
metaflow/plugins/pypi/pypi_environment.py,sha256=FYMg8kF3lXqcLfRYWD83a9zpVjcoo_TARqMGZ763rRk,230
|
299
299
|
metaflow/plugins/pypi/utils.py,sha256=ds1Mnv_DaxGnLAYp7ozg_K6oyguGyNhvHfE-75Ia1YA,2836
|
300
300
|
metaflow/plugins/secrets/__init__.py,sha256=mhJaN2eMS_ZZVewAMR2E-JdP5i0t3v9e6Dcwd-WpruE,310
|
@@ -302,10 +302,10 @@ metaflow/plugins/secrets/inline_secrets_provider.py,sha256=EChmoBGA1i7qM3jtYwPpL
|
|
302
302
|
metaflow/plugins/secrets/secrets_decorator.py,sha256=s-sFzPWOjahhpr5fMj-ZEaHkDYAPTO0isYXGvaUwlG8,11273
|
303
303
|
metaflow/runner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
304
304
|
metaflow/runner/click_api.py,sha256=vrrIb5DtVYhCW_hB7wPgj6Q0o-h1bBSWiYnKTOyC454,13452
|
305
|
-
metaflow/runner/deployer.py,sha256=
|
306
|
-
metaflow/runner/metaflow_runner.py,sha256=
|
307
|
-
metaflow/runner/nbdeploy.py,sha256
|
308
|
-
metaflow/runner/nbrun.py,sha256=
|
305
|
+
metaflow/runner/deployer.py,sha256=nArjnErc0rOaZW612VRKDOT5594jwzeu86w5zW1LX6U,13558
|
306
|
+
metaflow/runner/metaflow_runner.py,sha256=AO9nwr5qUbZWmsbFdjkUJrvFlaylz7WvxslvHsIqDYc,15371
|
307
|
+
metaflow/runner/nbdeploy.py,sha256=fP1s_5MeiDyT_igP82pB5EUqX9rOy2s06Hyc-OUbOvQ,4115
|
308
|
+
metaflow/runner/nbrun.py,sha256=lmvhzMCz7iC9LSPGRijifW1wMXxa4RW_jVmpdjQi22E,7261
|
309
309
|
metaflow/runner/subprocess_manager.py,sha256=0knxWZYJx8srMv6wTPYKOC6tn4-airnyI7Vbqfb3iXY,19567
|
310
310
|
metaflow/runner/utils.py,sha256=FibdEj8CDnx1a-Je5KUQTwHuNbtkFm1unXGarj0D8ok,1394
|
311
311
|
metaflow/sidecar/__init__.py,sha256=1mmNpmQ5puZCpRmmYlCOeieZ4108Su9XQ4_EqF1FGOU,131
|
@@ -344,9 +344,9 @@ metaflow/tutorials/07-worldview/README.md,sha256=5vQTrFqulJ7rWN6r20dhot9lI2sVj9W
|
|
344
344
|
metaflow/tutorials/07-worldview/worldview.ipynb,sha256=ztPZPI9BXxvW1QdS2Tfe7LBuVzvFvv0AToDnsDJhLdE,2237
|
345
345
|
metaflow/tutorials/08-autopilot/README.md,sha256=GnePFp_q76jPs991lMUqfIIh5zSorIeWznyiUxzeUVE,1039
|
346
346
|
metaflow/tutorials/08-autopilot/autopilot.ipynb,sha256=DQoJlILV7Mq9vfPBGW-QV_kNhWPjS5n6SJLqePjFYLY,3191
|
347
|
-
metaflow-2.12.
|
348
|
-
metaflow-2.12.
|
349
|
-
metaflow-2.12.
|
350
|
-
metaflow-2.12.
|
351
|
-
metaflow-2.12.
|
352
|
-
metaflow-2.12.
|
347
|
+
metaflow-2.12.14.dist-info/LICENSE,sha256=nl_Lt5v9VvJ-5lWJDT4ddKAG-VZ-2IaLmbzpgYDz2hU,11343
|
348
|
+
metaflow-2.12.14.dist-info/METADATA,sha256=LHUByGEAJnWelBfACmll3cpcjnPWjbRXr4gLPYv6FnA,5906
|
349
|
+
metaflow-2.12.14.dist-info/WHEEL,sha256=GUeE9LxUgRABPG7YM0jCNs9cBsAIx0YAkzCB88PMLgc,109
|
350
|
+
metaflow-2.12.14.dist-info/entry_points.txt,sha256=IKwTN1T3I5eJL3uo_vnkyxVffcgnRdFbKwlghZfn27k,57
|
351
|
+
metaflow-2.12.14.dist-info/top_level.txt,sha256=v1pDHoWaSaKeuc5fKTRSfsXCKSdW1zvNVmvA-i0if3o,9
|
352
|
+
metaflow-2.12.14.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|