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
@@ -43,10 +43,10 @@ from metaflow._vendor.click.types import (
43
43
  )
44
44
  from metaflow.decorators import add_decorator_options
45
45
  from metaflow.exception import MetaflowException
46
+ from metaflow.flowspec import FlowStateItems
46
47
  from metaflow.includefile import FilePathClass
47
48
  from metaflow.metaflow_config import CLICK_API_PROCESS_CONFIG
48
49
  from metaflow.parameters import JSONTypeClass, flow_context
49
- from metaflow.user_configs.config_decorators import CustomFlowDecorator
50
50
  from metaflow.user_configs.config_options import (
51
51
  ConfigValue,
52
52
  ConvertDictOrStr,
@@ -55,6 +55,7 @@ from metaflow.user_configs.config_options import (
55
55
  MultipleTuple,
56
56
  config_options_with_config_input,
57
57
  )
58
+ from metaflow.user_decorators.user_flow_decorator import FlowMutator
58
59
 
59
60
  # Define a recursive type alias for JSON
60
61
  JSON = Union[Dict[str, "JSON"], List["JSON"], str, int, float, bool, None]
@@ -165,13 +166,23 @@ def _method_sanity_check(
165
166
  return method_params
166
167
 
167
168
 
169
+ def _cleanup_flow_parameters(cmd_obj: Union[click.Command, click.Group]):
170
+ if hasattr(cmd_obj, "original_params"):
171
+ cmd_obj.params = list(cmd_obj.original_params)
172
+
173
+ if isinstance(cmd_obj, click.Group):
174
+ for sub_cmd_name in cmd_obj.list_commands(None):
175
+ sub_cmd = cmd_obj.get_command(None, sub_cmd_name)
176
+ if sub_cmd:
177
+ _cleanup_flow_parameters(sub_cmd)
178
+
179
+
168
180
  def _lazy_load_command(
169
181
  cli_collection: click.Group,
170
182
  flow_parameters: Union[str, List[Parameter]],
171
183
  _self,
172
184
  name: str,
173
185
  ):
174
-
175
186
  # Context is not used in get_command so we can pass None. Since we pin click,
176
187
  # this won't change from under us.
177
188
 
@@ -181,6 +192,7 @@ def _lazy_load_command(
181
192
  flow_parameters = getattr(_self, flow_parameters)()
182
193
  cmd_obj = cli_collection.get_command(None, name)
183
194
  if cmd_obj:
195
+ _cleanup_flow_parameters(cmd_obj)
184
196
  if isinstance(cmd_obj, click.Group):
185
197
  # TODO: possibly check for fake groups with cmd_obj.name in ["cli", "main"]
186
198
  result = functools.partial(extract_group(cmd_obj, flow_parameters), _self)
@@ -264,12 +276,12 @@ def extract_flow_class_from_file(flow_file: str) -> FlowSpec:
264
276
  loaded_modules[flow_file] = module
265
277
 
266
278
  classes = inspect.getmembers(
267
- module, lambda x: inspect.isclass(x) or isinstance(x, CustomFlowDecorator)
279
+ module, lambda x: inspect.isclass(x) or isinstance(x, FlowMutator)
268
280
  )
269
281
  flow_cls = None
270
282
 
271
283
  for _, kls in classes:
272
- if isinstance(kls, CustomFlowDecorator):
284
+ if isinstance(kls, FlowMutator):
273
285
  kls = kls._flow_cls
274
286
  if (
275
287
  kls is not FlowSpec
@@ -339,8 +351,10 @@ class MetaflowAPI(object):
339
351
  class_dict = {
340
352
  "__module__": "metaflow",
341
353
  "_API_NAME": flow_file,
342
- "_internal_getattr": functools.partial(
343
- _lazy_load_command, cli_collection, "_compute_flow_parameters"
354
+ "_internal_getattr": staticmethod(
355
+ functools.partial(
356
+ _lazy_load_command, cli_collection, "_compute_flow_parameters"
357
+ )
344
358
  ),
345
359
  "__getattr__": getattr_wrapper,
346
360
  }
@@ -460,31 +474,62 @@ class MetaflowAPI(object):
460
474
 
461
475
  ds = opts.get("datastore", defaults["datastore"])
462
476
  quiet = opts.get("quiet", defaults["quiet"])
477
+
478
+ # Order to find config or config_value:
479
+ # 1. Passed directly to the Click API
480
+ # 2. If not found, check if passed through an environment variable
481
+ # 3. If not found, use the default value
463
482
  is_default = False
464
483
  config_file = opts.get("config")
465
484
  if config_file is None:
466
- is_default = True
467
- config_file = defaults.get("config")
485
+ # Check if it was set through an environment variable -- we
486
+ # don't have click process them here so we need to "fake" it.
487
+ env_config_file = os.environ.get("METAFLOW_FLOW_CONFIG")
488
+ if env_config_file:
489
+ # Convert dict items to list of tuples
490
+ config_file = list(json.loads(env_config_file).items())
491
+ is_default = False
492
+ else:
493
+ is_default = True
494
+ config_file = defaults.get("config")
468
495
 
469
496
  if config_file:
470
- config_file = map(
471
- lambda x: (x[0], ConvertPath.convert_value(x[1], is_default)),
472
- config_file,
497
+ config_file = dict(
498
+ map(
499
+ lambda x: (
500
+ x[0],
501
+ ConvertPath.convert_value(x[1], is_default),
502
+ ),
503
+ config_file,
504
+ )
473
505
  )
474
506
 
475
507
  is_default = False
476
508
  config_value = opts.get("config-value")
477
509
  if config_value is None:
478
- is_default = True
479
- config_value = defaults.get("config_value")
510
+ env_config_value = os.environ.get("METAFLOW_FLOW_CONFIG_VALUE")
511
+ if env_config_value:
512
+ # Parse environment variable using MultipleTuple logic
513
+ loaded = json.loads(env_config_value)
514
+ # Convert dict items to list of tuples with JSON-serialized values
515
+ config_value = [
516
+ (k, json.dumps(v) if not isinstance(v, str) else v)
517
+ for k, v in loaded.items()
518
+ ]
519
+ is_default = False
520
+ else:
521
+ is_default = True
522
+ config_value = defaults.get("config_value")
480
523
 
481
524
  if config_value:
482
- config_value = map(
483
- lambda x: (
484
- x[0],
485
- ConvertDictOrStr.convert_value(x[1], is_default),
486
- ),
487
- config_value,
525
+ config_value = dict(
526
+ map(
527
+ lambda x: (
528
+ x[0],
529
+ ConvertDictOrStr.convert_value(x[1], is_default),
530
+ ),
531
+ config_value,
532
+ )
488
533
  )
489
534
 
490
535
  if (config_file is None) ^ (config_value is None):
@@ -505,10 +550,15 @@ class MetaflowAPI(object):
505
550
 
506
551
  # At this point, we are like in start() in cli.py -- we obtained the
507
552
  # properly processed config_options which we can now use to process
508
- # the config decorators (including CustomStep/FlowDecorators)
553
+ # the config decorators (including StepMutator/FlowMutator)
509
554
  # Note that if CLICK_API_PROCESS_CONFIG is False, we still do this because
510
555
  # it will init all parameters (config_options will be None)
511
556
  # We ignore any errors if we don't check the configs in the click API.
557
+
558
+ # Init all values in the flow mutators and then process them
559
+ for decorator in self._flow_cls._flow_state[FlowStateItems.FLOW_MUTATORS]:
560
+ decorator.external_init()
561
+
512
562
  new_cls = self._flow_cls._process_config_decorators(
513
563
  config_options, process_configs=CLICK_API_PROCESS_CONFIG
514
564
  )
@@ -534,14 +584,16 @@ def extract_all_params(cmd_obj: Union[click.Command, click.Group]):
534
584
 
535
585
  for each_param in cmd_obj.params:
536
586
  if isinstance(each_param, click.Argument):
537
- arg_params_sigs[each_param.name], annotations[each_param.name] = (
538
- get_inspect_param_obj(each_param, inspect.Parameter.POSITIONAL_ONLY)
539
- )
587
+ (
588
+ arg_params_sigs[each_param.name],
589
+ annotations[each_param.name],
590
+ ) = get_inspect_param_obj(each_param, inspect.Parameter.POSITIONAL_ONLY)
540
591
  arg_parameters[each_param.name] = each_param
541
592
  elif isinstance(each_param, click.Option):
542
- opt_params_sigs[each_param.name], annotations[each_param.name] = (
543
- get_inspect_param_obj(each_param, inspect.Parameter.KEYWORD_ONLY)
544
- )
593
+ (
594
+ opt_params_sigs[each_param.name],
595
+ annotations[each_param.name],
596
+ ) = get_inspect_param_obj(each_param, inspect.Parameter.KEYWORD_ONLY)
545
597
  opt_parameters[each_param.name] = each_param
546
598
 
547
599
  defaults[each_param.name] = each_param.default
@@ -651,6 +703,7 @@ if __name__ == "__main__":
651
703
  .kubernetes()
652
704
  .step(
653
705
  step_name="process",
706
+ code_package_metadata="some_version",
654
707
  code_package_sha="some_sha",
655
708
  code_package_url="some_url",
656
709
  )
@@ -7,6 +7,57 @@ 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.get(
17
+ "python_var_name", param_details["name"]
18
+ )
19
+ param_name = param_details["name"]
20
+ param_type = param_details["type"]
21
+ param_help = param_details["description"]
22
+ param_required = param_details["is_required"]
23
+
24
+ if param_type == "JSON":
25
+ params_code += (
26
+ f" {param_python_var_name} = Parameter('{param_name}', "
27
+ f"type=JSONType, help='''{param_help}''', required={param_required})\n"
28
+ )
29
+ elif param_type == "FilePath":
30
+ is_text = param_details.get("is_text", True)
31
+ encoding = param_details.get("encoding", "utf-8")
32
+ params_code += (
33
+ f" {param_python_var_name} = IncludeFile('{param_name}', "
34
+ f"is_text={is_text}, encoding='{encoding}', help='''{param_help}''', "
35
+ f"required={param_required})\n"
36
+ )
37
+ else:
38
+ params_code += (
39
+ f" {param_python_var_name} = Parameter('{param_name}', "
40
+ f"type={param_type}, help='''{param_help}''', required={param_required})\n"
41
+ )
42
+
43
+ project_decorator = f"@project(name='{project_name}')\n" if project_name else ""
44
+
45
+ contents = f"""\
46
+ from metaflow import FlowSpec, Parameter, IncludeFile, JSONType, step, project
47
+ {project_decorator}class {flow_name}(FlowSpec):
48
+ {params_code}
49
+ @step
50
+ def start(self):
51
+ self.next(self.end)
52
+ @step
53
+ def end(self):
54
+ pass
55
+ if __name__ == '__main__':
56
+ {flow_name}()
57
+ """
58
+ return contents
59
+
60
+
10
61
  if TYPE_CHECKING:
11
62
  import metaflow
12
63
  import metaflow.runner.deployer_impl
@@ -180,7 +231,68 @@ class DeployedFlowMeta(type):
180
231
  }
181
232
  )
182
233
 
183
- def _default_injected_method():
234
+ def _get_triggered_run_injected_method():
235
+ def f(
236
+ cls,
237
+ identifier: str,
238
+ run_id: str,
239
+ metadata: Optional[str] = None,
240
+ impl: str = DEFAULT_FROM_DEPLOYMENT_IMPL.replace("-", "_"),
241
+ ) -> "TriggeredRun":
242
+ """
243
+ Retrieves a `TriggeredRun` object from an identifier, a run id and optional
244
+ metadata. The `impl` parameter specifies the deployer implementation
245
+ to use (like `argo-workflows`).
246
+
247
+ Parameters
248
+ ----------
249
+ identifier : str
250
+ Deployer specific identifier for the workflow to retrieve
251
+ run_id : str
252
+ Run ID for the which to fetch the triggered run object
253
+ metadata : str, optional, default None
254
+ Optional deployer specific metadata.
255
+ impl : str, optional, default given by METAFLOW_DEFAULT_FROM_DEPLOYMENT_IMPL
256
+ The default implementation to use if not specified
257
+
258
+ Returns
259
+ -------
260
+ TriggeredRun
261
+ A `TriggeredRun` object representing the triggered run corresponding
262
+ to the identifier and the run id.
263
+ """
264
+ if impl in allowed_providers:
265
+ return (
266
+ allowed_providers[impl]
267
+ .deployed_flow_type()
268
+ .get_triggered_run(identifier, run_id, metadata)
269
+ )
270
+ else:
271
+ raise ValueError(
272
+ f"No deployer '{impl}' exists; valid deployers are: "
273
+ f"{list(allowed_providers.keys())}"
274
+ )
275
+
276
+ f.__name__ = "get_triggered_run"
277
+ return f
278
+
279
+ def _per_type_get_triggered_run_injected_method(method_name, impl):
280
+ def f(
281
+ cls,
282
+ identifier: str,
283
+ run_id: str,
284
+ metadata: Optional[str] = None,
285
+ ):
286
+ return (
287
+ allowed_providers[impl]
288
+ .deployed_flow_type()
289
+ .get_triggered_run(identifier, run_id, metadata)
290
+ )
291
+
292
+ f.__name__ = method_name
293
+ return f
294
+
295
+ def _from_deployment_injected_method():
184
296
  def f(
185
297
  cls,
186
298
  identifier: str,
@@ -222,7 +334,7 @@ class DeployedFlowMeta(type):
222
334
  f.__name__ = "from_deployment"
223
335
  return f
224
336
 
225
- def _per_type_injected_method(method_name, impl):
337
+ def _per_type_from_deployment_injected_method(method_name, impl):
226
338
  def f(
227
339
  cls,
228
340
  identifier: str,
@@ -237,14 +349,104 @@ class DeployedFlowMeta(type):
237
349
  f.__name__ = method_name
238
350
  return f
239
351
 
240
- setattr(cls, "from_deployment", classmethod(_default_injected_method()))
352
+ def _list_deployed_flows_injected_method():
353
+ def f(
354
+ cls,
355
+ flow_name: Optional[str] = None,
356
+ impl: str = DEFAULT_FROM_DEPLOYMENT_IMPL.replace("-", "_"),
357
+ ):
358
+ """
359
+ List all deployed flows for the specified implementation.
360
+
361
+ Parameters
362
+ ----------
363
+ flow_name : str, optional, default None
364
+ If specified, only list deployed flows for this specific flow name.
365
+ If None, list all deployed flows.
366
+ impl : str, optional, default given by METAFLOW_DEFAULT_FROM_DEPLOYMENT_IMPL
367
+ The default implementation to use if not specified
368
+
369
+ Yields
370
+ ------
371
+ DeployedFlow
372
+ `DeployedFlow` objects representing deployed flows.
373
+ """
374
+ if impl in allowed_providers:
375
+ return (
376
+ allowed_providers[impl]
377
+ .deployed_flow_type()
378
+ .list_deployed_flows(flow_name)
379
+ )
380
+ else:
381
+ raise ValueError(
382
+ f"No deployer '{impl}' exists; valid deployers are: "
383
+ f"{list(allowed_providers.keys())}"
384
+ )
385
+
386
+ f.__name__ = "list_deployed_flows"
387
+ return f
388
+
389
+ def _per_type_list_deployed_flows_injected_method(method_name, impl):
390
+ def f(
391
+ cls,
392
+ flow_name: Optional[str] = None,
393
+ ):
394
+ return (
395
+ allowed_providers[impl]
396
+ .deployed_flow_type()
397
+ .list_deployed_flows(flow_name)
398
+ )
399
+
400
+ f.__name__ = method_name
401
+ return f
402
+
403
+ setattr(
404
+ cls, "from_deployment", classmethod(_from_deployment_injected_method())
405
+ )
406
+ setattr(
407
+ cls,
408
+ "list_deployed_flows",
409
+ classmethod(_list_deployed_flows_injected_method()),
410
+ )
411
+ setattr(
412
+ cls,
413
+ "get_triggered_run",
414
+ classmethod(_get_triggered_run_injected_method()),
415
+ )
241
416
 
242
417
  for impl in allowed_providers:
243
- method_name = f"from_{impl}"
418
+ from_deployment_method_name = f"from_{impl}"
419
+ list_deployed_flows_method_name = f"list_{impl}"
420
+ get_triggered_run_method_name = f"get_triggered_{impl}_run"
421
+
422
+ setattr(
423
+ cls,
424
+ from_deployment_method_name,
425
+ classmethod(
426
+ _per_type_from_deployment_injected_method(
427
+ from_deployment_method_name, impl
428
+ )
429
+ ),
430
+ )
431
+
244
432
  setattr(
245
433
  cls,
246
- method_name,
247
- classmethod(_per_type_injected_method(method_name, impl)),
434
+ list_deployed_flows_method_name,
435
+ classmethod(
436
+ _per_type_list_deployed_flows_injected_method(
437
+ list_deployed_flows_method_name, impl
438
+ )
439
+ ),
440
+ )
441
+
442
+ setattr(
443
+ cls,
444
+ get_triggered_run_method_name,
445
+ classmethod(
446
+ _per_type_get_triggered_run_injected_method(
447
+ get_triggered_run_method_name, impl
448
+ )
449
+ ),
248
450
  )
249
451
 
250
452
  return cls
@@ -3,10 +3,12 @@ import json
3
3
  import os
4
4
  import sys
5
5
 
6
- from typing import Any, ClassVar, Dict, Optional, TYPE_CHECKING, Type
6
+ from typing import Any, ClassVar, Dict, Optional, TYPE_CHECKING, Type, List
7
+
8
+ from metaflow.metaflow_config import CLICK_API_PROCESS_CONFIG
7
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
@@ -65,15 +67,10 @@ class DeployerImpl(object):
65
67
 
66
68
  # Reload the CLI with an "empty" flow -- this will remove any configuration
67
69
  # and parameter options. They are re-added in from_cli (called below).
68
- to_reload = [
69
- "metaflow.cli",
70
- "metaflow.cli_components.run_cmds",
71
- "metaflow.cli_components.init_cmd",
72
- ]
73
70
  with flow_context(None):
74
71
  [
75
72
  importlib.reload(sys.modules[module])
76
- for module in to_reload
73
+ for module in self.to_reload
77
74
  if module in sys.modules
78
75
  ]
79
76
 
@@ -88,7 +85,7 @@ class DeployerImpl(object):
88
85
  self.show_output = show_output
89
86
  self.profile = profile
90
87
  self.env = env
91
- self.cwd = cwd
88
+ self.cwd = cwd or os.getcwd()
92
89
  self.file_read_timeout = file_read_timeout
93
90
 
94
91
  self.env_vars = os.environ.copy()
@@ -100,6 +97,19 @@ class DeployerImpl(object):
100
97
  self.top_level_kwargs = kwargs
101
98
  self.api = MetaflowAPI.from_cli(self.flow_file, start)
102
99
 
100
+ @property
101
+ def to_reload(self) -> List[str]:
102
+ """
103
+ List of modules to reload when the deployer is initialized.
104
+ This is used to ensure that the CLI is in a clean state before
105
+ deploying the flow.
106
+ """
107
+ return [
108
+ "metaflow.cli",
109
+ "metaflow.cli_components.run_cmds",
110
+ "metaflow.cli_components.init_cmd",
111
+ ]
112
+
103
113
  @property
104
114
  def deployer_kwargs(self) -> Dict[str, Any]:
105
115
  raise NotImplementedError
@@ -140,9 +150,19 @@ class DeployerImpl(object):
140
150
  ) -> "metaflow.runner.deployer.DeployedFlow":
141
151
  with temporary_fifo() as (attribute_file_path, attribute_file_fd):
142
152
  # every subclass needs to have `self.deployer_kwargs`
143
- command = get_lower_level_group(
144
- self.api, self.top_level_kwargs, self.TYPE, self.deployer_kwargs
145
- ).create(deployer_attribute_file=attribute_file_path, **kwargs)
153
+ # TODO: Get rid of CLICK_API_PROCESS_CONFIG in the near future
154
+ if CLICK_API_PROCESS_CONFIG:
155
+ # We need to run this in the cwd because configs depend on files
156
+ # that may be located in paths relative to the directory the user
157
+ # wants to run in
158
+ with with_dir(self.cwd):
159
+ command = get_lower_level_group(
160
+ self.api, self.top_level_kwargs, self.TYPE, self.deployer_kwargs
161
+ ).create(deployer_attribute_file=attribute_file_path, **kwargs)
162
+ else:
163
+ command = get_lower_level_group(
164
+ self.api, self.top_level_kwargs, self.TYPE, self.deployer_kwargs
165
+ ).create(deployer_attribute_file=attribute_file_path, **kwargs)
146
166
 
147
167
  pid = self.spm.run_command(
148
168
  [sys.executable, *command],