ob-metaflow 2.15.13.1__py2.py3-none-any.whl → 2.19.7.1rc0__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.
Files changed (169) hide show
  1. metaflow/__init__.py +10 -3
  2. metaflow/_vendor/imghdr/__init__.py +186 -0
  3. metaflow/_vendor/yaml/__init__.py +427 -0
  4. metaflow/_vendor/yaml/composer.py +139 -0
  5. metaflow/_vendor/yaml/constructor.py +748 -0
  6. metaflow/_vendor/yaml/cyaml.py +101 -0
  7. metaflow/_vendor/yaml/dumper.py +62 -0
  8. metaflow/_vendor/yaml/emitter.py +1137 -0
  9. metaflow/_vendor/yaml/error.py +75 -0
  10. metaflow/_vendor/yaml/events.py +86 -0
  11. metaflow/_vendor/yaml/loader.py +63 -0
  12. metaflow/_vendor/yaml/nodes.py +49 -0
  13. metaflow/_vendor/yaml/parser.py +589 -0
  14. metaflow/_vendor/yaml/reader.py +185 -0
  15. metaflow/_vendor/yaml/representer.py +389 -0
  16. metaflow/_vendor/yaml/resolver.py +227 -0
  17. metaflow/_vendor/yaml/scanner.py +1435 -0
  18. metaflow/_vendor/yaml/serializer.py +111 -0
  19. metaflow/_vendor/yaml/tokens.py +104 -0
  20. metaflow/cards.py +4 -0
  21. metaflow/cli.py +125 -21
  22. metaflow/cli_components/init_cmd.py +1 -0
  23. metaflow/cli_components/run_cmds.py +204 -40
  24. metaflow/cli_components/step_cmd.py +160 -4
  25. metaflow/client/__init__.py +1 -0
  26. metaflow/client/core.py +198 -130
  27. metaflow/client/filecache.py +59 -32
  28. metaflow/cmd/code/__init__.py +2 -1
  29. metaflow/cmd/develop/stub_generator.py +49 -18
  30. metaflow/cmd/develop/stubs.py +9 -27
  31. metaflow/cmd/make_wrapper.py +30 -0
  32. metaflow/datastore/__init__.py +1 -0
  33. metaflow/datastore/content_addressed_store.py +40 -9
  34. metaflow/datastore/datastore_set.py +10 -1
  35. metaflow/datastore/flow_datastore.py +124 -4
  36. metaflow/datastore/spin_datastore.py +91 -0
  37. metaflow/datastore/task_datastore.py +92 -6
  38. metaflow/debug.py +5 -0
  39. metaflow/decorators.py +331 -82
  40. metaflow/extension_support/__init__.py +414 -356
  41. metaflow/extension_support/_empty_file.py +2 -2
  42. metaflow/flowspec.py +322 -82
  43. metaflow/graph.py +178 -15
  44. metaflow/includefile.py +25 -3
  45. metaflow/lint.py +94 -3
  46. metaflow/meta_files.py +13 -0
  47. metaflow/metadata_provider/metadata.py +13 -2
  48. metaflow/metaflow_config.py +66 -4
  49. metaflow/metaflow_environment.py +91 -25
  50. metaflow/metaflow_profile.py +18 -0
  51. metaflow/metaflow_version.py +16 -1
  52. metaflow/package/__init__.py +673 -0
  53. metaflow/packaging_sys/__init__.py +880 -0
  54. metaflow/packaging_sys/backend.py +128 -0
  55. metaflow/packaging_sys/distribution_support.py +153 -0
  56. metaflow/packaging_sys/tar_backend.py +99 -0
  57. metaflow/packaging_sys/utils.py +54 -0
  58. metaflow/packaging_sys/v1.py +527 -0
  59. metaflow/parameters.py +6 -2
  60. metaflow/plugins/__init__.py +6 -0
  61. metaflow/plugins/airflow/airflow.py +11 -1
  62. metaflow/plugins/airflow/airflow_cli.py +16 -5
  63. metaflow/plugins/argo/argo_client.py +42 -20
  64. metaflow/plugins/argo/argo_events.py +6 -6
  65. metaflow/plugins/argo/argo_workflows.py +1023 -344
  66. metaflow/plugins/argo/argo_workflows_cli.py +396 -94
  67. metaflow/plugins/argo/argo_workflows_decorator.py +9 -0
  68. metaflow/plugins/argo/argo_workflows_deployer_objects.py +75 -49
  69. metaflow/plugins/argo/capture_error.py +5 -2
  70. metaflow/plugins/argo/conditional_input_paths.py +35 -0
  71. metaflow/plugins/argo/exit_hooks.py +209 -0
  72. metaflow/plugins/argo/param_val.py +19 -0
  73. metaflow/plugins/aws/aws_client.py +6 -0
  74. metaflow/plugins/aws/aws_utils.py +33 -1
  75. metaflow/plugins/aws/batch/batch.py +72 -5
  76. metaflow/plugins/aws/batch/batch_cli.py +24 -3
  77. metaflow/plugins/aws/batch/batch_decorator.py +57 -6
  78. metaflow/plugins/aws/step_functions/step_functions.py +28 -3
  79. metaflow/plugins/aws/step_functions/step_functions_cli.py +49 -4
  80. metaflow/plugins/aws/step_functions/step_functions_deployer.py +3 -0
  81. metaflow/plugins/aws/step_functions/step_functions_deployer_objects.py +30 -0
  82. metaflow/plugins/cards/card_cli.py +20 -1
  83. metaflow/plugins/cards/card_creator.py +24 -1
  84. metaflow/plugins/cards/card_datastore.py +21 -49
  85. metaflow/plugins/cards/card_decorator.py +58 -6
  86. metaflow/plugins/cards/card_modules/basic.py +38 -9
  87. metaflow/plugins/cards/card_modules/bundle.css +1 -1
  88. metaflow/plugins/cards/card_modules/chevron/renderer.py +1 -1
  89. metaflow/plugins/cards/card_modules/components.py +592 -3
  90. metaflow/plugins/cards/card_modules/convert_to_native_type.py +34 -5
  91. metaflow/plugins/cards/card_modules/json_viewer.py +232 -0
  92. metaflow/plugins/cards/card_modules/main.css +1 -0
  93. metaflow/plugins/cards/card_modules/main.js +56 -41
  94. metaflow/plugins/cards/card_modules/test_cards.py +22 -6
  95. metaflow/plugins/cards/component_serializer.py +1 -8
  96. metaflow/plugins/cards/metadata.py +22 -0
  97. metaflow/plugins/catch_decorator.py +9 -0
  98. metaflow/plugins/datastores/local_storage.py +12 -6
  99. metaflow/plugins/datastores/spin_storage.py +12 -0
  100. metaflow/plugins/datatools/s3/s3.py +49 -17
  101. metaflow/plugins/datatools/s3/s3op.py +113 -66
  102. metaflow/plugins/env_escape/client_modules.py +102 -72
  103. metaflow/plugins/events_decorator.py +127 -121
  104. metaflow/plugins/exit_hook/__init__.py +0 -0
  105. metaflow/plugins/exit_hook/exit_hook_decorator.py +46 -0
  106. metaflow/plugins/exit_hook/exit_hook_script.py +52 -0
  107. metaflow/plugins/kubernetes/kubernetes.py +12 -1
  108. metaflow/plugins/kubernetes/kubernetes_cli.py +11 -0
  109. metaflow/plugins/kubernetes/kubernetes_decorator.py +25 -6
  110. metaflow/plugins/kubernetes/kubernetes_job.py +12 -4
  111. metaflow/plugins/kubernetes/kubernetes_jobsets.py +31 -30
  112. metaflow/plugins/metadata_providers/local.py +76 -82
  113. metaflow/plugins/metadata_providers/service.py +13 -9
  114. metaflow/plugins/metadata_providers/spin.py +16 -0
  115. metaflow/plugins/package_cli.py +36 -24
  116. metaflow/plugins/parallel_decorator.py +11 -2
  117. metaflow/plugins/parsers.py +16 -0
  118. metaflow/plugins/pypi/bootstrap.py +7 -1
  119. metaflow/plugins/pypi/conda_decorator.py +41 -82
  120. metaflow/plugins/pypi/conda_environment.py +14 -6
  121. metaflow/plugins/pypi/micromamba.py +9 -1
  122. metaflow/plugins/pypi/pip.py +41 -5
  123. metaflow/plugins/pypi/pypi_decorator.py +4 -4
  124. metaflow/plugins/pypi/utils.py +22 -0
  125. metaflow/plugins/secrets/__init__.py +3 -0
  126. metaflow/plugins/secrets/secrets_decorator.py +14 -178
  127. metaflow/plugins/secrets/secrets_func.py +49 -0
  128. metaflow/plugins/secrets/secrets_spec.py +101 -0
  129. metaflow/plugins/secrets/utils.py +74 -0
  130. metaflow/plugins/test_unbounded_foreach_decorator.py +2 -2
  131. metaflow/plugins/timeout_decorator.py +0 -1
  132. metaflow/plugins/uv/bootstrap.py +29 -1
  133. metaflow/plugins/uv/uv_environment.py +5 -3
  134. metaflow/pylint_wrapper.py +5 -1
  135. metaflow/runner/click_api.py +79 -26
  136. metaflow/runner/deployer.py +208 -6
  137. metaflow/runner/deployer_impl.py +32 -12
  138. metaflow/runner/metaflow_runner.py +266 -33
  139. metaflow/runner/subprocess_manager.py +21 -1
  140. metaflow/runner/utils.py +27 -16
  141. metaflow/runtime.py +660 -66
  142. metaflow/task.py +255 -26
  143. metaflow/user_configs/config_options.py +33 -21
  144. metaflow/user_configs/config_parameters.py +220 -58
  145. metaflow/user_decorators/__init__.py +0 -0
  146. metaflow/user_decorators/common.py +144 -0
  147. metaflow/user_decorators/mutable_flow.py +512 -0
  148. metaflow/user_decorators/mutable_step.py +424 -0
  149. metaflow/user_decorators/user_flow_decorator.py +264 -0
  150. metaflow/user_decorators/user_step_decorator.py +749 -0
  151. metaflow/util.py +197 -7
  152. metaflow/vendor.py +23 -7
  153. metaflow/version.py +1 -1
  154. {ob_metaflow-2.15.13.1.data → ob_metaflow-2.19.7.1rc0.data}/data/share/metaflow/devtools/Makefile +13 -2
  155. {ob_metaflow-2.15.13.1.data → ob_metaflow-2.19.7.1rc0.data}/data/share/metaflow/devtools/Tiltfile +107 -7
  156. {ob_metaflow-2.15.13.1.data → ob_metaflow-2.19.7.1rc0.data}/data/share/metaflow/devtools/pick_services.sh +1 -0
  157. {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/METADATA +2 -3
  158. {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/RECORD +162 -121
  159. {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/WHEEL +1 -1
  160. metaflow/_vendor/v3_5/__init__.py +0 -1
  161. metaflow/_vendor/v3_5/importlib_metadata/__init__.py +0 -644
  162. metaflow/_vendor/v3_5/importlib_metadata/_compat.py +0 -152
  163. metaflow/_vendor/v3_5/zipp.py +0 -329
  164. metaflow/info_file.py +0 -25
  165. metaflow/package.py +0 -203
  166. metaflow/user_configs/config_decorators.py +0 -568
  167. {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/entry_points.txt +0 -0
  168. {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/licenses/LICENSE +0 -0
  169. {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/top_level.txt +0 -0
metaflow/util.py CHANGED
@@ -4,12 +4,13 @@ import sys
4
4
  import tempfile
5
5
  import zlib
6
6
  import base64
7
+ import re
8
+
7
9
  from functools import wraps
8
10
  from io import BytesIO
9
11
  from itertools import takewhile
10
- import re
12
+ from typing import Dict, Any, Tuple, Optional, List, Generator
11
13
 
12
- from metaflow.exception import MetaflowUnknownUser, MetaflowInternalError
13
14
 
14
15
  try:
15
16
  # python2
@@ -162,6 +163,8 @@ def get_username():
162
163
 
163
164
 
164
165
  def resolve_identity_as_tuple():
166
+ from metaflow.exception import MetaflowUnknownUser
167
+
165
168
  prod_token = os.environ.get("METAFLOW_PRODUCTION_TOKEN")
166
169
  if prod_token:
167
170
  return "production", prod_token
@@ -177,6 +180,119 @@ def resolve_identity():
177
180
  return "%s:%s" % (identity_type, identity_value)
178
181
 
179
182
 
183
+ def parse_spin_pathspec(pathspec: str, flow_name: str) -> Tuple:
184
+ """
185
+ Parse various pathspec formats for the spin command.
186
+
187
+ Parameters
188
+ ----------
189
+ pathspec : str
190
+ The pathspec string in one of the following formats:
191
+ - step_name (e.g., 'start')
192
+ - run_id/step_name (e.g., '221165/start')
193
+ - run_id/step_name/task_id (e.g., '221165/start/1350987')
194
+ - flow_name/run_id/step_name (e.g., 'ScalableFlow/221165/start')
195
+ - flow_name/run_id/step_name/task_id (e.g., 'ScalableFlow/221165/start/1350987')
196
+ flow_name : str
197
+ The name of the current flow.
198
+
199
+ Returns
200
+ -------
201
+ Tuple
202
+ A tuple of (step_name, full_pathspec_or_none)
203
+
204
+ Raises
205
+ ------
206
+ CommandException
207
+ If the pathspec format is invalid or flow name doesn't match.
208
+ """
209
+ from .exception import CommandException
210
+
211
+ parts = pathspec.split("/")
212
+
213
+ if len(parts) == 1:
214
+ # Just step name: 'start'
215
+ step_name = parts[0]
216
+ parsed_pathspec = None
217
+ elif len(parts) == 2:
218
+ # run_id/step_name: '221165/start'
219
+ run_id, step_name = parts
220
+ parsed_pathspec = f"{flow_name}/{run_id}/{step_name}"
221
+ elif len(parts) == 3:
222
+ # Could be run_id/step_name/task_id or flow_name/run_id/step_name
223
+ if parts[0] == flow_name:
224
+ # flow_name/run_id/step_name
225
+ _, run_id, step_name = parts
226
+ parsed_pathspec = f"{flow_name}/{run_id}/{step_name}"
227
+ else:
228
+ # run_id/step_name/task_id
229
+ run_id, step_name, task_id = parts
230
+ parsed_pathspec = f"{flow_name}/{run_id}/{step_name}/{task_id}"
231
+ elif len(parts) == 4:
232
+ # flow_name/run_id/step_name/task_id
233
+ parsed_flow_name, run_id, step_name, task_id = parts
234
+ if parsed_flow_name != flow_name:
235
+ raise CommandException(
236
+ f"Flow name '{parsed_flow_name}' in pathspec does not match current flow '{flow_name}'."
237
+ )
238
+ parsed_pathspec = pathspec
239
+ else:
240
+ raise CommandException(
241
+ f"Invalid pathspec format: '{pathspec}'. \n"
242
+ "Expected formats:\n"
243
+ " - step_name (e.g., 'start')\n"
244
+ " - run_id/step_name (e.g., '221165/start')\n"
245
+ " - run_id/step_name/task_id (e.g., '221165/start/1350987')\n"
246
+ " - flow_name/run_id/step_name (e.g., 'ScalableFlow/221165/start')\n"
247
+ " - flow_name/run_id/step_name/task_id (e.g., 'ScalableFlow/221165/start/1350987')"
248
+ )
249
+
250
+ return step_name, parsed_pathspec
251
+
252
+
253
+ def get_latest_task_pathspec(
254
+ flow_name: str, step_name: str, run_id: str = None
255
+ ) -> "metaflow.Task":
256
+ """
257
+ Returns a task pathspec from the latest run (or specified run) of the flow for the queried step.
258
+ If the queried step has several tasks, the task pathspec of the first task is returned.
259
+
260
+ Parameters
261
+ ----------
262
+ flow_name : str
263
+ The name of the flow.
264
+ step_name : str
265
+ The name of the step.
266
+ run_id : str, optional
267
+ The run ID to use. If None, uses the latest run.
268
+
269
+ Returns
270
+ -------
271
+ Task
272
+ A Metaflow Task instance containing the latest task for the queried step.
273
+
274
+ Raises
275
+ ------
276
+ MetaflowNotFound
277
+ If no task or run is found for the queried step.
278
+ """
279
+ from metaflow import Flow, Step
280
+ from metaflow.exception import MetaflowNotFound
281
+
282
+ if not run_id:
283
+ flow = Flow(flow_name)
284
+ run = flow.latest_run
285
+ if run is None:
286
+ raise MetaflowNotFound(f"No run found for flow {flow_name}")
287
+ run_id = run.id
288
+
289
+ try:
290
+ task = Step(f"{flow_name}/{run_id}/{step_name}").task
291
+ return task
292
+ except:
293
+ raise MetaflowNotFound(f"No task found for step {step_name} in run {run_id}")
294
+
295
+
180
296
  def get_latest_run_id(echo, flow_name):
181
297
  from metaflow.plugins.datastores.local_storage import LocalStorage
182
298
 
@@ -236,6 +352,8 @@ def get_object_package_version(obj):
236
352
 
237
353
 
238
354
  def compress_list(lst, separator=",", rangedelim=":", zlibmarker="!", zlibmin=500):
355
+ from metaflow.exception import MetaflowInternalError
356
+
239
357
  bad_items = [x for x in lst if separator in x or rangedelim in x or zlibmarker in x]
240
358
  if bad_items:
241
359
  raise MetaflowInternalError(
@@ -418,7 +536,7 @@ def to_pascalcase(obj):
418
536
  if isinstance(obj, dict):
419
537
  res = obj.__class__()
420
538
  for k in obj:
421
- res[re.sub("([a-zA-Z])", lambda x: x.groups()[0].upper(), k, 1)] = (
539
+ res[re.sub("([a-zA-Z])", lambda x: x.groups()[0].upper(), k, count=1)] = (
422
540
  to_pascalcase(obj[k])
423
541
  )
424
542
  elif isinstance(obj, (list, set, tuple)):
@@ -467,7 +585,79 @@ def to_pod(value):
467
585
  return str(value)
468
586
 
469
587
 
470
- if sys.version_info[:2] > (3, 5):
471
- from metaflow._vendor.packaging.version import parse as version_parse
472
- else:
473
- from distutils.version import LooseVersion as version_parse
588
+ from metaflow._vendor.packaging.version import parse as version_parse
589
+
590
+
591
+ def read_artifacts_module(file_path: str) -> Dict[str, Any]:
592
+ """
593
+ Read a Python module from the given file path and return its ARTIFACTS variable.
594
+
595
+ Parameters
596
+ ----------
597
+ file_path : str
598
+ The path to the Python file containing the ARTIFACTS variable.
599
+
600
+ Returns
601
+ -------
602
+ Dict[str, Any]
603
+ A dictionary containing the ARTIFACTS variable from the module.
604
+
605
+ Raises
606
+ -------
607
+ MetaflowInternalError
608
+ If the file cannot be read or does not contain the ARTIFACTS variable.
609
+ """
610
+ import importlib.util
611
+ import os
612
+
613
+ try:
614
+ module_name = os.path.splitext(os.path.basename(file_path))[0]
615
+ spec = importlib.util.spec_from_file_location(module_name, file_path)
616
+ module = importlib.util.module_from_spec(spec)
617
+ spec.loader.exec_module(module)
618
+ variables = vars(module)
619
+ if "ARTIFACTS" not in variables:
620
+ raise MetaflowInternalError(
621
+ f"Module {file_path} does not contain ARTIFACTS variable"
622
+ )
623
+ return variables.get("ARTIFACTS")
624
+ except Exception as e:
625
+ raise MetaflowInternalError(f"Error reading file {file_path}") from e
626
+
627
+
628
+ # this is os.walk(follow_symlinks=True) with cycle detection
629
+ def walk_without_cycles(
630
+ top_root: str,
631
+ exclude_dirs: Optional[List[str]] = None,
632
+ ) -> Generator[Tuple[str, List[str], List[str]], None, None]:
633
+ seen = set()
634
+
635
+ default_skip_dirs = ["__pycache__"]
636
+
637
+ def _recurse(root, skip_dirs):
638
+ for parent, dirs, files in os.walk(root):
639
+ dirs[:] = [d for d in dirs if d not in skip_dirs]
640
+ for d in dirs:
641
+ path = os.path.join(parent, d)
642
+ if os.path.islink(path):
643
+ # Breaking loops: never follow the same symlink twice
644
+ #
645
+ # NOTE: this also means that links to sibling links are
646
+ # not followed. In this case:
647
+ #
648
+ # x -> y
649
+ # y -> oo
650
+ # oo/real_file
651
+ #
652
+ # real_file is only included twice, not three times
653
+ reallink = os.path.realpath(path)
654
+ if reallink not in seen:
655
+ seen.add(reallink)
656
+ for x in _recurse(path, default_skip_dirs):
657
+ yield x
658
+ yield parent, dirs, files
659
+
660
+ skip_dirs = set(default_skip_dirs + (exclude_dirs or []))
661
+ for x in _recurse(top_root, skip_dirs):
662
+ skip_dirs = default_skip_dirs
663
+ yield x
metaflow/vendor.py CHANGED
@@ -11,7 +11,6 @@ WHITELIST = {
11
11
  "README.txt",
12
12
  "__init__.py",
13
13
  "vendor_any.txt",
14
- "vendor_v3_5.txt",
15
14
  "vendor_v3_6.txt",
16
15
  "vendor_v3_7.txt",
17
16
  "pip.LICENSE",
@@ -64,14 +63,29 @@ def find_vendored_libs(vendor_dir, whitelist, whitelist_dirs):
64
63
  return vendored_libs, paths
65
64
 
66
65
 
67
- def fetch_licenses(*info_dir, vendor_dir):
68
- for file in chain.from_iterable(map(iter_subtree, info_dir)):
69
- if "LICENSE" in file.name:
70
- library = file.parent.name.split("-")[0]
71
- shutil.copy(file, vendor_dir / ("%s.LICENSE" % library))
72
- else:
66
+ def fetch_licenses(*info_dirs, vendor_dir):
67
+ for dist_info in info_dirs:
68
+ metadata_file = dist_info / "METADATA"
69
+ if not metadata_file.exists():
70
+ continue
71
+
72
+ project_name = None
73
+ for line in metadata_file.read_text("utf-8").splitlines():
74
+ if line.startswith("Name: "):
75
+ project_name = line.split("Name: ", 1)[1].strip()
76
+ break
77
+ if not project_name:
73
78
  continue
74
79
 
80
+ for item in dist_info.iterdir():
81
+ if item.is_file() and re.search(r"(LICENSE|COPYING)", item.name, re.I):
82
+ shutil.copy(item, vendor_dir / f"{project_name}.LICENSE")
83
+ elif item.is_dir() and item.name.lower() == "licenses":
84
+ for license_file in item.iterdir():
85
+ if license_file.is_file():
86
+ dest_name = f"{project_name}.{license_file.name}"
87
+ shutil.copy(license_file, vendor_dir / dest_name)
88
+
75
89
 
76
90
  def vendor(vendor_dir):
77
91
  # remove everything
@@ -109,6 +123,8 @@ def vendor(vendor_dir):
109
123
  "-r",
110
124
  "_vendor/vendor_%s.txt" % subdir,
111
125
  "--no-compile",
126
+ "--no-binary",
127
+ ":all:",
112
128
  ]
113
129
  )
114
130
 
metaflow/version.py CHANGED
@@ -1 +1 @@
1
- metaflow_version = "2.15.13.1"
1
+ metaflow_version = "2.19.7.1rc0"
@@ -31,6 +31,7 @@ MAKE_CMD := $(MAKE) -f "$(MKFILE_PATH)"
31
31
  MINIKUBE_CPUS ?= 4
32
32
  MINIKUBE_MEMORY ?= 6144
33
33
  MINIKUBE_DISK_SIZE ?= 20g
34
+ WAIT_TIMEOUT ?= 300
34
35
 
35
36
  ifeq ($(shell uname), Darwin)
36
37
  minikube_os = darwin
@@ -257,10 +258,11 @@ shell: setup-tilt
257
258
  echo "🔎 Using $$user_shell for interactive session."; \
258
259
  echo "🐍 If you installed Metaflow in a virtual environment, activate it now."; \
259
260
  if [ -f "$(DEVTOOLS_DIR)/aws_config" ]; then \
260
- env METAFLOW_HOME="$(DEVTOOLS_DIR)" \
261
+ env -u AWS_PROFILE \
262
+ -u AWS_SHARED_CREDENTIALS_FILE \
263
+ METAFLOW_HOME="$(DEVTOOLS_DIR)" \
261
264
  METAFLOW_PROFILE=local \
262
265
  AWS_CONFIG_FILE="$(DEVTOOLS_DIR)/aws_config" \
263
- AWS_SHARED_CREDENTIALS_FILE= \
264
266
  "$$user_shell" -i; \
265
267
  else \
266
268
  env METAFLOW_HOME="$(DEVTOOLS_DIR)" \
@@ -268,6 +270,15 @@ shell: setup-tilt
268
270
  "$$user_shell" -i; \
269
271
  fi'
270
272
 
273
+ wait-until-ready:
274
+ @echo "Waiting for infrastructure to be ready. Timing out in $(WAIT_TIMEOUT) seconds..."
275
+ @timeout $(WAIT_TIMEOUT) bash -c 'while [ ! -f $(DEVTOOLS_DIR)/start.sh ]; do sleep 10; done; echo "Infra is Ready"' || (echo "Waiting for infra timed out"&&exit 1)
276
+ # buffer to get the tilt api running
277
+ @timeout 120 bash -c 'while ! $(TILT) get session; do sleep 3;done'
278
+ @echo "Waiting for services to be ready. Timing out in $(WAIT_TIMEOUT) seconds..."
279
+ # Need to wait for Tiltfile first, as other resources return 404 otherwise
280
+ @$(TILT) wait --for=condition=Ready "uiresource/(Tiltfile)" --timeout=$(WAIT_TIMEOUT)s
281
+ @$(TILT) wait --for=condition=Ready uiresource/generate-configs --timeout=$(WAIT_TIMEOUT)s
271
282
 
272
283
  # @echo '$(MAKE_CMD) create-dev-shell' >> $(DEVTOOLS_DIR)/start.sh
273
284
  # @echo 'rm -f /tmp/metaflow-devshell-*' >> $(DEVTOOLS_DIR)/start.sh
@@ -14,6 +14,17 @@
14
14
  version_settings(constraint='>=0.22.2')
15
15
  allow_k8s_contexts('minikube')
16
16
 
17
+ # Version configuration for components
18
+ JOBSET_VERSION = os.getenv("JOBSET_VERSION", "v0.8.2")
19
+
20
+ # Argo Workflows versions
21
+ ARGO_WORKFLOWS_HELM_CHART_VERSION = os.getenv("ARGO_WORKFLOWS_HELM_CHART_VERSION", "0.45.2") # Helm chart version
22
+ ARGO_WORKFLOWS_IMAGE_TAG = os.getenv("ARGO_WORKFLOWS_IMAGE_TAG", "v3.6.0") # Argo Workflows application version
23
+
24
+ # Argo Events versions
25
+ ARGO_EVENTS_HELM_CHART_VERSION = os.getenv("ARGO_EVENTS_HELM_CHART_VERSION", "2.4.8") # Helm chart version
26
+ ARGO_EVENTS_IMAGE_TAG = os.getenv("ARGO_EVENTS_IMAGE_TAG", "v1.9.2") # Argo Events application version
27
+
17
28
  components = {
18
29
  "metadata-service": ["postgresql"],
19
30
  "ui": ["postgresql", "minio"],
@@ -21,9 +32,10 @@ components = {
21
32
  "postgresql": [],
22
33
  "argo-workflows": [],
23
34
  "argo-events": ["argo-workflows"],
35
+ "jobset": [],
24
36
  }
25
37
 
26
- services_env = os.getenv("SERVICES", "").strip().lower()
38
+ services_env = os.getenv("SERVICES", "all").strip().lower()
27
39
 
28
40
  if services_env:
29
41
  if services_env == "all":
@@ -172,6 +184,7 @@ if "postgresql" in enabled_components:
172
184
  'auth.username=metaflow',
173
185
  'auth.password=metaflow123',
174
186
  'auth.database=metaflow',
187
+ 'image.repository=bitnamilegacy/postgresql',
175
188
  'primary.persistence.enabled=false',
176
189
  'primary.resources.requests.memory=128Mi',
177
190
  'primary.resources.requests.cpu=50m',
@@ -205,6 +218,7 @@ if "postgresql" in enabled_components:
205
218
  if "argo-workflows" in enabled_components:
206
219
  helm_remote(
207
220
  'argo-workflows',
221
+ version=ARGO_WORKFLOWS_HELM_CHART_VERSION,
208
222
  repo_name='argo',
209
223
  repo_url='https://argoproj.github.io/argo-helm',
210
224
  set=[
@@ -220,10 +234,25 @@ if "argo-workflows" in enabled_components:
220
234
  'controller.resources.requests.memory=128Mi',
221
235
  'controller.resources.requests.cpu=50m',
222
236
  'controller.resources.limits.memory=256Mi',
223
- 'controller.resources.limits.cpu=100m'
237
+ 'controller.resources.limits.cpu=100m',
238
+ # Image version overrides
239
+ 'images.tag=%s' % ARGO_WORKFLOWS_IMAGE_TAG,
224
240
  ]
225
241
  )
226
242
 
243
+ # This fixes issue described in: https://github.com/argoproj/argo-workflows/issues/10340
244
+ k8s_yaml(encode_yaml({
245
+ 'apiVersion': 'v1',
246
+ 'kind': 'Secret',
247
+ 'metadata': {
248
+ 'name': 'default.service-account-token',
249
+ 'annotations': {
250
+ 'kubernetes.io/service-account.name': 'default'
251
+ }
252
+ },
253
+ 'type': 'kubernetes.io/service-account-token'
254
+ }))
255
+
227
256
  k8s_yaml(encode_yaml({
228
257
  'apiVersion': 'rbac.authorization.k8s.io/v1',
229
258
  'kind': 'Role',
@@ -231,11 +260,23 @@ if "argo-workflows" in enabled_components:
231
260
  'name': 'argo-workflowtaskresults-role',
232
261
  'namespace': 'default'
233
262
  },
234
- 'rules': [{
263
+ 'rules': [
264
+ {
235
265
  'apiGroups': ['argoproj.io'],
236
266
  'resources': ['workflowtaskresults'],
237
267
  'verbs': ['create', 'patch', 'get', 'list']
238
- }]
268
+ },
269
+ {
270
+ 'apiGroups': ['argoproj.io'],
271
+ 'resources': ['workflowtasksets'],
272
+ 'verbs': ['watch', 'list']
273
+ },
274
+ {
275
+ 'apiGroups': ['argoproj.io'],
276
+ 'resources': ['workflowtasksets/status'],
277
+ 'verbs': ['patch']
278
+ },
279
+ ]
239
280
  }))
240
281
 
241
282
  k8s_yaml(encode_yaml({
@@ -282,6 +323,7 @@ if "argo-workflows" in enabled_components:
282
323
  if "argo-events" in enabled_components:
283
324
  helm_remote(
284
325
  'argo-events',
326
+ version=ARGO_EVENTS_HELM_CHART_VERSION,
285
327
  repo_name='argo',
286
328
  repo_url='https://argoproj.github.io/argo-helm',
287
329
  set=[
@@ -309,6 +351,8 @@ if "argo-events" in enabled_components:
309
351
  'configs.jetstream.versions[1].natsImage=nats:2.9.15',
310
352
  'configs.jetstream.versions[1].startCommand=/nats-server',
311
353
  'configs.jetstream.versions[1].version=2.9.15',
354
+ # Image version overrides
355
+ 'global.image.tag=%s' % ARGO_EVENTS_IMAGE_TAG,
312
356
  ]
313
357
  )
314
358
 
@@ -516,6 +560,62 @@ if "argo-events" in enabled_components:
516
560
  config_resources.append('argo-events-controller-manager')
517
561
  config_resources.append('argo-events-webhook-eventsource-svc')
518
562
 
563
+ #################################################
564
+ # JOBSET
565
+ #################################################
566
+ if "jobset" in enabled_components:
567
+ # Apply JobSet manifests directly from GitHub releases
568
+ jobset_manifest_url = "https://github.com/kubernetes-sigs/jobset/releases/download/%s/manifests.yaml" % JOBSET_VERSION
569
+
570
+ cmd = "curl -sSL %s" % (jobset_manifest_url)
571
+ k8s_yaml(
572
+ local(
573
+ cmd,
574
+ )
575
+ )
576
+
577
+ k8s_resource(
578
+ 'jobset-controller-manager',
579
+ labels=['jobset'],
580
+ )
581
+
582
+ metaflow_config["METAFLOW_KUBERNETES_JOBSET_ENABLED"] = "true"
583
+
584
+ config_resources.append('jobset-controller-manager')
585
+
586
+ # ClusterRole for jobset operations
587
+ k8s_yaml(encode_yaml({
588
+ 'apiVersion': 'rbac.authorization.k8s.io/v1',
589
+ 'kind': 'ClusterRole',
590
+ 'metadata': {
591
+ 'name': 'jobset-full-access'
592
+ },
593
+ 'rules': [{
594
+ 'apiGroups': ['jobset.x-k8s.io'],
595
+ 'resources': ['jobsets'],
596
+ 'verbs': ['*']
597
+ }]
598
+ }))
599
+
600
+ # ClusterRoleBinding for default service account to access jobsets
601
+ k8s_yaml(encode_yaml({
602
+ 'apiVersion': 'rbac.authorization.k8s.io/v1',
603
+ 'kind': 'ClusterRoleBinding',
604
+ 'metadata': {
605
+ 'name': 'default-jobset-binding'
606
+ },
607
+ 'subjects': [{
608
+ 'kind': 'ServiceAccount',
609
+ 'name': 'default',
610
+ 'namespace': 'default'
611
+ }],
612
+ 'roleRef': {
613
+ 'kind': 'ClusterRole',
614
+ 'name': 'jobset-full-access',
615
+ 'apiGroup': 'rbac.authorization.k8s.io'
616
+ }
617
+ }))
618
+
519
619
  #################################################
520
620
  # METADATA SERVICE
521
621
  #################################################
@@ -530,7 +630,7 @@ if "metadata-service" in enabled_components:
530
630
  'metadatadb.database=metaflow',
531
631
  'metadatadb.host=postgresql',
532
632
  'image.repository=public.ecr.aws/outerbounds/metaflow_metadata_service',
533
- 'image.tag=2.4.13-2-g70af4ed',
633
+ 'image.tag=2.5.0',
534
634
  'resources.requests.cpu=25m',
535
635
  'resources.requests.memory=64Mi',
536
636
  'resources.limits.cpu=50m',
@@ -568,7 +668,7 @@ if "ui" in enabled_components:
568
668
  'uiBackend.metaflowDatastoreSysRootS3=s3://metaflow-test',
569
669
  'uiBackend.metaflowS3EndpointURL=http://minio.default.svc.cluster.local:9000',
570
670
  'uiBackend.image.name=public.ecr.aws/outerbounds/metaflow_metadata_service',
571
- 'uiBackend.image.tag=2.4.13-2-g70af4ed',
671
+ 'uiBackend.image.tag=2.5.0',
572
672
  'uiBackend.env[0].name=AWS_ACCESS_KEY_ID',
573
673
  'uiBackend.env[0].value=rootuser',
574
674
  'uiBackend.env[1].name=AWS_SECRET_ACCESS_KEY',
@@ -578,7 +678,7 @@ if "ui" in enabled_components:
578
678
  'uiBackend.resources.requests.memory=256Mi',
579
679
  'uiStatic.metaflowUIBackendURL=http://localhost:8083/api',
580
680
  'uiStatic.image.name=public.ecr.aws/outerbounds/metaflow_ui',
581
- 'uiStatic.image.tag=v1.3.13-5-g5dd049e',
681
+ 'uiStatic.image.tag=v1.3.14',
582
682
  'uiStatic.resources.requests.cpu=25m',
583
683
  'uiStatic.resources.requests.memory=64Mi',
584
684
  'uiStatic.resources.limits.cpu=50m',
@@ -18,6 +18,7 @@ SERVICE_OPTIONS=(
18
18
  "ui"
19
19
  "argo-workflows"
20
20
  "argo-events"
21
+ "jobset"
21
22
  )
22
23
 
23
24
  gum style "$LOGO" \
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ob-metaflow
3
- Version: 2.15.13.1
3
+ Version: 2.19.7.1rc0
4
4
  Summary: Metaflow: More AI and ML, Less Engineering
5
5
  Author: Netflix, Outerbounds & the Metaflow Community
6
6
  Author-email: help@outerbounds.co
@@ -12,7 +12,7 @@ Requires-Dist: boto3
12
12
  Requires-Dist: pylint
13
13
  Requires-Dist: kubernetes
14
14
  Provides-Extra: stubs
15
- Requires-Dist: metaflow-stubs==2.15.13.1; extra == "stubs"
15
+ Requires-Dist: metaflow-stubs==2.19.7.1rc0; extra == "stubs"
16
16
  Dynamic: author
17
17
  Dynamic: author-email
18
18
  Dynamic: description
@@ -85,4 +85,3 @@ We'd love to hear from you. Join our community [Slack workspace](http://slack.ou
85
85
 
86
86
  ## Contributing
87
87
  We welcome contributions to Metaflow. Please see our [contribution guide](https://docs.metaflow.org/introduction/contributing-to-metaflow) for more details.
88
-