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
@@ -8,19 +8,26 @@ from .. import decorators, namespace, parameters, tracing
8
8
  from ..exception import CommandException
9
9
  from ..graph import FlowGraph
10
10
  from ..metaflow_current import current
11
- from ..metaflow_config import DEFAULT_DECOSPECS
11
+ from ..metaflow_config import (
12
+ DEFAULT_DECOSPECS,
13
+ FEAT_ALWAYS_UPLOAD_CODE_PACKAGE,
14
+ SPIN_PERSIST,
15
+ )
16
+ from ..metaflow_profile import from_start
12
17
  from ..package import MetaflowPackage
13
- from ..runtime import NativeRuntime
18
+ from ..runtime import NativeRuntime, SpinRuntime
14
19
  from ..system import _system_logger
15
20
 
21
+ # from ..client.core import Run
22
+
16
23
  from ..tagging_util import validate_tags
17
- from ..util import get_latest_run_id, write_latest_run_id
24
+ from ..util import get_latest_run_id, write_latest_run_id, parse_spin_pathspec
18
25
 
19
26
 
20
- def before_run(obj, tags, decospecs):
27
+ def before_run(obj, tags, decospecs, skip_decorators=False):
21
28
  validate_tags(tags)
22
29
 
23
- # There's a --with option both at the top-level and for the run
30
+ # There's a --with option both at the top-level and for the run/resume/spin
24
31
  # subcommand. Why?
25
32
  #
26
33
  # "run --with shoes" looks so much better than "--with shoes run".
@@ -34,26 +41,38 @@ def before_run(obj, tags, decospecs):
34
41
  # - run level decospecs
35
42
  # - top level decospecs
36
43
  # - environment decospecs
37
- all_decospecs = (
38
- list(decospecs or [])
39
- + obj.tl_decospecs
40
- + list(obj.environment.decospecs() or [])
41
- )
42
- if all_decospecs:
43
- # These decospecs are the ones from run/resume PLUS the ones from the
44
- # environment (for example the @conda)
45
- decorators._attach_decorators(obj.flow, all_decospecs)
46
- decorators._init(obj.flow)
47
- # Regenerate graph if we attached more decorators
48
- obj.flow.__class__._init_attrs()
49
- obj.graph = obj.flow._graph
50
-
51
- obj.check(obj.graph, obj.flow, obj.environment, pylint=obj.pylint)
52
- # obj.environment.init_environment(obj.logger)
53
-
54
- decorators._init_step_decorators(
55
- obj.flow, obj.graph, obj.environment, obj.flow_datastore, obj.logger
44
+ from_start(
45
+ f"Inside before_run, skip_decorators={skip_decorators}, is_spin={obj.is_spin}"
56
46
  )
47
+ if not skip_decorators:
48
+ all_decospecs = (
49
+ list(decospecs or [])
50
+ + obj.tl_decospecs
51
+ + list(obj.environment.decospecs() or [])
52
+ )
53
+ if all_decospecs:
54
+ # These decospecs are the ones from run/resume/spin PLUS the ones from the
55
+ # environment (for example the @conda)
56
+ decorators._attach_decorators(obj.flow, all_decospecs)
57
+ decorators._init(obj.flow)
58
+ # Regenerate graph if we attached more decorators
59
+ obj.flow.__class__._init_graph()
60
+ obj.graph = obj.flow._graph
61
+
62
+ obj.check(obj.graph, obj.flow, obj.environment, pylint=obj.pylint)
63
+ # obj.environment.init_environment(obj.logger)
64
+
65
+ decorators._init_step_decorators(
66
+ obj.flow,
67
+ obj.graph,
68
+ obj.environment,
69
+ obj.flow_datastore,
70
+ obj.logger,
71
+ obj.is_spin,
72
+ skip_decorators,
73
+ )
74
+ # Re-read graph since it may have been modified by mutators
75
+ obj.graph = obj.flow._graph
57
76
 
58
77
  obj.metadata.add_sticky_tags(tags=tags)
59
78
 
@@ -61,10 +80,37 @@ def before_run(obj, tags, decospecs):
61
80
  # We explicitly avoid doing this in `start` since it is invoked for every
62
81
  # step in the run.
63
82
  obj.package = MetaflowPackage(
64
- obj.flow, obj.environment, obj.echo, obj.package_suffixes
83
+ obj.flow,
84
+ obj.environment,
85
+ obj.echo,
86
+ suffixes=obj.package_suffixes,
87
+ flow_datastore=obj.flow_datastore if FEAT_ALWAYS_UPLOAD_CODE_PACKAGE else None,
65
88
  )
66
89
 
67
90
 
91
+ def common_runner_options(func):
92
+ @click.option(
93
+ "--run-id-file",
94
+ default=None,
95
+ show_default=True,
96
+ type=str,
97
+ help="Write the ID of this run to the file specified.",
98
+ )
99
+ @click.option(
100
+ "--runner-attribute-file",
101
+ default=None,
102
+ show_default=True,
103
+ type=str,
104
+ help="Write the metadata and pathspec of this run to the file specified. Used internally "
105
+ "for Metaflow's Runner API.",
106
+ )
107
+ @wraps(func)
108
+ def wrapper(*args, **kwargs):
109
+ return func(*args, **kwargs)
110
+
111
+ return wrapper
112
+
113
+
68
114
  def write_file(file_path, content):
69
115
  if file_path is not None:
70
116
  with open(file_path, "w", encoding="utf-8") as f:
@@ -129,20 +175,6 @@ def common_run_options(func):
129
175
  "in steps.",
130
176
  callback=config_callback,
131
177
  )
132
- @click.option(
133
- "--run-id-file",
134
- default=None,
135
- show_default=True,
136
- type=str,
137
- help="Write the ID of this run to the file specified.",
138
- )
139
- @click.option(
140
- "--runner-attribute-file",
141
- default=None,
142
- show_default=True,
143
- type=str,
144
- help="Write the metadata and pathspec of this run to the file specified. Used internally for Metaflow's Runner API.",
145
- )
146
178
  @wraps(func)
147
179
  def wrapper(*args, **kwargs):
148
180
  return func(*args, **kwargs)
@@ -187,6 +219,7 @@ def common_run_options(func):
187
219
  @click.command(help="Resume execution of a previous run of this flow.")
188
220
  @tracing.cli("cli/resume")
189
221
  @common_run_options
222
+ @common_runner_options
190
223
  @click.pass_obj
191
224
  def resume(
192
225
  obj,
@@ -224,6 +257,19 @@ def resume(
224
257
  step_to_rerun, ",".join(list(obj.graph.nodes.keys()))
225
258
  )
226
259
  )
260
+
261
+ ## TODO: instead of checking execution path here, can add a warning later
262
+ ## instead of throwing an error. This is for resuming a step which was not
263
+ ## taken inside a branch i.e. not present in the execution path.
264
+
265
+ # origin_run = Run(f"{obj.flow.name}/{origin_run_id}", _namespace_check=False)
266
+ # executed_steps = {step.path_components[-1] for step in origin_run}
267
+ # if step_to_rerun not in executed_steps:
268
+ # raise CommandException(
269
+ # f"Cannot resume from step '{step_to_rerun}'. This step was not "
270
+ # f"part of the original execution path for run '{origin_run_id}'."
271
+ # )
272
+
227
273
  steps_to_rerun = {step_to_rerun}
228
274
 
229
275
  if run_id:
@@ -305,6 +351,7 @@ def resume(
305
351
  @click.command(help="Run the workflow locally.")
306
352
  @tracing.cli("cli/run")
307
353
  @common_run_options
354
+ @common_runner_options
308
355
  @click.option(
309
356
  "--namespace",
310
357
  "user_namespace",
@@ -327,7 +374,7 @@ def run(
327
374
  run_id_file=None,
328
375
  runner_attribute_file=None,
329
376
  user_namespace=None,
330
- **kwargs
377
+ **kwargs,
331
378
  ):
332
379
  if user_namespace is not None:
333
380
  namespace(user_namespace or None)
@@ -380,3 +427,120 @@ def run(
380
427
  )
381
428
  with runtime.run_heartbeat():
382
429
  runtime.execute()
430
+
431
+
432
+ # @parameters.add_custom_parameters(deploy_mode=True)
433
+ @click.command(help="Spins up a task for a given step from a previous run locally.")
434
+ @tracing.cli("cli/spin")
435
+ @click.argument("pathspec")
436
+ @click.option(
437
+ "--skip-decorators/--no-skip-decorators",
438
+ is_flag=True,
439
+ # Default False matches the saved_args check in cli.py for spin steps - skip_decorators
440
+ # only becomes True when explicitly passed, otherwise decorators are applied by default
441
+ default=False,
442
+ show_default=True,
443
+ help="Skip decorators attached to the step or flow.",
444
+ )
445
+ @click.option(
446
+ "--artifacts-module",
447
+ default=None,
448
+ show_default=True,
449
+ help="Path to a module that contains artifacts to be used in the spun step. "
450
+ "The artifacts should be defined as a dictionary called ARTIFACTS with keys as "
451
+ "the artifact names and values as the artifact values. The artifact values will "
452
+ "overwrite the default values of the artifacts used in the spun step.",
453
+ )
454
+ @click.option(
455
+ "--persist/--no-persist",
456
+ "persist",
457
+ default=SPIN_PERSIST,
458
+ show_default=True,
459
+ help="Whether to persist the artifacts in the spun step. If set to False, "
460
+ "the artifacts will not be persisted and will not be available in the spun step's "
461
+ "datastore.",
462
+ )
463
+ @click.option(
464
+ "--max-log-size",
465
+ default=10,
466
+ show_default=True,
467
+ help="Maximum size of stdout and stderr captured in "
468
+ "megabytes. If a step outputs more than this to "
469
+ "stdout/stderr, its output will be truncated.",
470
+ )
471
+ @common_runner_options
472
+ @click.pass_obj
473
+ def spin(
474
+ obj,
475
+ pathspec,
476
+ persist=True,
477
+ artifacts_module=None,
478
+ skip_decorators=False,
479
+ max_log_size=None,
480
+ run_id_file=None,
481
+ runner_attribute_file=None,
482
+ **kwargs,
483
+ ):
484
+ # Parse the pathspec argument to extract step name and full pathspec
485
+ step_name, parsed_pathspec = parse_spin_pathspec(pathspec, obj.flow.name)
486
+
487
+ before_run(obj, [], [], skip_decorators)
488
+ obj.echo(f"Spinning up step *{step_name}* locally for flow *{obj.flow.name}*")
489
+ # For spin, flow parameters come from the original run, but _set_constants
490
+ # requires them in kwargs. Use parameter defaults as placeholders - they'll be
491
+ # overwritten when the spin step loads artifacts from the original run.
492
+ flow_param_defaults = {}
493
+ for var, param in obj.flow._get_parameters():
494
+ if not param.IS_CONFIG_PARAMETER:
495
+ default_value = param.kwargs.get("default")
496
+ # Use None for required parameters without defaults
497
+ flow_param_defaults[param.name.replace("-", "_").lower()] = default_value
498
+ obj.flow._set_constants(obj.graph, flow_param_defaults, obj.config_options)
499
+ step_func = getattr(obj.flow, step_name, None)
500
+ if step_func is None:
501
+ raise CommandException(
502
+ f"Step '{step_name}' not found in flow '{obj.flow.name}'. "
503
+ "Please provide a valid step name."
504
+ )
505
+ from_start("Spin: before spin runtime init")
506
+ spin_runtime = SpinRuntime(
507
+ obj.flow,
508
+ obj.graph,
509
+ obj.flow_datastore,
510
+ obj.metadata,
511
+ obj.environment,
512
+ obj.package,
513
+ obj.logger,
514
+ obj.entrypoint,
515
+ obj.event_logger,
516
+ obj.monitor,
517
+ step_func,
518
+ step_name,
519
+ parsed_pathspec,
520
+ skip_decorators,
521
+ artifacts_module,
522
+ persist,
523
+ max_log_size * 1024 * 1024,
524
+ )
525
+ write_latest_run_id(obj, spin_runtime.run_id)
526
+ write_file(run_id_file, spin_runtime.run_id)
527
+ # We only need the root for the metadata, i.e. the portion before DATASTORE_LOCAL_DIR
528
+ datastore_root = spin_runtime._flow_datastore._storage_impl.datastore_root
529
+ orig_task_metadata_root = datastore_root.rsplit("/", 1)[0]
530
+ from_start("Spin: going to execute")
531
+ spin_runtime.execute()
532
+ from_start("Spin: after spin runtime execute")
533
+
534
+ if runner_attribute_file:
535
+ with open(runner_attribute_file, "w") as f:
536
+ json.dump(
537
+ {
538
+ "task_id": spin_runtime.task.task_id,
539
+ "step_name": step_name,
540
+ "run_id": spin_runtime.run_id,
541
+ "flow_name": obj.flow.name,
542
+ # Store metadata in a format that can be used by the Runner API
543
+ "metadata": f"{obj.metadata.__class__.TYPE}@{orig_task_metadata_root}",
544
+ },
545
+ f,
546
+ )
@@ -1,12 +1,17 @@
1
1
  from metaflow._vendor import click
2
2
 
3
- from .. import decorators, namespace
3
+ from .. import namespace
4
4
  from ..cli import echo_always, echo_dev_null
5
5
  from ..cli_args import cli_args
6
+ from ..datastore.flow_datastore import FlowDataStore
6
7
  from ..exception import CommandException
8
+ from ..client.filecache import FileCache, FileBlobCache, TaskMetadataCache
9
+ from ..metaflow_config import SPIN_ALLOWED_DECORATORS
10
+ from ..metaflow_profile import from_start
11
+ from ..plugins import DATASTORES
7
12
  from ..task import MetaflowTask
8
13
  from ..unbounded_foreach import UBF_CONTROL, UBF_TASK
9
- from ..util import decompress_list
14
+ from ..util import decompress_list, read_artifacts_module
10
15
  import metaflow.tracing as tracing
11
16
 
12
17
 
@@ -109,7 +114,6 @@ def step(
109
114
  ubf_context="none",
110
115
  num_parallel=None,
111
116
  ):
112
-
113
117
  if ctx.obj.is_quiet:
114
118
  echo = echo_dev_null
115
119
  else:
@@ -118,7 +122,7 @@ def step(
118
122
  if ubf_context == "none":
119
123
  ubf_context = None
120
124
  if opt_namespace is not None:
121
- namespace(opt_namespace or None)
125
+ namespace(opt_namespace)
122
126
 
123
127
  func = None
124
128
  try:
@@ -176,3 +180,155 @@ def step(
176
180
  )
177
181
 
178
182
  echo("Success", fg="green", bold=True, indent=True)
183
+
184
+
185
+ @click.command(help="Internal command to spin a single task.", hidden=True)
186
+ @click.argument("step-name")
187
+ @click.option(
188
+ "--run-id",
189
+ default=None,
190
+ required=True,
191
+ help="Original run ID for the step that will be spun",
192
+ )
193
+ @click.option(
194
+ "--task-id",
195
+ default=None,
196
+ required=True,
197
+ help="Original Task ID for the step that will be spun",
198
+ )
199
+ @click.option(
200
+ "--orig-flow-datastore",
201
+ show_default=True,
202
+ help="Original datastore for the flow from which a task is being spun",
203
+ )
204
+ @click.option(
205
+ "--input-paths",
206
+ help="A comma-separated list of pathspecs specifying inputs for this step.",
207
+ )
208
+ @click.option(
209
+ "--split-index",
210
+ type=int,
211
+ default=None,
212
+ show_default=True,
213
+ help="Index of this foreach split.",
214
+ )
215
+ @click.option(
216
+ "--retry-count",
217
+ default=0,
218
+ help="How many times we have attempted to run this task.",
219
+ )
220
+ @click.option(
221
+ "--max-user-code-retries",
222
+ default=0,
223
+ help="How many times we should attempt running the user code.",
224
+ )
225
+ @click.option(
226
+ "--namespace",
227
+ "opt_namespace",
228
+ default=None,
229
+ help="Change namespace from the default (your username) to the specified tag.",
230
+ )
231
+ @click.option(
232
+ "--skip-decorators/--no-skip-decorators",
233
+ is_flag=True,
234
+ default=False,
235
+ show_default=True,
236
+ help="Skip decorators attached to the step or flow.",
237
+ )
238
+ @click.option(
239
+ "--persist/--no-persist",
240
+ "persist",
241
+ default=True,
242
+ show_default=True,
243
+ help="Whether to persist the artifacts in the spun step. If set to false, the artifacts will not"
244
+ " be persisted and will not be available in the spun step's datastore.",
245
+ )
246
+ @click.option(
247
+ "--artifacts-module",
248
+ default=None,
249
+ show_default=True,
250
+ help="Path to a module that contains artifacts to be used in the spun step. The artifacts should "
251
+ "be defined as a dictionary called ARTIFACTS with keys as the artifact names and values as the "
252
+ "artifact values. The artifact values will overwrite the default values of the artifacts used in "
253
+ "the spun step.",
254
+ )
255
+ @click.pass_context
256
+ def spin_step(
257
+ ctx,
258
+ step_name,
259
+ orig_flow_datastore,
260
+ run_id=None,
261
+ task_id=None,
262
+ input_paths=None,
263
+ split_index=None,
264
+ retry_count=None,
265
+ max_user_code_retries=None,
266
+ opt_namespace=None,
267
+ skip_decorators=False,
268
+ artifacts_module=None,
269
+ persist=True,
270
+ ):
271
+ import time
272
+
273
+ if ctx.obj.is_quiet:
274
+ echo = echo_dev_null
275
+ else:
276
+ echo = echo_always
277
+
278
+ if opt_namespace is not None:
279
+ namespace(opt_namespace)
280
+
281
+ input_paths = decompress_list(input_paths) if input_paths else []
282
+
283
+ skip_decorators = skip_decorators
284
+ whitelist_decorators = [] if skip_decorators else SPIN_ALLOWED_DECORATORS
285
+ from_start("SpinStep: initialized decorators")
286
+ spin_artifacts = read_artifacts_module(artifacts_module) if artifacts_module else {}
287
+ from_start("SpinStep: read artifacts module")
288
+
289
+ ds_type, ds_root = orig_flow_datastore.split("@")
290
+ orig_datastore_impl = [d for d in DATASTORES if d.TYPE == ds_type][0]
291
+ orig_datastore_impl.datastore_root = ds_root
292
+ orig_flow_datastore = FlowDataStore(
293
+ ctx.obj.flow.name,
294
+ environment=None,
295
+ storage_impl=orig_datastore_impl,
296
+ ds_root=ds_root,
297
+ )
298
+
299
+ filecache = FileCache()
300
+ orig_flow_datastore.set_metadata_cache(
301
+ TaskMetadataCache(filecache, ds_type, ds_root, ctx.obj.flow.name)
302
+ )
303
+ orig_flow_datastore.ca_store.set_blob_cache(
304
+ FileBlobCache(
305
+ filecache, FileCache.flow_ds_id(ds_type, ds_root, ctx.obj.flow.name)
306
+ )
307
+ )
308
+
309
+ task = MetaflowTask(
310
+ ctx.obj.flow,
311
+ ctx.obj.flow_datastore,
312
+ ctx.obj.metadata,
313
+ ctx.obj.environment,
314
+ echo,
315
+ ctx.obj.event_logger,
316
+ ctx.obj.monitor,
317
+ None, # no unbounded foreach context
318
+ orig_flow_datastore=orig_flow_datastore,
319
+ spin_artifacts=spin_artifacts,
320
+ )
321
+ from_start("SpinStep: initialized task")
322
+ task.run_step(
323
+ step_name,
324
+ run_id,
325
+ task_id,
326
+ None,
327
+ input_paths,
328
+ split_index,
329
+ retry_count,
330
+ max_user_code_retries,
331
+ whitelist_decorators,
332
+ persist,
333
+ )
334
+ from_start("SpinStep: ran step")
@@ -6,6 +6,7 @@ from .core import (
6
6
  metadata,
7
7
  get_metadata,
8
8
  default_metadata,
9
+ inspect_spin,
9
10
  Metaflow,
10
11
  Flow,
11
12
  Run,