ob-metaflow 2.16.8.2rc2__py2.py3-none-any.whl → 2.17.0.1__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.
Potentially problematic release.
This version of ob-metaflow might be problematic. Click here for more details.
- metaflow/_vendor/click/core.py +3 -4
- metaflow/_vendor/imghdr/__init__.py +7 -1
- metaflow/_vendor/yaml/__init__.py +427 -0
- metaflow/_vendor/yaml/composer.py +139 -0
- metaflow/_vendor/yaml/constructor.py +748 -0
- metaflow/_vendor/yaml/cyaml.py +101 -0
- metaflow/_vendor/yaml/dumper.py +62 -0
- metaflow/_vendor/yaml/emitter.py +1137 -0
- metaflow/_vendor/yaml/error.py +75 -0
- metaflow/_vendor/yaml/events.py +86 -0
- metaflow/_vendor/yaml/loader.py +63 -0
- metaflow/_vendor/yaml/nodes.py +49 -0
- metaflow/_vendor/yaml/parser.py +589 -0
- metaflow/_vendor/yaml/reader.py +185 -0
- metaflow/_vendor/yaml/representer.py +389 -0
- metaflow/_vendor/yaml/resolver.py +227 -0
- metaflow/_vendor/yaml/scanner.py +1435 -0
- metaflow/_vendor/yaml/serializer.py +111 -0
- metaflow/_vendor/yaml/tokens.py +104 -0
- metaflow/cli.py +11 -2
- metaflow/cli_components/run_cmds.py +0 -15
- metaflow/client/core.py +6 -1
- metaflow/extension_support/__init__.py +4 -3
- metaflow/flowspec.py +1 -113
- metaflow/graph.py +10 -134
- metaflow/lint.py +3 -70
- metaflow/metaflow_environment.py +14 -6
- metaflow/package/__init__.py +18 -9
- metaflow/packaging_sys/__init__.py +53 -43
- metaflow/packaging_sys/backend.py +21 -6
- metaflow/packaging_sys/tar_backend.py +16 -3
- metaflow/packaging_sys/v1.py +21 -21
- metaflow/plugins/argo/argo_client.py +31 -14
- metaflow/plugins/argo/argo_workflows.py +67 -22
- metaflow/plugins/argo/argo_workflows_cli.py +348 -85
- metaflow/plugins/argo/argo_workflows_deployer_objects.py +69 -0
- metaflow/plugins/aws/step_functions/step_functions.py +0 -6
- metaflow/plugins/aws/step_functions/step_functions_deployer_objects.py +30 -0
- metaflow/plugins/cards/card_modules/basic.py +3 -14
- metaflow/plugins/cards/card_modules/convert_to_native_type.py +7 -1
- metaflow/plugins/kubernetes/kubernetes_decorator.py +1 -1
- metaflow/plugins/kubernetes/kubernetes_job.py +8 -2
- metaflow/plugins/kubernetes/kubernetes_jobsets.py +26 -28
- metaflow/plugins/pypi/conda_decorator.py +4 -2
- metaflow/runner/click_api.py +14 -7
- metaflow/runner/deployer.py +160 -7
- metaflow/runner/subprocess_manager.py +20 -12
- metaflow/runtime.py +27 -102
- metaflow/task.py +25 -46
- metaflow/user_decorators/mutable_flow.py +3 -1
- metaflow/util.py +0 -29
- metaflow/vendor.py +23 -6
- metaflow/version.py +1 -1
- {ob_metaflow-2.16.8.2rc2.dist-info → ob_metaflow-2.17.0.1.dist-info}/METADATA +2 -2
- {ob_metaflow-2.16.8.2rc2.dist-info → ob_metaflow-2.17.0.1.dist-info}/RECORD +62 -45
- {ob_metaflow-2.16.8.2rc2.data → ob_metaflow-2.17.0.1.data}/data/share/metaflow/devtools/Makefile +0 -0
- {ob_metaflow-2.16.8.2rc2.data → ob_metaflow-2.17.0.1.data}/data/share/metaflow/devtools/Tiltfile +0 -0
- {ob_metaflow-2.16.8.2rc2.data → ob_metaflow-2.17.0.1.data}/data/share/metaflow/devtools/pick_services.sh +0 -0
- {ob_metaflow-2.16.8.2rc2.dist-info → ob_metaflow-2.17.0.1.dist-info}/WHEEL +0 -0
- {ob_metaflow-2.16.8.2rc2.dist-info → ob_metaflow-2.17.0.1.dist-info}/entry_points.txt +0 -0
- {ob_metaflow-2.16.8.2rc2.dist-info → ob_metaflow-2.17.0.1.dist-info}/licenses/LICENSE +0 -0
- {ob_metaflow-2.16.8.2rc2.dist-info → ob_metaflow-2.17.0.1.dist-info}/top_level.txt +0 -0
|
@@ -57,6 +57,15 @@ class PackagingBackend(ABC):
|
|
|
57
57
|
"""Open the archive from the given content."""
|
|
58
58
|
pass
|
|
59
59
|
|
|
60
|
+
@classmethod
|
|
61
|
+
@abstractmethod
|
|
62
|
+
def cls_member_name(cls, member: Union[Any, str]) -> str:
|
|
63
|
+
"""
|
|
64
|
+
Returns the name of the member as a string.
|
|
65
|
+
This is used to ensure consistent naming across different archive formats.
|
|
66
|
+
"""
|
|
67
|
+
pass
|
|
68
|
+
|
|
60
69
|
@classmethod
|
|
61
70
|
@abstractmethod
|
|
62
71
|
def cls_has_member(cls, archive: Any, name: str) -> bool:
|
|
@@ -72,14 +81,20 @@ class PackagingBackend(ABC):
|
|
|
72
81
|
def cls_extract_members(
|
|
73
82
|
cls,
|
|
74
83
|
archive: Any,
|
|
75
|
-
members: Optional[List[
|
|
84
|
+
members: Optional[List[Any]] = None,
|
|
76
85
|
dest_dir: str = ".",
|
|
77
86
|
) -> None:
|
|
78
87
|
pass
|
|
79
88
|
|
|
80
89
|
@classmethod
|
|
81
90
|
@abstractmethod
|
|
82
|
-
def
|
|
91
|
+
def cls_list_names(cls, archive: Any) -> Optional[List[str]]:
|
|
92
|
+
pass
|
|
93
|
+
|
|
94
|
+
@classmethod
|
|
95
|
+
@abstractmethod
|
|
96
|
+
def cls_list_members(cls, archive: Any) -> Optional[List[Any]]:
|
|
97
|
+
"""List all members in the archive."""
|
|
83
98
|
pass
|
|
84
99
|
|
|
85
100
|
def has_member(self, name: str) -> bool:
|
|
@@ -93,17 +108,17 @@ class PackagingBackend(ABC):
|
|
|
93
108
|
raise ValueError("Cannot get member from an uncreated archive")
|
|
94
109
|
|
|
95
110
|
def extract_members(
|
|
96
|
-
self, members: Optional[List[
|
|
111
|
+
self, members: Optional[List[Any]] = None, dest_dir: str = "."
|
|
97
112
|
) -> None:
|
|
98
113
|
if self._archive:
|
|
99
114
|
self.cls_extract_members(self._archive, members, dest_dir)
|
|
100
115
|
else:
|
|
101
116
|
raise ValueError("Cannot extract from an uncreated archive")
|
|
102
117
|
|
|
103
|
-
def
|
|
118
|
+
def list_names(self) -> Optional[List[str]]:
|
|
104
119
|
if self._archive:
|
|
105
|
-
return self.
|
|
106
|
-
raise ValueError("Cannot list
|
|
120
|
+
return self.cls_list_names(self._archive)
|
|
121
|
+
raise ValueError("Cannot list names from an uncreated archive")
|
|
107
122
|
|
|
108
123
|
def __enter__(self):
|
|
109
124
|
self.create()
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import tarfile
|
|
2
2
|
|
|
3
3
|
from io import BytesIO
|
|
4
|
-
from typing import IO, List, Optional, Union
|
|
4
|
+
from typing import Any, IO, List, Optional, Union
|
|
5
5
|
|
|
6
6
|
from .backend import PackagingBackend
|
|
7
7
|
|
|
@@ -56,6 +56,13 @@ class TarPackagingBackend(PackagingBackend):
|
|
|
56
56
|
def cls_open(cls, content: IO[bytes]) -> tarfile.TarFile:
|
|
57
57
|
return tarfile.open(fileobj=content, mode="r:gz")
|
|
58
58
|
|
|
59
|
+
@classmethod
|
|
60
|
+
def cls_member_name(cls, member: Union[tarfile.TarInfo, str]) -> str:
|
|
61
|
+
"""
|
|
62
|
+
Returns the name of the member as a string.
|
|
63
|
+
"""
|
|
64
|
+
return member.name if isinstance(member, tarfile.TarInfo) else member
|
|
65
|
+
|
|
59
66
|
@classmethod
|
|
60
67
|
def cls_has_member(cls, archive: tarfile.TarFile, name: str) -> bool:
|
|
61
68
|
try:
|
|
@@ -76,11 +83,17 @@ class TarPackagingBackend(PackagingBackend):
|
|
|
76
83
|
def cls_extract_members(
|
|
77
84
|
cls,
|
|
78
85
|
archive: tarfile.TarFile,
|
|
79
|
-
members: Optional[List[
|
|
86
|
+
members: Optional[List[Any]] = None,
|
|
80
87
|
dest_dir: str = ".",
|
|
81
88
|
) -> None:
|
|
82
89
|
archive.extractall(path=dest_dir, members=members)
|
|
83
90
|
|
|
84
91
|
@classmethod
|
|
85
|
-
def cls_list_members(
|
|
92
|
+
def cls_list_members(
|
|
93
|
+
cls, archive: tarfile.TarFile
|
|
94
|
+
) -> Optional[List[tarfile.TarInfo]]:
|
|
95
|
+
return archive.getmembers() or None
|
|
96
|
+
|
|
97
|
+
@classmethod
|
|
98
|
+
def cls_list_names(cls, archive: tarfile.TarFile) -> Optional[List[str]]:
|
|
86
99
|
return archive.getnames() or None
|
metaflow/packaging_sys/v1.py
CHANGED
|
@@ -61,23 +61,25 @@ class MetaflowCodeContentV1(MetaflowCodeContentV1Base):
|
|
|
61
61
|
else:
|
|
62
62
|
new_modules = []
|
|
63
63
|
|
|
64
|
-
self._modules = {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
64
|
+
self._modules = {} # type: Dict[str, _ModuleInfo]
|
|
65
|
+
# We do this explicitly module by module to harden it against misbehaving
|
|
66
|
+
# modules like the one in:
|
|
67
|
+
# https://github.com/Netflix/metaflow/issues/2512
|
|
68
|
+
# We will silently ignore modules that are not well built.
|
|
69
|
+
for name, mod in new_modules:
|
|
70
|
+
try:
|
|
71
|
+
minfo = _ModuleInfo(
|
|
72
|
+
name,
|
|
73
|
+
set(
|
|
74
|
+
Path(p).resolve().as_posix()
|
|
75
|
+
for p in getattr(mod, "__path__", [mod.__file__])
|
|
76
|
+
),
|
|
77
|
+
mod,
|
|
78
|
+
True, # This is a Metaflow module (see filter below)
|
|
79
|
+
)
|
|
80
|
+
except:
|
|
81
|
+
continue
|
|
82
|
+
self._modules[name] = minfo
|
|
81
83
|
|
|
82
84
|
# Contain metadata information regarding the distributions packaged.
|
|
83
85
|
# This allows Metaflow to "fake" distribution information when packaged
|
|
@@ -355,16 +357,14 @@ class MetaflowCodeContentV1(MetaflowCodeContentV1Base):
|
|
|
355
357
|
)
|
|
356
358
|
yield json.dumps(self.create_mfcontent_info()).encode(
|
|
357
359
|
"utf-8"
|
|
358
|
-
),
|
|
360
|
+
), MFCONTENT_MARKER
|
|
359
361
|
else:
|
|
360
362
|
for k in self._other_content.keys():
|
|
361
363
|
yield "<generated %s content>" % (os.path.basename(k)), k
|
|
362
364
|
yield "<generated %s content>" % (
|
|
363
365
|
os.path.basename(self._dist_info_file)
|
|
364
366
|
), os.path.join(self._other_dir, self._dist_info_file)
|
|
365
|
-
yield "<generated %s content>" % MFCONTENT_MARKER,
|
|
366
|
-
self._code_dir, MFCONTENT_MARKER
|
|
367
|
-
)
|
|
367
|
+
yield "<generated %s content>" % MFCONTENT_MARKER, MFCONTENT_MARKER
|
|
368
368
|
|
|
369
369
|
def _metaflow_distribution_files(self) -> Generator[Tuple[str, str], None, None]:
|
|
370
370
|
debug.package_exec("Including Metaflow from '%s'" % self._metaflow_root)
|
|
@@ -58,21 +58,38 @@ class ArgoClient(object):
|
|
|
58
58
|
json.loads(e.body)["message"] if e.body is not None else e.reason
|
|
59
59
|
)
|
|
60
60
|
|
|
61
|
-
def get_workflow_templates(self):
|
|
61
|
+
def get_workflow_templates(self, page_size=100):
|
|
62
62
|
client = self._client.get()
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
63
|
+
continue_token = None
|
|
64
|
+
|
|
65
|
+
while True:
|
|
66
|
+
try:
|
|
67
|
+
params = {"limit": page_size}
|
|
68
|
+
if continue_token:
|
|
69
|
+
params["_continue"] = continue_token
|
|
70
|
+
|
|
71
|
+
response = client.CustomObjectsApi().list_namespaced_custom_object(
|
|
72
|
+
group=self._group,
|
|
73
|
+
version=self._version,
|
|
74
|
+
namespace=self._namespace,
|
|
75
|
+
plural="workflowtemplates",
|
|
76
|
+
**params,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
for item in response.get("items", []):
|
|
80
|
+
yield item
|
|
81
|
+
|
|
82
|
+
metadata = response.get("metadata", {})
|
|
83
|
+
continue_token = metadata.get("continue")
|
|
84
|
+
|
|
85
|
+
if not continue_token:
|
|
86
|
+
break
|
|
87
|
+
except client.rest.ApiException as e:
|
|
88
|
+
if e.status == 404:
|
|
89
|
+
return None
|
|
90
|
+
raise ArgoClientException(
|
|
91
|
+
json.loads(e.body)["message"] if e.body is not None else e.reason
|
|
92
|
+
)
|
|
76
93
|
|
|
77
94
|
def register_workflow_template(self, name, workflow_template):
|
|
78
95
|
# Unfortunately, Kubernetes client does not handle optimistic
|
|
@@ -216,23 +216,14 @@ class ArgoWorkflows(object):
|
|
|
216
216
|
return name.replace(".", "-")
|
|
217
217
|
|
|
218
218
|
@staticmethod
|
|
219
|
-
def list_templates(flow_name, all=False):
|
|
219
|
+
def list_templates(flow_name, all=False, page_size=100):
|
|
220
220
|
client = ArgoClient(namespace=KUBERNETES_NAMESPACE)
|
|
221
221
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
template["metadata"]["name"]
|
|
228
|
-
for template in templates
|
|
229
|
-
if all
|
|
230
|
-
or flow_name
|
|
231
|
-
== template["metadata"]
|
|
232
|
-
.get("annotations", {})
|
|
233
|
-
.get("metaflow/flow_name", None)
|
|
234
|
-
]
|
|
235
|
-
return template_names
|
|
222
|
+
for template in client.get_workflow_templates(page_size=page_size):
|
|
223
|
+
if all or flow_name == template["metadata"].get("annotations", {}).get(
|
|
224
|
+
"metaflow/flow_name", None
|
|
225
|
+
):
|
|
226
|
+
yield template["metadata"]["name"]
|
|
236
227
|
|
|
237
228
|
@staticmethod
|
|
238
229
|
def delete(name):
|
|
@@ -271,6 +262,7 @@ class ArgoWorkflows(object):
|
|
|
271
262
|
flow_name=flow_name, run_id=name
|
|
272
263
|
)
|
|
273
264
|
)
|
|
265
|
+
return True
|
|
274
266
|
|
|
275
267
|
@staticmethod
|
|
276
268
|
def get_workflow_status(flow_name, name):
|
|
@@ -965,11 +957,6 @@ class ArgoWorkflows(object):
|
|
|
965
957
|
dag_task = DAGTask(self._sanitize(node.name)).template(
|
|
966
958
|
self._sanitize(node.name)
|
|
967
959
|
)
|
|
968
|
-
if node.type == "split-switch":
|
|
969
|
-
raise ArgoWorkflowsException(
|
|
970
|
-
"Deploying flows with switch statement "
|
|
971
|
-
"to Argo Workflows is not supported currently."
|
|
972
|
-
)
|
|
973
960
|
elif (
|
|
974
961
|
node.is_inside_foreach
|
|
975
962
|
and self.graph[node.in_funcs[0]].type == "foreach"
|
|
@@ -3291,8 +3278,8 @@ class ArgoWorkflows(object):
|
|
|
3291
3278
|
Trigger().template(
|
|
3292
3279
|
TriggerTemplate(self.name)
|
|
3293
3280
|
# Trigger a deployed workflow template
|
|
3294
|
-
.
|
|
3295
|
-
|
|
3281
|
+
.k8s_trigger(
|
|
3282
|
+
StandardK8STrigger()
|
|
3296
3283
|
.source(
|
|
3297
3284
|
{
|
|
3298
3285
|
"resource": {
|
|
@@ -4270,6 +4257,10 @@ class TriggerTemplate(object):
|
|
|
4270
4257
|
self.payload = tree()
|
|
4271
4258
|
self.payload["name"] = name
|
|
4272
4259
|
|
|
4260
|
+
def k8s_trigger(self, k8s_trigger):
|
|
4261
|
+
self.payload["k8s"] = k8s_trigger.to_json()
|
|
4262
|
+
return self
|
|
4263
|
+
|
|
4273
4264
|
def argo_workflow_trigger(self, argo_workflow_trigger):
|
|
4274
4265
|
self.payload["argoWorkflow"] = argo_workflow_trigger.to_json()
|
|
4275
4266
|
return self
|
|
@@ -4344,3 +4335,57 @@ class TriggerParameter(object):
|
|
|
4344
4335
|
|
|
4345
4336
|
def __str__(self):
|
|
4346
4337
|
return json.dumps(self.payload, indent=4)
|
|
4338
|
+
|
|
4339
|
+
|
|
4340
|
+
class StandardK8STrigger(object):
|
|
4341
|
+
# https://pkg.go.dev/github.com/argoproj/argo-events/pkg/apis/sensor/v1alpha1#StandardK8STrigger
|
|
4342
|
+
|
|
4343
|
+
def __init__(self):
|
|
4344
|
+
tree = lambda: defaultdict(tree)
|
|
4345
|
+
self.payload = tree()
|
|
4346
|
+
self.payload["operation"] = "create"
|
|
4347
|
+
|
|
4348
|
+
def operation(self, operation):
|
|
4349
|
+
self.payload["operation"] = operation
|
|
4350
|
+
return self
|
|
4351
|
+
|
|
4352
|
+
def group(self, group):
|
|
4353
|
+
self.payload["group"] = group
|
|
4354
|
+
return self
|
|
4355
|
+
|
|
4356
|
+
def version(self, version):
|
|
4357
|
+
self.payload["version"] = version
|
|
4358
|
+
return self
|
|
4359
|
+
|
|
4360
|
+
def resource(self, resource):
|
|
4361
|
+
self.payload["resource"] = resource
|
|
4362
|
+
return self
|
|
4363
|
+
|
|
4364
|
+
def namespace(self, namespace):
|
|
4365
|
+
self.payload["namespace"] = namespace
|
|
4366
|
+
return self
|
|
4367
|
+
|
|
4368
|
+
def source(self, source):
|
|
4369
|
+
self.payload["source"] = source
|
|
4370
|
+
return self
|
|
4371
|
+
|
|
4372
|
+
def parameters(self, trigger_parameters):
|
|
4373
|
+
if "parameters" not in self.payload:
|
|
4374
|
+
self.payload["parameters"] = []
|
|
4375
|
+
for trigger_parameter in trigger_parameters:
|
|
4376
|
+
self.payload["parameters"].append(trigger_parameter.to_json())
|
|
4377
|
+
return self
|
|
4378
|
+
|
|
4379
|
+
def live_object(self, live_object=True):
|
|
4380
|
+
self.payload["liveObject"] = live_object
|
|
4381
|
+
return self
|
|
4382
|
+
|
|
4383
|
+
def patch_strategy(self, patch_strategy):
|
|
4384
|
+
self.payload["patchStrategy"] = patch_strategy
|
|
4385
|
+
return self
|
|
4386
|
+
|
|
4387
|
+
def to_json(self):
|
|
4388
|
+
return self.payload
|
|
4389
|
+
|
|
4390
|
+
def __str__(self):
|
|
4391
|
+
return json.dumps(self.payload, indent=4)
|