ob-metaflow 2.11.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 (289) hide show
  1. metaflow/R.py +10 -7
  2. metaflow/__init__.py +40 -25
  3. metaflow/_vendor/imghdr/__init__.py +186 -0
  4. metaflow/_vendor/importlib_metadata/__init__.py +1063 -0
  5. metaflow/_vendor/importlib_metadata/_adapters.py +68 -0
  6. metaflow/_vendor/importlib_metadata/_collections.py +30 -0
  7. metaflow/_vendor/importlib_metadata/_compat.py +71 -0
  8. metaflow/_vendor/importlib_metadata/_functools.py +104 -0
  9. metaflow/_vendor/importlib_metadata/_itertools.py +73 -0
  10. metaflow/_vendor/importlib_metadata/_meta.py +48 -0
  11. metaflow/_vendor/importlib_metadata/_text.py +99 -0
  12. metaflow/_vendor/importlib_metadata/py.typed +0 -0
  13. metaflow/_vendor/typeguard/__init__.py +48 -0
  14. metaflow/_vendor/typeguard/_checkers.py +1070 -0
  15. metaflow/_vendor/typeguard/_config.py +108 -0
  16. metaflow/_vendor/typeguard/_decorators.py +233 -0
  17. metaflow/_vendor/typeguard/_exceptions.py +42 -0
  18. metaflow/_vendor/typeguard/_functions.py +308 -0
  19. metaflow/_vendor/typeguard/_importhook.py +213 -0
  20. metaflow/_vendor/typeguard/_memo.py +48 -0
  21. metaflow/_vendor/typeguard/_pytest_plugin.py +127 -0
  22. metaflow/_vendor/typeguard/_suppression.py +86 -0
  23. metaflow/_vendor/typeguard/_transformer.py +1229 -0
  24. metaflow/_vendor/typeguard/_union_transformer.py +55 -0
  25. metaflow/_vendor/typeguard/_utils.py +173 -0
  26. metaflow/_vendor/typeguard/py.typed +0 -0
  27. metaflow/_vendor/typing_extensions.py +3641 -0
  28. metaflow/_vendor/v3_7/importlib_metadata/__init__.py +1063 -0
  29. metaflow/_vendor/v3_7/importlib_metadata/_adapters.py +68 -0
  30. metaflow/_vendor/v3_7/importlib_metadata/_collections.py +30 -0
  31. metaflow/_vendor/v3_7/importlib_metadata/_compat.py +71 -0
  32. metaflow/_vendor/v3_7/importlib_metadata/_functools.py +104 -0
  33. metaflow/_vendor/v3_7/importlib_metadata/_itertools.py +73 -0
  34. metaflow/_vendor/v3_7/importlib_metadata/_meta.py +48 -0
  35. metaflow/_vendor/v3_7/importlib_metadata/_text.py +99 -0
  36. metaflow/_vendor/v3_7/importlib_metadata/py.typed +0 -0
  37. metaflow/_vendor/v3_7/typeguard/__init__.py +48 -0
  38. metaflow/_vendor/v3_7/typeguard/_checkers.py +906 -0
  39. metaflow/_vendor/v3_7/typeguard/_config.py +108 -0
  40. metaflow/_vendor/v3_7/typeguard/_decorators.py +237 -0
  41. metaflow/_vendor/v3_7/typeguard/_exceptions.py +42 -0
  42. metaflow/_vendor/v3_7/typeguard/_functions.py +310 -0
  43. metaflow/_vendor/v3_7/typeguard/_importhook.py +213 -0
  44. metaflow/_vendor/v3_7/typeguard/_memo.py +48 -0
  45. metaflow/_vendor/v3_7/typeguard/_pytest_plugin.py +100 -0
  46. metaflow/_vendor/v3_7/typeguard/_suppression.py +88 -0
  47. metaflow/_vendor/v3_7/typeguard/_transformer.py +1207 -0
  48. metaflow/_vendor/v3_7/typeguard/_union_transformer.py +54 -0
  49. metaflow/_vendor/v3_7/typeguard/_utils.py +169 -0
  50. metaflow/_vendor/v3_7/typeguard/py.typed +0 -0
  51. metaflow/_vendor/v3_7/typing_extensions.py +3072 -0
  52. metaflow/_vendor/yaml/__init__.py +427 -0
  53. metaflow/_vendor/yaml/composer.py +139 -0
  54. metaflow/_vendor/yaml/constructor.py +748 -0
  55. metaflow/_vendor/yaml/cyaml.py +101 -0
  56. metaflow/_vendor/yaml/dumper.py +62 -0
  57. metaflow/_vendor/yaml/emitter.py +1137 -0
  58. metaflow/_vendor/yaml/error.py +75 -0
  59. metaflow/_vendor/yaml/events.py +86 -0
  60. metaflow/_vendor/yaml/loader.py +63 -0
  61. metaflow/_vendor/yaml/nodes.py +49 -0
  62. metaflow/_vendor/yaml/parser.py +589 -0
  63. metaflow/_vendor/yaml/reader.py +185 -0
  64. metaflow/_vendor/yaml/representer.py +389 -0
  65. metaflow/_vendor/yaml/resolver.py +227 -0
  66. metaflow/_vendor/yaml/scanner.py +1435 -0
  67. metaflow/_vendor/yaml/serializer.py +111 -0
  68. metaflow/_vendor/yaml/tokens.py +104 -0
  69. metaflow/cards.py +5 -0
  70. metaflow/cli.py +331 -785
  71. metaflow/cli_args.py +17 -0
  72. metaflow/cli_components/__init__.py +0 -0
  73. metaflow/cli_components/dump_cmd.py +96 -0
  74. metaflow/cli_components/init_cmd.py +52 -0
  75. metaflow/cli_components/run_cmds.py +546 -0
  76. metaflow/cli_components/step_cmd.py +334 -0
  77. metaflow/cli_components/utils.py +140 -0
  78. metaflow/client/__init__.py +1 -0
  79. metaflow/client/core.py +467 -73
  80. metaflow/client/filecache.py +75 -35
  81. metaflow/clone_util.py +7 -1
  82. metaflow/cmd/code/__init__.py +231 -0
  83. metaflow/cmd/develop/stub_generator.py +756 -288
  84. metaflow/cmd/develop/stubs.py +12 -28
  85. metaflow/cmd/main_cli.py +6 -4
  86. metaflow/cmd/make_wrapper.py +78 -0
  87. metaflow/datastore/__init__.py +1 -0
  88. metaflow/datastore/content_addressed_store.py +41 -10
  89. metaflow/datastore/datastore_set.py +11 -2
  90. metaflow/datastore/flow_datastore.py +156 -10
  91. metaflow/datastore/spin_datastore.py +91 -0
  92. metaflow/datastore/task_datastore.py +154 -39
  93. metaflow/debug.py +5 -0
  94. metaflow/decorators.py +404 -78
  95. metaflow/exception.py +8 -2
  96. metaflow/extension_support/__init__.py +527 -376
  97. metaflow/extension_support/_empty_file.py +2 -2
  98. metaflow/extension_support/plugins.py +49 -31
  99. metaflow/flowspec.py +482 -33
  100. metaflow/graph.py +210 -42
  101. metaflow/includefile.py +84 -40
  102. metaflow/lint.py +141 -22
  103. metaflow/meta_files.py +13 -0
  104. metaflow/{metadata → metadata_provider}/heartbeat.py +24 -8
  105. metaflow/{metadata → metadata_provider}/metadata.py +86 -1
  106. metaflow/metaflow_config.py +175 -28
  107. metaflow/metaflow_config_funcs.py +51 -3
  108. metaflow/metaflow_current.py +4 -10
  109. metaflow/metaflow_environment.py +139 -53
  110. metaflow/metaflow_git.py +115 -0
  111. metaflow/metaflow_profile.py +18 -0
  112. metaflow/metaflow_version.py +150 -66
  113. metaflow/mflog/__init__.py +4 -3
  114. metaflow/mflog/save_logs.py +2 -2
  115. metaflow/multicore_utils.py +31 -14
  116. metaflow/package/__init__.py +673 -0
  117. metaflow/packaging_sys/__init__.py +880 -0
  118. metaflow/packaging_sys/backend.py +128 -0
  119. metaflow/packaging_sys/distribution_support.py +153 -0
  120. metaflow/packaging_sys/tar_backend.py +99 -0
  121. metaflow/packaging_sys/utils.py +54 -0
  122. metaflow/packaging_sys/v1.py +527 -0
  123. metaflow/parameters.py +149 -28
  124. metaflow/plugins/__init__.py +74 -5
  125. metaflow/plugins/airflow/airflow.py +40 -25
  126. metaflow/plugins/airflow/airflow_cli.py +22 -5
  127. metaflow/plugins/airflow/airflow_decorator.py +1 -1
  128. metaflow/plugins/airflow/airflow_utils.py +5 -3
  129. metaflow/plugins/airflow/sensors/base_sensor.py +4 -4
  130. metaflow/plugins/airflow/sensors/external_task_sensor.py +2 -2
  131. metaflow/plugins/airflow/sensors/s3_sensor.py +2 -2
  132. metaflow/plugins/argo/argo_client.py +78 -33
  133. metaflow/plugins/argo/argo_events.py +6 -6
  134. metaflow/plugins/argo/argo_workflows.py +2410 -527
  135. metaflow/plugins/argo/argo_workflows_cli.py +571 -121
  136. metaflow/plugins/argo/argo_workflows_decorator.py +43 -12
  137. metaflow/plugins/argo/argo_workflows_deployer.py +106 -0
  138. metaflow/plugins/argo/argo_workflows_deployer_objects.py +453 -0
  139. metaflow/plugins/argo/capture_error.py +73 -0
  140. metaflow/plugins/argo/conditional_input_paths.py +35 -0
  141. metaflow/plugins/argo/exit_hooks.py +209 -0
  142. metaflow/plugins/argo/jobset_input_paths.py +15 -0
  143. metaflow/plugins/argo/param_val.py +19 -0
  144. metaflow/plugins/aws/aws_client.py +10 -3
  145. metaflow/plugins/aws/aws_utils.py +55 -2
  146. metaflow/plugins/aws/batch/batch.py +72 -5
  147. metaflow/plugins/aws/batch/batch_cli.py +33 -10
  148. metaflow/plugins/aws/batch/batch_client.py +4 -3
  149. metaflow/plugins/aws/batch/batch_decorator.py +102 -35
  150. metaflow/plugins/aws/secrets_manager/aws_secrets_manager_secrets_provider.py +13 -10
  151. metaflow/plugins/aws/step_functions/dynamo_db_client.py +0 -3
  152. metaflow/plugins/aws/step_functions/production_token.py +1 -1
  153. metaflow/plugins/aws/step_functions/step_functions.py +65 -8
  154. metaflow/plugins/aws/step_functions/step_functions_cli.py +101 -7
  155. metaflow/plugins/aws/step_functions/step_functions_decorator.py +1 -2
  156. metaflow/plugins/aws/step_functions/step_functions_deployer.py +97 -0
  157. metaflow/plugins/aws/step_functions/step_functions_deployer_objects.py +264 -0
  158. metaflow/plugins/azure/azure_exceptions.py +1 -1
  159. metaflow/plugins/azure/azure_secret_manager_secrets_provider.py +240 -0
  160. metaflow/plugins/azure/azure_tail.py +1 -1
  161. metaflow/plugins/azure/includefile_support.py +2 -0
  162. metaflow/plugins/cards/card_cli.py +66 -30
  163. metaflow/plugins/cards/card_creator.py +25 -1
  164. metaflow/plugins/cards/card_datastore.py +21 -49
  165. metaflow/plugins/cards/card_decorator.py +132 -8
  166. metaflow/plugins/cards/card_modules/basic.py +112 -17
  167. metaflow/plugins/cards/card_modules/bundle.css +1 -1
  168. metaflow/plugins/cards/card_modules/card.py +16 -1
  169. metaflow/plugins/cards/card_modules/chevron/renderer.py +1 -1
  170. metaflow/plugins/cards/card_modules/components.py +665 -28
  171. metaflow/plugins/cards/card_modules/convert_to_native_type.py +36 -7
  172. metaflow/plugins/cards/card_modules/json_viewer.py +232 -0
  173. metaflow/plugins/cards/card_modules/main.css +1 -0
  174. metaflow/plugins/cards/card_modules/main.js +68 -49
  175. metaflow/plugins/cards/card_modules/renderer_tools.py +1 -0
  176. metaflow/plugins/cards/card_modules/test_cards.py +26 -12
  177. metaflow/plugins/cards/card_server.py +39 -14
  178. metaflow/plugins/cards/component_serializer.py +2 -9
  179. metaflow/plugins/cards/metadata.py +22 -0
  180. metaflow/plugins/catch_decorator.py +9 -0
  181. metaflow/plugins/datastores/azure_storage.py +10 -1
  182. metaflow/plugins/datastores/gs_storage.py +6 -2
  183. metaflow/plugins/datastores/local_storage.py +12 -6
  184. metaflow/plugins/datastores/spin_storage.py +12 -0
  185. metaflow/plugins/datatools/local.py +2 -0
  186. metaflow/plugins/datatools/s3/s3.py +126 -75
  187. metaflow/plugins/datatools/s3/s3op.py +254 -121
  188. metaflow/plugins/env_escape/__init__.py +3 -3
  189. metaflow/plugins/env_escape/client_modules.py +102 -72
  190. metaflow/plugins/env_escape/server.py +7 -0
  191. metaflow/plugins/env_escape/stub.py +24 -5
  192. metaflow/plugins/events_decorator.py +343 -185
  193. metaflow/plugins/exit_hook/__init__.py +0 -0
  194. metaflow/plugins/exit_hook/exit_hook_decorator.py +46 -0
  195. metaflow/plugins/exit_hook/exit_hook_script.py +52 -0
  196. metaflow/plugins/gcp/__init__.py +1 -1
  197. metaflow/plugins/gcp/gcp_secret_manager_secrets_provider.py +11 -6
  198. metaflow/plugins/gcp/gs_tail.py +10 -6
  199. metaflow/plugins/gcp/includefile_support.py +3 -0
  200. metaflow/plugins/kubernetes/kube_utils.py +108 -0
  201. metaflow/plugins/kubernetes/kubernetes.py +411 -130
  202. metaflow/plugins/kubernetes/kubernetes_cli.py +168 -36
  203. metaflow/plugins/kubernetes/kubernetes_client.py +104 -2
  204. metaflow/plugins/kubernetes/kubernetes_decorator.py +246 -88
  205. metaflow/plugins/kubernetes/kubernetes_job.py +253 -581
  206. metaflow/plugins/kubernetes/kubernetes_jobsets.py +1071 -0
  207. metaflow/plugins/kubernetes/spot_metadata_cli.py +69 -0
  208. metaflow/plugins/kubernetes/spot_monitor_sidecar.py +109 -0
  209. metaflow/plugins/logs_cli.py +359 -0
  210. metaflow/plugins/{metadata → metadata_providers}/local.py +144 -84
  211. metaflow/plugins/{metadata → metadata_providers}/service.py +103 -26
  212. metaflow/plugins/metadata_providers/spin.py +16 -0
  213. metaflow/plugins/package_cli.py +36 -24
  214. metaflow/plugins/parallel_decorator.py +128 -11
  215. metaflow/plugins/parsers.py +16 -0
  216. metaflow/plugins/project_decorator.py +51 -5
  217. metaflow/plugins/pypi/bootstrap.py +357 -105
  218. metaflow/plugins/pypi/conda_decorator.py +82 -81
  219. metaflow/plugins/pypi/conda_environment.py +187 -52
  220. metaflow/plugins/pypi/micromamba.py +157 -47
  221. metaflow/plugins/pypi/parsers.py +268 -0
  222. metaflow/plugins/pypi/pip.py +88 -13
  223. metaflow/plugins/pypi/pypi_decorator.py +37 -1
  224. metaflow/plugins/pypi/utils.py +48 -2
  225. metaflow/plugins/resources_decorator.py +2 -2
  226. metaflow/plugins/secrets/__init__.py +3 -0
  227. metaflow/plugins/secrets/secrets_decorator.py +26 -181
  228. metaflow/plugins/secrets/secrets_func.py +49 -0
  229. metaflow/plugins/secrets/secrets_spec.py +101 -0
  230. metaflow/plugins/secrets/utils.py +74 -0
  231. metaflow/plugins/tag_cli.py +4 -7
  232. metaflow/plugins/test_unbounded_foreach_decorator.py +41 -6
  233. metaflow/plugins/timeout_decorator.py +3 -3
  234. metaflow/plugins/uv/__init__.py +0 -0
  235. metaflow/plugins/uv/bootstrap.py +128 -0
  236. metaflow/plugins/uv/uv_environment.py +72 -0
  237. metaflow/procpoll.py +1 -1
  238. metaflow/pylint_wrapper.py +5 -1
  239. metaflow/runner/__init__.py +0 -0
  240. metaflow/runner/click_api.py +717 -0
  241. metaflow/runner/deployer.py +470 -0
  242. metaflow/runner/deployer_impl.py +201 -0
  243. metaflow/runner/metaflow_runner.py +714 -0
  244. metaflow/runner/nbdeploy.py +132 -0
  245. metaflow/runner/nbrun.py +225 -0
  246. metaflow/runner/subprocess_manager.py +650 -0
  247. metaflow/runner/utils.py +335 -0
  248. metaflow/runtime.py +1078 -260
  249. metaflow/sidecar/sidecar_worker.py +1 -1
  250. metaflow/system/__init__.py +5 -0
  251. metaflow/system/system_logger.py +85 -0
  252. metaflow/system/system_monitor.py +108 -0
  253. metaflow/system/system_utils.py +19 -0
  254. metaflow/task.py +521 -225
  255. metaflow/tracing/__init__.py +7 -7
  256. metaflow/tracing/span_exporter.py +31 -38
  257. metaflow/tracing/tracing_modules.py +38 -43
  258. metaflow/tuple_util.py +27 -0
  259. metaflow/user_configs/__init__.py +0 -0
  260. metaflow/user_configs/config_options.py +563 -0
  261. metaflow/user_configs/config_parameters.py +598 -0
  262. metaflow/user_decorators/__init__.py +0 -0
  263. metaflow/user_decorators/common.py +144 -0
  264. metaflow/user_decorators/mutable_flow.py +512 -0
  265. metaflow/user_decorators/mutable_step.py +424 -0
  266. metaflow/user_decorators/user_flow_decorator.py +264 -0
  267. metaflow/user_decorators/user_step_decorator.py +749 -0
  268. metaflow/util.py +243 -27
  269. metaflow/vendor.py +23 -7
  270. metaflow/version.py +1 -1
  271. ob_metaflow-2.19.7.1rc0.data/data/share/metaflow/devtools/Makefile +355 -0
  272. ob_metaflow-2.19.7.1rc0.data/data/share/metaflow/devtools/Tiltfile +726 -0
  273. ob_metaflow-2.19.7.1rc0.data/data/share/metaflow/devtools/pick_services.sh +105 -0
  274. ob_metaflow-2.19.7.1rc0.dist-info/METADATA +87 -0
  275. ob_metaflow-2.19.7.1rc0.dist-info/RECORD +445 -0
  276. {ob_metaflow-2.11.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/WHEEL +1 -1
  277. {ob_metaflow-2.11.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/entry_points.txt +1 -0
  278. metaflow/_vendor/v3_5/__init__.py +0 -1
  279. metaflow/_vendor/v3_5/importlib_metadata/__init__.py +0 -644
  280. metaflow/_vendor/v3_5/importlib_metadata/_compat.py +0 -152
  281. metaflow/package.py +0 -188
  282. ob_metaflow-2.11.13.1.dist-info/METADATA +0 -85
  283. ob_metaflow-2.11.13.1.dist-info/RECORD +0 -308
  284. /metaflow/_vendor/{v3_5/zipp.py → zipp.py} +0 -0
  285. /metaflow/{metadata → metadata_provider}/__init__.py +0 -0
  286. /metaflow/{metadata → metadata_provider}/util.py +0 -0
  287. /metaflow/plugins/{metadata → metadata_providers}/__init__.py +0 -0
  288. {ob_metaflow-2.11.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info/licenses}/LICENSE +0 -0
  289. {ob_metaflow-2.11.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/top_level.txt +0 -0
metaflow/client/core.py CHANGED
@@ -5,6 +5,7 @@ import os
5
5
  import tarfile
6
6
  from collections import namedtuple
7
7
  from datetime import datetime
8
+ from tempfile import TemporaryDirectory
8
9
  from io import BytesIO
9
10
  from itertools import chain
10
11
  from typing import (
@@ -16,6 +17,7 @@ from typing import (
16
17
  List,
17
18
  NamedTuple,
18
19
  Optional,
20
+ TYPE_CHECKING,
19
21
  Tuple,
20
22
  )
21
23
 
@@ -30,13 +32,17 @@ from metaflow.exception import (
30
32
  from metaflow.includefile import IncludedFile
31
33
  from metaflow.metaflow_config import DEFAULT_METADATA, MAX_ATTEMPTS
32
34
  from metaflow.metaflow_environment import MetaflowEnvironment
35
+ from metaflow.package import MetaflowPackage
36
+ from metaflow.packaging_sys import ContentType
33
37
  from metaflow.plugins import ENVIRONMENTS, METADATA_PROVIDERS
34
38
  from metaflow.unbounded_foreach import CONTROL_TASK_TAG
35
39
  from metaflow.util import cached_property, is_stringish, resolve_identity, to_unicode
36
40
 
37
- from .. import INFO_FILE
38
41
  from .filecache import FileCache
39
42
 
43
+ if TYPE_CHECKING:
44
+ from metaflow.metadata_provider import MetadataProvider
45
+
40
46
  try:
41
47
  # python2
42
48
  import cPickle as pickle
@@ -82,28 +88,16 @@ def metadata(ms: str) -> str:
82
88
  get_metadata()).
83
89
  """
84
90
  global current_metadata
85
- infos = ms.split("@", 1)
86
- types = [m.TYPE for m in METADATA_PROVIDERS]
87
- if infos[0] in types:
88
- current_metadata = [m for m in METADATA_PROVIDERS if m.TYPE == infos[0]][0]
89
- if len(infos) > 1:
90
- current_metadata.INFO = infos[1]
91
- else:
92
- # Deduce from ms; if starts with http, use service or else use local
93
- if ms.startswith("http"):
94
- metadata_type = "service"
95
- else:
96
- metadata_type = "local"
97
- res = [m for m in METADATA_PROVIDERS if m.TYPE == metadata_type]
98
- if not res:
99
- print(
100
- "Cannot find a '%s' metadata provider -- "
101
- "try specifying one explicitly using <type>@<info>",
102
- metadata_type,
103
- )
104
- return get_metadata()
105
- current_metadata = res[0]
106
- current_metadata.INFO = ms
91
+ provider, info = _metadata(ms)
92
+ if provider is None:
93
+ print(
94
+ "Cannot find a metadata provider -- "
95
+ "try specifying one explicitly using <type>@<info>",
96
+ )
97
+ return get_metadata()
98
+ current_metadata = provider
99
+ if info:
100
+ current_metadata.INFO = info
107
101
  return get_metadata()
108
102
 
109
103
 
@@ -127,7 +121,7 @@ def get_metadata() -> str:
127
121
  """
128
122
  if current_metadata is False:
129
123
  default_metadata()
130
- return "%s@%s" % (current_metadata.TYPE, current_metadata.INFO)
124
+ return current_metadata.metadata_str()
131
125
 
132
126
 
133
127
  def default_metadata() -> str:
@@ -151,7 +145,7 @@ def default_metadata() -> str:
151
145
  if default:
152
146
  current_metadata = default[0]
153
147
  else:
154
- from metaflow.plugins.metadata import LocalMetadataProvider
148
+ from metaflow.plugins.metadata_providers import LocalMetadataProvider
155
149
 
156
150
  current_metadata = LocalMetadataProvider
157
151
  return get_metadata()
@@ -213,6 +207,20 @@ def default_namespace() -> str:
213
207
  return get_namespace()
214
208
 
215
209
 
210
+ def inspect_spin(datastore_root: str = "."):
211
+ """
212
+ Set metadata provider to spin metadata so that users can inspect spin
213
+ steps, tasks, and artifacts.
214
+
215
+ Parameters
216
+ ----------
217
+ datastore_root : str, default "."
218
+ The root path to the spin datastore.
219
+ """
220
+ metadata_str = f"spin@{datastore_root}"
221
+ metadata(metadata_str)
222
+
223
+
216
224
  MetaflowArtifacts = NamedTuple
217
225
 
218
226
 
@@ -268,13 +276,28 @@ class MetaflowObject(object):
268
276
  _object: Optional["MetaflowObject"] = None,
269
277
  _parent: Optional["MetaflowObject"] = None,
270
278
  _namespace_check: bool = True,
279
+ _metaflow: Optional["Metaflow"] = None,
280
+ _current_namespace: Optional[str] = None,
281
+ _current_metadata: Optional[str] = None,
271
282
  ):
272
- self._metaflow = Metaflow()
283
+ # the default namespace is activated lazily at the first
284
+ # get_namespace(). The other option of activating
285
+ # the namespace at the import time is problematic, since there
286
+ # may be other modules that alter environment variables etc.
287
+ # which may affect the namespace setting.
288
+ self._metaflow = Metaflow(_current_metadata) or _metaflow
273
289
  self._parent = _parent
274
290
  self._path_components = None
275
291
  self._attempt = attempt
292
+ self._current_namespace = _current_namespace or get_namespace()
276
293
  self._namespace_check = _namespace_check
277
294
 
295
+ # If the current namespace is False, we disable checking for namespace for this
296
+ # and all children objects. Not setting namespace_check to False has the consequence
297
+ # of preventing access to children objects after the namespace changes
298
+ if self._current_namespace is None:
299
+ self._namespace_check = False
300
+
278
301
  if self._attempt is not None:
279
302
  if self._NAME not in ["task", "artifact"]:
280
303
  raise MetaflowNotFound(
@@ -295,7 +318,7 @@ class MetaflowObject(object):
295
318
  # distinguish between "attempt will happen" and "no such
296
319
  # attempt exists".
297
320
 
298
- if pathspec:
321
+ if pathspec and _object is None:
299
322
  ids = pathspec.split("/")
300
323
 
301
324
  if self._NAME == "flow" and len(ids) != 1:
@@ -339,8 +362,8 @@ class MetaflowObject(object):
339
362
  self._user_tags = frozenset(self._object.get("tags") or [])
340
363
  self._system_tags = frozenset(self._object.get("system_tags") or [])
341
364
 
342
- if self._namespace_check and not self.is_in_namespace():
343
- raise MetaflowNamespaceMismatch(current_namespace)
365
+ if self._namespace_check and not self._is_in_namespace(self._current_namespace):
366
+ raise MetaflowNamespaceMismatch(self._current_namespace)
344
367
 
345
368
  def _get_object(self, *path_components):
346
369
  result = self._metaflow.metadata.get_object(
@@ -365,15 +388,15 @@ class MetaflowObject(object):
365
388
  query_filter = {}
366
389
 
367
390
  # skip namespace filtering if _namespace_check is unset.
368
- if self._namespace_check and current_namespace:
369
- query_filter = {"any_tags": current_namespace}
391
+ if self._namespace_check and self._current_namespace:
392
+ query_filter = {"any_tags": self._current_namespace}
370
393
 
371
394
  unfiltered_children = self._metaflow.metadata.get_object(
372
395
  self._NAME,
373
396
  _CLASSES[self._CHILD_CLASS]._NAME,
374
397
  query_filter,
375
398
  self._attempt,
376
- *self.path_components
399
+ *self.path_components,
377
400
  )
378
401
  unfiltered_children = unfiltered_children if unfiltered_children else []
379
402
  children = filter(
@@ -383,7 +406,11 @@ class MetaflowObject(object):
383
406
  attempt=self._attempt,
384
407
  _object=obj,
385
408
  _parent=self,
386
- _namespace_check=False,
409
+ _metaflow=self._metaflow,
410
+ _namespace_check=self._namespace_check,
411
+ _current_namespace=(
412
+ self._current_namespace if self._namespace_check else None
413
+ ),
387
414
  )
388
415
  for obj in unfiltered_children
389
416
  ),
@@ -422,6 +449,23 @@ class MetaflowObject(object):
422
449
 
423
450
  If the current namespace is None, this will always return True.
424
451
 
452
+ Returns
453
+ -------
454
+ bool
455
+ Whether or not the object is in the current namespace
456
+ """
457
+ return self._is_in_namespace(current_namespace)
458
+
459
+ def _is_in_namespace(self, ns: str) -> bool:
460
+ """
461
+ Returns whether this object is in namespace passed in.
462
+
463
+ If the current namespace is None, this will always return True.
464
+
465
+ Parameters
466
+ ----------
467
+ ns : str
468
+ Namespace to check if the object is in.
425
469
  Returns
426
470
  -------
427
471
  bool
@@ -430,7 +474,7 @@ class MetaflowObject(object):
430
474
  if self._NAME == "flow":
431
475
  return any(True for _ in self)
432
476
  else:
433
- return current_namespace is None or current_namespace in self._tags
477
+ return ns is None or ns in self._tags
434
478
 
435
479
  def __str__(self):
436
480
  if self._attempt is not None:
@@ -478,7 +522,11 @@ class MetaflowObject(object):
478
522
  attempt=self._attempt,
479
523
  _object=obj,
480
524
  _parent=self,
525
+ _metaflow=self._metaflow,
481
526
  _namespace_check=self._namespace_check,
527
+ _current_namespace=(
528
+ self._current_namespace if self._namespace_check else None
529
+ ),
482
530
  )
483
531
  else:
484
532
  raise KeyError(id)
@@ -509,7 +557,38 @@ class MetaflowObject(object):
509
557
  pathspec=pathspec, attempt=attempt, _namespace_check=namespace_check
510
558
  )
511
559
 
512
- _UNPICKLE_FUNC = {"2.8.4": _unpickle_284}
560
+ def _unpickle_2124(self, data):
561
+ if len(data) != 4:
562
+ raise MetaflowInternalError(
563
+ "Unexpected size of array: {}".format(len(data))
564
+ )
565
+ pathspec, attempt, ns, namespace_check = data
566
+ self.__init__(
567
+ pathspec=pathspec,
568
+ attempt=attempt,
569
+ _namespace_check=namespace_check,
570
+ _current_namespace=ns,
571
+ )
572
+
573
+ def _unpickle_21227(self, data):
574
+ if len(data) != 5:
575
+ raise MetaflowInternalError(
576
+ "Unexpected size of array: {}".format(len(data))
577
+ )
578
+ pathspec, attempt, md, ns, namespace_check = data
579
+ self.__init__(
580
+ pathspec=pathspec,
581
+ attempt=attempt,
582
+ _namespace_check=namespace_check,
583
+ _current_metadata=md,
584
+ _current_namespace=ns,
585
+ )
586
+
587
+ _UNPICKLE_FUNC = {
588
+ "2.8.4": _unpickle_284,
589
+ "2.12.4": _unpickle_2124,
590
+ "2.12.27": _unpickle_21227,
591
+ }
513
592
 
514
593
  def __setstate__(self, state):
515
594
  """
@@ -529,12 +608,13 @@ class MetaflowObject(object):
529
608
  self._UNPICKLE_FUNC[version](self, state["data"])
530
609
  else:
531
610
  # For backward compatibility: handles pickled objects that were serialized without a __getstate__ override
532
- # We set namespace_check to False if it doesn't exist for the same
533
- # reason as the one listed in __getstate__
611
+ # We set namespace_check to False if it doesn't exist so that the user can
612
+ # continue accessing this object once unpickled.
534
613
  self.__init__(
535
614
  pathspec=state.get("_pathspec", None),
536
615
  attempt=state.get("_attempt", None),
537
616
  _namespace_check=state.get("_namespace_check", False),
617
+ _current_namespace=None,
538
618
  )
539
619
 
540
620
  def __getstate__(self):
@@ -546,16 +626,18 @@ class MetaflowObject(object):
546
626
  from this object) are pickled (serialized) in a later version of Metaflow, it may not be possible
547
627
  to unpickle (deserialize) them in a previous version of Metaflow.
548
628
  """
549
- # Note that we set _namespace_check to False because we want the user to
550
- # be able to access this object even after unpickling it. If we set it to
551
- # True, it would check the namespace again at the time of unpickling even
552
- # if the user properly got the object in the first place and pickled it.
629
+ # Note that we now record the namespace at the time of the object creation so
630
+ # we don't need to force namespace_check to be False and can properly continue
631
+ # checking for the namespace even after unpickling since we will know which
632
+ # namespace to check.
553
633
  return {
554
- "version": "2.8.4",
634
+ "version": "2.12.27",
555
635
  "data": [
556
636
  self.pathspec,
557
637
  self._attempt,
558
- False,
638
+ self._metaflow.metadata.metadata_str(),
639
+ self._current_namespace,
640
+ self._namespace_check,
559
641
  ],
560
642
  }
561
643
 
@@ -625,7 +707,7 @@ class MetaflowObject(object):
625
707
  origin_pathspec = None
626
708
  if self._NAME == "run":
627
709
  latest_step = next(self.steps())
628
- if latest_step:
710
+ if latest_step and latest_step.task:
629
711
  # If we had a step
630
712
  task = latest_step.task
631
713
  origin_run_id = [
@@ -750,20 +832,29 @@ class MetaflowCode(object):
750
832
  self._path = info["location"]
751
833
  self._ds_type = info["ds_type"]
752
834
  self._sha = info["sha"]
835
+ self._code_metadata = info.get(
836
+ "metadata",
837
+ '{"version": 0, "archive_format": "tgz", "mfcontent_version": 0}',
838
+ )
839
+
840
+ self._backend = MetaflowPackage.get_backend(self._code_metadata)
753
841
 
754
842
  if filecache is None:
755
843
  filecache = FileCache()
756
844
  _, blobdata = filecache.get_data(
757
845
  self._ds_type, self._flow_name, self._path, self._sha
758
846
  )
759
- code_obj = BytesIO(blobdata)
760
- self._tar = tarfile.open(fileobj=code_obj, mode="r:gz")
761
- # The JSON module in Python3 deals with Unicode. Tar gives bytes.
762
- info_str = (
763
- self._tar.extractfile(os.path.basename(INFO_FILE)).read().decode("utf-8")
764
- )
765
- self._info = json.loads(info_str)
766
- self._flowspec = self._tar.extractfile(self._info["script"]).read()
847
+ self._code_obj = BytesIO(blobdata)
848
+ self._info = MetaflowPackage.cls_get_info(self._code_metadata, self._code_obj)
849
+ self._code_obj.seek(0)
850
+ if self._info:
851
+ self._flowspec = MetaflowPackage.cls_get_content(
852
+ self._code_metadata, self._code_obj, self._info["script"]
853
+ )
854
+ self._code_obj.seek(0)
855
+ else:
856
+ raise MetaflowInternalError("Code package metadata is invalid.")
857
+ self._tarball = None
767
858
 
768
859
  @property
769
860
  def path(self) -> str:
@@ -811,7 +902,69 @@ class MetaflowCode(object):
811
902
  TarFile
812
903
  TarFile for everything in this code package
813
904
  """
814
- return self._tar
905
+ # We only return one tarball because the different TarFile objects share
906
+ # a common bytes buffer (self._code_obj).
907
+ if self._tarball is not None:
908
+ return self._tarball
909
+ if self._backend.type == "tgz":
910
+ self._tarball = self._backend.cls_open(self._code_obj)
911
+ return self._tarball
912
+ raise RuntimeError("Archive is not a tarball")
913
+
914
+ def extract(self) -> TemporaryDirectory:
915
+ """
916
+ Extracts the code package to a temporary directory.
917
+
918
+ This creates a temporary directory containing all user code
919
+ files from the code package. The temporary directory is
920
+ automatically deleted when the returned TemporaryDirectory
921
+ object is garbage collected or when its cleanup() is called.
922
+
923
+ To preserve the contents to a permanent location, use
924
+ os.replace() which performs a zero-copy move on the same
925
+ filesystem:
926
+
927
+ ```python
928
+ with task.code.extract() as tmp_dir:
929
+ # Move contents to permanent location
930
+ for item in os.listdir(tmp_dir):
931
+ src = os.path.join(tmp_dir, item)
932
+ dst = os.path.join('/path/to/permanent/dir', item)
933
+ os.makedirs(os.path.dirname(dst), exist_ok=True)
934
+ os.replace(src, dst) # Atomic move operation
935
+ ```
936
+ Returns
937
+ -------
938
+ TemporaryDirectory
939
+ A temporary directory containing the extracted code files.
940
+ The directory and its contents are automatically deleted when
941
+ this object is garbage collected.
942
+ """
943
+ tmp = TemporaryDirectory()
944
+ # We save the position we are in _code_obj -- in case tarball is using it at
945
+ # the same time -- so we can reset it to not perturb tarball.
946
+ pos = self._code_obj.tell()
947
+ self._code_obj.seek(0)
948
+ MetaflowPackage.cls_extract_into(
949
+ self._code_metadata, self._code_obj, tmp.name, ContentType.USER_CONTENT
950
+ )
951
+ self._code_obj.seek(pos)
952
+ return tmp
953
+
954
+ @property
955
+ def script_name(self) -> str:
956
+ """
957
+ Returns the filename of the Python script containing the FlowSpec.
958
+
959
+ This is the main Python file that was used to execute the flow. For example,
960
+ if your flow is defined in 'myflow.py', this property will return 'myflow.py'.
961
+
962
+ Returns
963
+ -------
964
+ str
965
+ Name of the Python file containing the FlowSpec
966
+ """
967
+ return self._info["script"]
815
968
 
816
969
  def __str__(self):
817
970
  return "<MetaflowCode: %s>" % self._info["script"]
@@ -982,6 +1135,8 @@ class MetaflowData(object):
982
1135
  self._artifacts = dict((art.id, art) for art in artifacts)
983
1136
 
984
1137
  def __getattr__(self, name: str):
1138
+ if name not in self._artifacts:
1139
+ raise AttributeError(name)
985
1140
  return self._artifacts[name].data
986
1141
 
987
1142
  def __contains__(self, var):
@@ -1049,13 +1204,198 @@ class Task(MetaflowObject):
1049
1204
  _PARENT_CLASS = "step"
1050
1205
  _CHILD_CLASS = "artifact"
1051
1206
 
1052
- def __init__(self, *args, **kwargs):
1053
- super(Task, self).__init__(*args, **kwargs)
1054
-
1055
1207
  def _iter_filter(self, x):
1056
1208
  # exclude private data artifacts
1057
1209
  return x.id[0] != "_"
1058
1210
 
1211
+ def _get_matching_pathspecs(self, steps, metadata_key, metadata_pattern):
1212
+ """
1213
+ Yield pathspecs of tasks from specified steps that match a given metadata pattern.
1214
+
1215
+ Parameters
1216
+ ----------
1217
+ steps : List[str]
1218
+ List of Step objects to search for tasks.
1219
+ metadata_key : str
1220
+ Metadata key to filter tasks on (e.g., 'foreach-execution-path').
1221
+ metadata_pattern : str
1222
+ Regular expression pattern to match against the metadata value.
1223
+
1224
+ Yields
1225
+ ------
1226
+ str
1227
+ Pathspec of each task whose metadata value for the specified key matches the pattern.
1228
+ """
1229
+ flow_id, run_id, _, _ = self.path_components
1230
+ for step in steps:
1231
+ task_pathspecs = self._metaflow.metadata.filter_tasks_by_metadata(
1232
+ flow_id, run_id, step, metadata_key, metadata_pattern
1233
+ )
1234
+ for task_pathspec in task_pathspecs:
1235
+ yield task_pathspec
1236
+
1237
+ @staticmethod
1238
+ def _get_previous_steps(graph_info, step_name):
1239
+ # Get the parent steps
1240
+ steps = []
1241
+ for node_name, attributes in graph_info["steps"].items():
1242
+ if step_name in attributes["next"]:
1243
+ steps.append(node_name)
1244
+ return steps
1245
+
1246
+ @property
1247
+ def parent_task_pathspecs(self) -> Iterator[str]:
1248
+ """
1249
+ Yields pathspecs of all parent tasks of the current task.
1250
+
1251
+ Yields
1252
+ ------
1253
+ str
1254
+ Pathspec of the parent task of the current task
1255
+ """
1256
+ _, _, step_name, _ = self.path_components
1257
+ metadata_dict = self.metadata_dict
1258
+ graph_info = self["_graph_info"].data
1259
+
1260
+ # Get the parent steps
1261
+ steps = self._get_previous_steps(graph_info, step_name)
1262
+ node_type = graph_info["steps"][step_name]["type"]
1263
+ metadata_key = "foreach-execution-path"
1264
+ current_path = metadata_dict.get(metadata_key)
1265
+
1266
+ if len(steps) > 1:
1267
+ # Static join - use exact path matching
1268
+ pattern = current_path or ".*"
1269
+ else:
1270
+ if not steps:
1271
+ return # No parent steps, yield nothing
1272
+
1273
+ if not current_path:
1274
+ # Current task is not part of a foreach
1275
+ # Pattern: ".*"
1276
+ pattern = ".*"
1277
+ else:
1278
+ current_depth = len(current_path.split(","))
1279
+ if node_type == "join":
1280
+ # Foreach join
1281
+ # (Current task, "A:10,B:13") and (Parent task, "A:10,B:13,C:21")
1282
+ # Pattern: "A:10,B:13,.*"
1283
+ pattern = f"{current_path},.*"
1284
+ else:
1285
+ # Foreach split or linear step
1286
+ # Pattern: "A:10,B:13"
1287
+ parent_step_type = graph_info["steps"][steps[0]]["type"]
1288
+ target_depth = current_depth
1289
+ if (
1290
+ parent_step_type == "split-foreach"
1291
+ or parent_step_type == "split-parallel"
1292
+ ) and current_depth == 1:
1293
+ # (Current task, "A:10") and (Parent task, "")
1294
+ pattern = ".*"
1295
+ else:
1296
+ # (Current task, "A:10,B:13,C:21") and (Parent task, "A:10,B:13")
1297
+ # (Current task, "A:10,B:13") and (Parent task, "A:10,B:13")
1298
+ if (
1299
+ parent_step_type == "split-foreach"
1300
+ or parent_step_type == "split-parallel"
1301
+ ):
1302
+ target_depth = current_depth - 1
1303
+ pattern = ",".join(current_path.split(",")[:target_depth])
1304
+
1305
+ for pathspec in self._get_matching_pathspecs(steps, metadata_key, pattern):
1306
+ yield pathspec
1307
+
1308
+ @property
1309
+ def child_task_pathspecs(self) -> Iterator[str]:
1310
+ """
1311
+ Yields pathspecs of all child tasks of the current task.
1312
+
1313
+ Yields
1314
+ ------
1315
+ str
1316
+ Pathspec of the child task of the current task
1317
+ """
1318
+ flow_id, run_id, step_name, _ = self.path_components
1319
+ metadata_dict = self.metadata_dict
1320
+ graph_info = self["_graph_info"].data
1321
+
1322
+ # Get the child steps
1323
+ steps = graph_info["steps"][step_name]["next"]
1324
+
1325
+ node_type = graph_info["steps"][step_name]["type"]
1326
+ metadata_key = "foreach-execution-path"
1327
+ current_path = metadata_dict.get(metadata_key)
1328
+
1329
+ if len(steps) > 1:
1330
+ # Static split - use exact path matching
1331
+ pattern = current_path or ".*"
1332
+ else:
1333
+ if not steps:
1334
+ return # No child steps, yield nothing
1335
+
1336
+ if not current_path:
1337
+ # Current task is not part of a foreach
1338
+ # Pattern: ".*"
1339
+ pattern = ".*"
1340
+ else:
1341
+ current_depth = len(current_path.split(","))
1342
+ if node_type == "split-foreach" or node_type == "split-parallel":
1343
+ # Foreach split
1344
+ # (Current task, "A:10,B:13") and (Child task, "A:10,B:13,C:21")
1345
+ # Pattern: "A:10,B:13,.*"
1346
+ pattern = f"{current_path},.*"
1347
+ else:
1348
+ # Foreach join or linear step
1349
+ # Pattern: "A:10,B:13"
1350
+ child_step_type = graph_info["steps"][steps[0]]["type"]
1351
+
1352
+ # We need to know if the child step is a foreach join or a static join
1353
+ child_step_prev_steps = self._get_previous_steps(
1354
+ graph_info, steps[0]
1355
+ )
1356
+ if len(child_step_prev_steps) > 1:
1357
+ child_step_type = "static-join"
1358
+ target_depth = current_depth
1359
+ if child_step_type == "join" and current_depth == 1:
1360
+ # (Current task, "A:10") and (Child task, "")
1361
+ pattern = ".*"
1362
+ else:
1363
+ # (Current task, "A:10,B:13,C:21") and (Child task, "A:10,B:13")
1364
+ # (Current task, "A:10,B:13") and (Child task, "A:10,B:13")
1365
+ if child_step_type == "join":
1366
+ target_depth = current_depth - 1
1367
+ pattern = ",".join(current_path.split(",")[:target_depth])
1368
+
1369
+ for pathspec in self._get_matching_pathspecs(steps, metadata_key, pattern):
1370
+ yield pathspec
1371
+
1372
+ @property
1373
+ def parent_tasks(self) -> Iterator["Task"]:
1374
+ """
1375
+ Yields all parent tasks of the current task if one exists.
1376
+
1377
+ Yields
1378
+ ------
1379
+ Task
1380
+ Parent task of the current task
1381
+ """
1382
+ parent_task_pathspecs = self.parent_task_pathspecs
1383
+ for pathspec in parent_task_pathspecs:
1384
+ yield Task(pathspec=pathspec, _namespace_check=False)
1385
+
1386
+ @property
1387
+ def child_tasks(self) -> Iterator["Task"]:
1388
+ """
1389
+ Yields all child tasks of the current task if one exists.
1390
+
1391
+ Yields
1392
+ ------
1393
+ Task
1394
+ Child task of the current task
1395
+ """
1396
+ for pathspec in self.child_task_pathspecs:
1397
+ yield Task(pathspec=pathspec, _namespace_check=False)
1398
+
1059
1399
  @property
1060
1400
  def metadata(self) -> List[Metadata]:
1061
1401
  """
@@ -1770,6 +2110,41 @@ class Step(MetaflowObject):
1770
2110
  for t in self:
1771
2111
  return t.environment_info
1772
2112
 
2113
+ @property
2114
+ def parent_steps(self) -> Iterator["Step"]:
2115
+ """
2116
+ Yields parent steps for the current step.
2117
+
2118
+ Yields
2119
+ ------
2120
+ Step
2121
+ Parent step
2122
+ """
2123
+ graph_info = self.task["_graph_info"].data
2124
+
2125
+ if self.id != "start":
2126
+ flow, run, _ = self.path_components
2127
+ for node_name, attributes in graph_info["steps"].items():
2128
+ if self.id in attributes["next"]:
2129
+ yield Step(f"{flow}/{run}/{node_name}", _namespace_check=False)
2130
+
2131
+ @property
2132
+ def child_steps(self) -> Iterator["Step"]:
2133
+ """
2134
+ Yields child steps for the current step.
2135
+
2136
+ Yields
2137
+ ------
2138
+ Step
2139
+ Child step
2140
+ """
2141
+ graph_info = self.task["_graph_info"].data
2142
+
2143
+ if self.id != "end":
2144
+ flow, run, _ = self.path_components
2145
+ for next_step in graph_info["steps"][self.id]["next"]:
2146
+ yield Step(f"{flow}/{run}/{next_step}", _namespace_check=False)
2147
+
1773
2148
 
1774
2149
  class Run(MetaflowObject):
1775
2150
  """
@@ -1841,9 +2216,10 @@ class Run(MetaflowObject):
1841
2216
  # TODO: A more optimized way of figuring out if a run has remote steps (and thus a codepackage) available.
1842
2217
  # This might require changes to the metadata-service as well.
1843
2218
  for step in self:
1844
- code = step.task.code
1845
- if code:
1846
- return code
2219
+ if step.task:
2220
+ code = step.task.code
2221
+ if code:
2222
+ return code
1847
2223
 
1848
2224
  @property
1849
2225
  def data(self) -> Optional[MetaflowData]:
@@ -2105,7 +2481,7 @@ class Run(MetaflowObject):
2105
2481
  Trigger, optional
2106
2482
  Container of triggering events
2107
2483
  """
2108
- if "start" in self:
2484
+ if "start" in self and self["start"].task:
2109
2485
  meta = self["start"].task.metadata_dict.get("execution-triggers")
2110
2486
  if meta:
2111
2487
  return Trigger(json.loads(meta))
@@ -2240,17 +2616,16 @@ class Metaflow(object):
2240
2616
  if it has at least one run in the namespace.
2241
2617
  """
2242
2618
 
2243
- def __init__(self):
2244
- # the default namespace is activated lazily at the first object
2245
- # invocation or get_namespace(). The other option of activating
2246
- # the namespace at the import time is problematic, since there
2247
- # may be other modules that alter environment variables etc.
2248
- # which may affect the namescape setting.
2249
- if current_namespace is False:
2250
- default_namespace()
2251
- if current_metadata is False:
2252
- default_metadata()
2253
- self.metadata = current_metadata
2619
+ def __init__(self, _current_metadata: Optional[str] = None):
2620
+ if _current_metadata:
2621
+ provider, info = _metadata(_current_metadata)
2622
+ self.metadata = provider
2623
+ if info:
2624
+ self.metadata.INFO = info
2625
+ else:
2626
+ if current_metadata is False:
2627
+ default_metadata()
2628
+ self.metadata = current_metadata
2254
2629
 
2255
2630
  @property
2256
2631
  def flows(self) -> List[Flow]:
@@ -2287,7 +2662,7 @@ class Metaflow(object):
2287
2662
  all_flows = all_flows if all_flows else []
2288
2663
  for flow in all_flows:
2289
2664
  try:
2290
- v = Flow(_object=flow)
2665
+ v = Flow(_object=flow, _metaflow=self)
2291
2666
  yield v
2292
2667
  except MetaflowNamespaceMismatch:
2293
2668
  continue
@@ -2311,7 +2686,26 @@ class Metaflow(object):
2311
2686
  Flow
2312
2687
  Flow with the given name.
2313
2688
  """
2314
- return Flow(id)
2689
+ return Flow(name, _metaflow=self)
2690
+
2691
+
2692
+ def _metadata(ms: str) -> Tuple[Optional["MetadataProvider"], Optional[str]]:
2693
+ infos = ms.split("@", 1)
2694
+ types = [m.TYPE for m in METADATA_PROVIDERS]
2695
+ if infos[0] in types:
2696
+ provider = [m for m in METADATA_PROVIDERS if m.TYPE == infos[0]][0]
2697
+ if len(infos) > 1:
2698
+ return provider, infos[1]
2699
+ return provider, None
2700
+ # Deduce from ms; if starts with http, use service or else use local
2701
+ if ms.startswith("http"):
2702
+ metadata_type = "service"
2703
+ else:
2704
+ metadata_type = "local"
2705
+ res = [m for m in METADATA_PROVIDERS if m.TYPE == metadata_type]
2706
+ if not res:
2707
+ return None, None
2708
+ return res[0], ms
2315
2709
 
2316
2710
 
2317
2711
  _CLASSES["flow"] = Flow