metaflow 2.15.14__py2.py3-none-any.whl → 2.15.16__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/__init__.py +2 -2
- metaflow/_vendor/click/core.py +4 -3
- metaflow/cmd/develop/stub_generator.py +30 -16
- metaflow/cmd/develop/stubs.py +9 -27
- metaflow/datastore/task_datastore.py +3 -3
- metaflow/decorators.py +3 -3
- metaflow/extension_support/__init__.py +25 -42
- metaflow/parameters.py +2 -2
- metaflow/plugins/argo/argo_workflows_cli.py +4 -4
- metaflow/plugins/argo/argo_workflows_deployer_objects.py +6 -49
- metaflow/plugins/aws/aws_client.py +6 -0
- metaflow/plugins/cards/card_modules/chevron/renderer.py +1 -1
- metaflow/plugins/cards/card_modules/test_cards.py +6 -6
- metaflow/plugins/cards/component_serializer.py +1 -8
- metaflow/plugins/datatools/s3/s3op.py +1 -1
- metaflow/plugins/metadata_providers/service.py +12 -8
- metaflow/plugins/package_cli.py +12 -2
- metaflow/plugins/pypi/bootstrap.py +2 -2
- metaflow/plugins/uv/bootstrap.py +18 -1
- metaflow/plugins/uv/uv_environment.py +1 -1
- metaflow/runner/click_api.py +16 -9
- metaflow/runner/deployer.py +49 -0
- metaflow/runner/deployer_impl.py +17 -5
- metaflow/runner/metaflow_runner.py +40 -13
- metaflow/runner/subprocess_manager.py +1 -1
- metaflow/runner/utils.py +8 -0
- metaflow/user_configs/config_options.py +6 -6
- metaflow/user_configs/config_parameters.py +211 -45
- metaflow/util.py +2 -5
- metaflow/vendor.py +0 -1
- metaflow/version.py +1 -1
- {metaflow-2.15.14.dist-info → metaflow-2.15.16.dist-info}/METADATA +2 -2
- {metaflow-2.15.14.dist-info → metaflow-2.15.16.dist-info}/RECORD +40 -44
- {metaflow-2.15.14.dist-info → metaflow-2.15.16.dist-info}/WHEEL +1 -1
- metaflow/_vendor/v3_5/__init__.py +0 -1
- metaflow/_vendor/v3_5/importlib_metadata/__init__.py +0 -644
- metaflow/_vendor/v3_5/importlib_metadata/_compat.py +0 -152
- metaflow/_vendor/v3_5/zipp.py +0 -329
- {metaflow-2.15.14.data → metaflow-2.15.16.data}/data/share/metaflow/devtools/Makefile +0 -0
- {metaflow-2.15.14.data → metaflow-2.15.16.data}/data/share/metaflow/devtools/Tiltfile +0 -0
- {metaflow-2.15.14.data → metaflow-2.15.16.data}/data/share/metaflow/devtools/pick_services.sh +0 -0
- {metaflow-2.15.14.dist-info → metaflow-2.15.16.dist-info}/entry_points.txt +0 -0
- {metaflow-2.15.14.dist-info → metaflow-2.15.16.dist-info}/licenses/LICENSE +0 -0
- {metaflow-2.15.14.dist-info → metaflow-2.15.16.dist-info}/top_level.txt +0 -0
metaflow/plugins/uv/bootstrap.py
CHANGED
@@ -4,6 +4,7 @@ import sys
|
|
4
4
|
import time
|
5
5
|
|
6
6
|
from metaflow.util import which
|
7
|
+
from metaflow.info_file import read_info_file
|
7
8
|
from metaflow.metaflow_config import get_pinned_conda_libs
|
8
9
|
from urllib.request import Request, urlopen
|
9
10
|
from urllib.error import URLError
|
@@ -78,11 +79,27 @@ if __name__ == "__main__":
|
|
78
79
|
# return only dependency names instead of pinned versions
|
79
80
|
return pinned.keys()
|
80
81
|
|
82
|
+
def skip_metaflow_dependencies():
|
83
|
+
skip_pkgs = ["metaflow"]
|
84
|
+
info = read_info_file()
|
85
|
+
if info is not None:
|
86
|
+
try:
|
87
|
+
skip_pkgs.extend([ext_name for ext_name in info["ext_info"][0].keys()])
|
88
|
+
except Exception:
|
89
|
+
print(
|
90
|
+
"Failed to read INFO. Metaflow-related packages might get installed during runtime."
|
91
|
+
)
|
92
|
+
|
93
|
+
return skip_pkgs
|
94
|
+
|
81
95
|
def sync_uv_project(datastore_type):
|
82
96
|
print("Syncing uv project...")
|
83
97
|
dependencies = " ".join(get_dependencies(datastore_type))
|
98
|
+
skip_pkgs = " ".join(
|
99
|
+
[f"--no-install-package {dep}" for dep in skip_metaflow_dependencies()]
|
100
|
+
)
|
84
101
|
cmd = f"""set -e;
|
85
|
-
uv sync --frozen
|
102
|
+
uv sync --frozen {skip_pkgs};
|
86
103
|
uv pip install {dependencies} --strict
|
87
104
|
"""
|
88
105
|
run_cmd(cmd)
|
@@ -22,7 +22,7 @@ class UVEnvironment(MetaflowEnvironment):
|
|
22
22
|
self.logger("Bootstrapping uv...")
|
23
23
|
|
24
24
|
def executable(self, step_name, default=None):
|
25
|
-
return "uv run python"
|
25
|
+
return "uv run --no-sync python"
|
26
26
|
|
27
27
|
def add_to_package(self):
|
28
28
|
# NOTE: We treat uv.lock and pyproject.toml as regular project assets and ship these along user code as part of the code package
|
metaflow/runner/click_api.py
CHANGED
@@ -467,9 +467,14 @@ class MetaflowAPI(object):
|
|
467
467
|
config_file = defaults.get("config")
|
468
468
|
|
469
469
|
if config_file:
|
470
|
-
config_file =
|
471
|
-
|
472
|
-
|
470
|
+
config_file = dict(
|
471
|
+
map(
|
472
|
+
lambda x: (
|
473
|
+
x[0],
|
474
|
+
ConvertPath.convert_value(x[1], is_default),
|
475
|
+
),
|
476
|
+
config_file,
|
477
|
+
)
|
473
478
|
)
|
474
479
|
|
475
480
|
is_default = False
|
@@ -479,12 +484,14 @@ class MetaflowAPI(object):
|
|
479
484
|
config_value = defaults.get("config_value")
|
480
485
|
|
481
486
|
if config_value:
|
482
|
-
config_value =
|
483
|
-
|
484
|
-
x
|
485
|
-
|
486
|
-
|
487
|
-
|
487
|
+
config_value = dict(
|
488
|
+
map(
|
489
|
+
lambda x: (
|
490
|
+
x[0],
|
491
|
+
ConvertDictOrStr.convert_value(x[1], is_default),
|
492
|
+
),
|
493
|
+
config_value,
|
494
|
+
)
|
488
495
|
)
|
489
496
|
|
490
497
|
if (config_file is None) ^ (config_value is None):
|
metaflow/runner/deployer.py
CHANGED
@@ -7,6 +7,55 @@ from typing import ClassVar, Dict, Optional, TYPE_CHECKING
|
|
7
7
|
from metaflow.exception import MetaflowNotFound
|
8
8
|
from metaflow.metaflow_config import DEFAULT_FROM_DEPLOYMENT_IMPL
|
9
9
|
|
10
|
+
|
11
|
+
def generate_fake_flow_file_contents(
|
12
|
+
flow_name: str, param_info: dict, project_name: Optional[str] = None
|
13
|
+
):
|
14
|
+
params_code = ""
|
15
|
+
for _, param_details in param_info.items():
|
16
|
+
param_python_var_name = param_details["python_var_name"]
|
17
|
+
param_name = param_details["name"]
|
18
|
+
param_type = param_details["type"]
|
19
|
+
param_help = param_details["description"]
|
20
|
+
param_required = param_details["is_required"]
|
21
|
+
|
22
|
+
if param_type == "JSON":
|
23
|
+
params_code += (
|
24
|
+
f" {param_python_var_name} = Parameter('{param_name}', "
|
25
|
+
f"type=JSONType, help='''{param_help}''', required={param_required})\n"
|
26
|
+
)
|
27
|
+
elif param_type == "FilePath":
|
28
|
+
is_text = param_details.get("is_text", True)
|
29
|
+
encoding = param_details.get("encoding", "utf-8")
|
30
|
+
params_code += (
|
31
|
+
f" {param_python_var_name} = IncludeFile('{param_name}', "
|
32
|
+
f"is_text={is_text}, encoding='{encoding}', help='''{param_help}''', "
|
33
|
+
f"required={param_required})\n"
|
34
|
+
)
|
35
|
+
else:
|
36
|
+
params_code += (
|
37
|
+
f" {param_python_var_name} = Parameter('{param_name}', "
|
38
|
+
f"type={param_type}, help='''{param_help}''', required={param_required})\n"
|
39
|
+
)
|
40
|
+
|
41
|
+
project_decorator = f"@project(name='{project_name}')\n" if project_name else ""
|
42
|
+
|
43
|
+
contents = f"""\
|
44
|
+
from metaflow import FlowSpec, Parameter, IncludeFile, JSONType, step, project
|
45
|
+
{project_decorator}class {flow_name}(FlowSpec):
|
46
|
+
{params_code}
|
47
|
+
@step
|
48
|
+
def start(self):
|
49
|
+
self.next(self.end)
|
50
|
+
@step
|
51
|
+
def end(self):
|
52
|
+
pass
|
53
|
+
if __name__ == '__main__':
|
54
|
+
{flow_name}()
|
55
|
+
"""
|
56
|
+
return contents
|
57
|
+
|
58
|
+
|
10
59
|
if TYPE_CHECKING:
|
11
60
|
import metaflow
|
12
61
|
import metaflow.runner.deployer_impl
|
metaflow/runner/deployer_impl.py
CHANGED
@@ -5,8 +5,10 @@ import sys
|
|
5
5
|
|
6
6
|
from typing import Any, ClassVar, Dict, Optional, TYPE_CHECKING, Type
|
7
7
|
|
8
|
+
from metaflow.metaflow_config import CLICK_API_PROCESS_CONFIG
|
9
|
+
|
8
10
|
from .subprocess_manager import SubprocessManager
|
9
|
-
from .utils import get_lower_level_group, handle_timeout, temporary_fifo
|
11
|
+
from .utils import get_lower_level_group, handle_timeout, temporary_fifo, with_dir
|
10
12
|
|
11
13
|
if TYPE_CHECKING:
|
12
14
|
import metaflow.runner.deployer
|
@@ -88,7 +90,7 @@ class DeployerImpl(object):
|
|
88
90
|
self.show_output = show_output
|
89
91
|
self.profile = profile
|
90
92
|
self.env = env
|
91
|
-
self.cwd = cwd
|
93
|
+
self.cwd = cwd or os.getcwd()
|
92
94
|
self.file_read_timeout = file_read_timeout
|
93
95
|
|
94
96
|
self.env_vars = os.environ.copy()
|
@@ -140,9 +142,19 @@ class DeployerImpl(object):
|
|
140
142
|
) -> "metaflow.runner.deployer.DeployedFlow":
|
141
143
|
with temporary_fifo() as (attribute_file_path, attribute_file_fd):
|
142
144
|
# every subclass needs to have `self.deployer_kwargs`
|
143
|
-
|
144
|
-
|
145
|
-
|
145
|
+
# TODO: Get rid of CLICK_API_PROCESS_CONFIG in the near future
|
146
|
+
if CLICK_API_PROCESS_CONFIG:
|
147
|
+
# We need to run this in the cwd because configs depend on files
|
148
|
+
# that may be located in paths relative to the directory the user
|
149
|
+
# wants to run in
|
150
|
+
with with_dir(self.cwd):
|
151
|
+
command = get_lower_level_group(
|
152
|
+
self.api, self.top_level_kwargs, self.TYPE, self.deployer_kwargs
|
153
|
+
).create(deployer_attribute_file=attribute_file_path, **kwargs)
|
154
|
+
else:
|
155
|
+
command = get_lower_level_group(
|
156
|
+
self.api, self.top_level_kwargs, self.TYPE, self.deployer_kwargs
|
157
|
+
).create(deployer_attribute_file=attribute_file_path, **kwargs)
|
146
158
|
|
147
159
|
pid = self.spm.run_command(
|
148
160
|
[sys.executable, *command],
|
@@ -7,12 +7,15 @@ from typing import Dict, Iterator, Optional, Tuple
|
|
7
7
|
|
8
8
|
from metaflow import Run
|
9
9
|
|
10
|
+
from metaflow.metaflow_config import CLICK_API_PROCESS_CONFIG
|
11
|
+
|
10
12
|
from metaflow.plugins import get_runner_cli
|
11
13
|
|
12
14
|
from .utils import (
|
13
15
|
temporary_fifo,
|
14
16
|
handle_timeout,
|
15
17
|
async_handle_timeout,
|
18
|
+
with_dir,
|
16
19
|
)
|
17
20
|
from .subprocess_manager import CommandManager, SubprocessManager
|
18
21
|
|
@@ -299,7 +302,7 @@ class Runner(metaclass=RunnerMeta):
|
|
299
302
|
if profile:
|
300
303
|
self.env_vars["METAFLOW_PROFILE"] = profile
|
301
304
|
|
302
|
-
self.cwd = cwd
|
305
|
+
self.cwd = cwd or os.getcwd()
|
303
306
|
self.file_read_timeout = file_read_timeout
|
304
307
|
self.spm = SubprocessManager()
|
305
308
|
self.top_level_kwargs = kwargs
|
@@ -359,9 +362,15 @@ class Runner(metaclass=RunnerMeta):
|
|
359
362
|
ExecutingRun containing the results of the run.
|
360
363
|
"""
|
361
364
|
with temporary_fifo() as (attribute_file_path, attribute_file_fd):
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
+
if CLICK_API_PROCESS_CONFIG:
|
366
|
+
with with_dir(self.cwd):
|
367
|
+
command = self.api(**self.top_level_kwargs).run(
|
368
|
+
runner_attribute_file=attribute_file_path, **kwargs
|
369
|
+
)
|
370
|
+
else:
|
371
|
+
command = self.api(**self.top_level_kwargs).run(
|
372
|
+
runner_attribute_file=attribute_file_path, **kwargs
|
373
|
+
)
|
365
374
|
|
366
375
|
pid = self.spm.run_command(
|
367
376
|
[sys.executable, *command],
|
@@ -390,9 +399,15 @@ class Runner(metaclass=RunnerMeta):
|
|
390
399
|
ExecutingRun containing the results of the resumed run.
|
391
400
|
"""
|
392
401
|
with temporary_fifo() as (attribute_file_path, attribute_file_fd):
|
393
|
-
|
394
|
-
|
395
|
-
|
402
|
+
if CLICK_API_PROCESS_CONFIG:
|
403
|
+
with with_dir(self.cwd):
|
404
|
+
command = self.api(**self.top_level_kwargs).resume(
|
405
|
+
runner_attribute_file=attribute_file_path, **kwargs
|
406
|
+
)
|
407
|
+
else:
|
408
|
+
command = self.api(**self.top_level_kwargs).resume(
|
409
|
+
runner_attribute_file=attribute_file_path, **kwargs
|
410
|
+
)
|
396
411
|
|
397
412
|
pid = self.spm.run_command(
|
398
413
|
[sys.executable, *command],
|
@@ -423,9 +438,15 @@ class Runner(metaclass=RunnerMeta):
|
|
423
438
|
ExecutingRun representing the run that was started.
|
424
439
|
"""
|
425
440
|
with temporary_fifo() as (attribute_file_path, attribute_file_fd):
|
426
|
-
|
427
|
-
|
428
|
-
|
441
|
+
if CLICK_API_PROCESS_CONFIG:
|
442
|
+
with with_dir(self.cwd):
|
443
|
+
command = self.api(**self.top_level_kwargs).run(
|
444
|
+
runner_attribute_file=attribute_file_path, **kwargs
|
445
|
+
)
|
446
|
+
else:
|
447
|
+
command = self.api(**self.top_level_kwargs).run(
|
448
|
+
runner_attribute_file=attribute_file_path, **kwargs
|
449
|
+
)
|
429
450
|
|
430
451
|
pid = await self.spm.async_run_command(
|
431
452
|
[sys.executable, *command],
|
@@ -455,9 +476,15 @@ class Runner(metaclass=RunnerMeta):
|
|
455
476
|
ExecutingRun representing the resumed run that was started.
|
456
477
|
"""
|
457
478
|
with temporary_fifo() as (attribute_file_path, attribute_file_fd):
|
458
|
-
|
459
|
-
|
460
|
-
|
479
|
+
if CLICK_API_PROCESS_CONFIG:
|
480
|
+
with with_dir(self.cwd):
|
481
|
+
command = self.api(**self.top_level_kwargs).resume(
|
482
|
+
runner_attribute_file=attribute_file_path, **kwargs
|
483
|
+
)
|
484
|
+
else:
|
485
|
+
command = self.api(**self.top_level_kwargs).resume(
|
486
|
+
runner_attribute_file=attribute_file_path, **kwargs
|
487
|
+
)
|
461
488
|
|
462
489
|
pid = await self.spm.async_run_command(
|
463
490
|
[sys.executable, *command],
|
@@ -237,7 +237,7 @@ class CommandManager(object):
|
|
237
237
|
self.command = command
|
238
238
|
|
239
239
|
self.env = env if env is not None else os.environ.copy()
|
240
|
-
self.cwd = cwd
|
240
|
+
self.cwd = cwd or os.getcwd()
|
241
241
|
|
242
242
|
self.process = None
|
243
243
|
self.stdout_thread = None
|
metaflow/runner/utils.py
CHANGED
@@ -322,3 +322,11 @@ def get_lower_level_group(
|
|
322
322
|
raise ValueError(f"Sub-command '{sub_command}' not found in API '{api.name}'")
|
323
323
|
|
324
324
|
return sub_command_obj(**sub_command_kwargs)
|
325
|
+
|
326
|
+
|
327
|
+
@contextmanager
|
328
|
+
def with_dir(new_dir):
|
329
|
+
current_dir = os.getcwd()
|
330
|
+
os.chdir(new_dir)
|
331
|
+
yield new_dir
|
332
|
+
os.chdir(current_dir)
|
@@ -221,13 +221,13 @@ class ConfigInput:
|
|
221
221
|
if param_name == "config_value":
|
222
222
|
self._value_values = {
|
223
223
|
k.lower(): v
|
224
|
-
for k, v in param_value
|
224
|
+
for k, v in param_value.items()
|
225
225
|
if v is not None and not v.startswith(_CONVERTED_DEFAULT)
|
226
226
|
}
|
227
227
|
else:
|
228
228
|
self._path_values = {
|
229
229
|
k.lower(): v
|
230
|
-
for k, v in param_value
|
230
|
+
for k, v in param_value.items()
|
231
231
|
if v is not None and not v.startswith(_CONVERTED_DEFAULT)
|
232
232
|
}
|
233
233
|
if do_return:
|
@@ -329,12 +329,12 @@ class ConfigInput:
|
|
329
329
|
to_return[name] = None
|
330
330
|
flow_cls._flow_state[_FlowState.CONFIGS][name] = None
|
331
331
|
continue
|
332
|
-
if val.startswith(_CONVERTED_DEFAULT_NO_FILE):
|
333
|
-
no_default_file.append(name)
|
334
|
-
continue
|
335
332
|
if val.startswith(_CONVERTED_NO_FILE):
|
336
333
|
no_file.append(name)
|
337
334
|
continue
|
335
|
+
if val.startswith(_CONVERTED_DEFAULT_NO_FILE):
|
336
|
+
no_default_file.append(name)
|
337
|
+
continue
|
338
338
|
|
339
339
|
val = val[len(_CONVERT_PREFIX) :] # Remove the _CONVERT_PREFIX
|
340
340
|
if val.startswith(_DEFAULT_PREFIX): # Remove the _DEFAULT_PREFIX if needed
|
@@ -398,7 +398,7 @@ class ConfigInput:
|
|
398
398
|
return self.process_configs(
|
399
399
|
ctx.obj.flow.name,
|
400
400
|
param.name,
|
401
|
-
value,
|
401
|
+
dict(value),
|
402
402
|
ctx.params["quiet"],
|
403
403
|
ctx.params["datastore"],
|
404
404
|
click_obj=ctx.obj,
|