metaflow 2.12.24__py2.py3-none-any.whl → 2.12.26__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.
metaflow/client/core.py CHANGED
@@ -670,7 +670,7 @@ class MetaflowObject(object):
670
670
  origin_pathspec = None
671
671
  if self._NAME == "run":
672
672
  latest_step = next(self.steps())
673
- if latest_step:
673
+ if latest_step and latest_step.task:
674
674
  # If we had a step
675
675
  task = latest_step.task
676
676
  origin_run_id = [
@@ -1888,9 +1888,10 @@ class Run(MetaflowObject):
1888
1888
  # TODO: A more optimized way of figuring out if a run has remote steps (and thus a codepackage) available.
1889
1889
  # This might require changes to the metadata-service as well.
1890
1890
  for step in self:
1891
- code = step.task.code
1892
- if code:
1893
- return code
1891
+ if step.task:
1892
+ code = step.task.code
1893
+ if code:
1894
+ return code
1894
1895
 
1895
1896
  @property
1896
1897
  def data(self) -> Optional[MetaflowData]:
@@ -2152,7 +2153,7 @@ class Run(MetaflowObject):
2152
2153
  Trigger, optional
2153
2154
  Container of triggering events
2154
2155
  """
2155
- if "start" in self:
2156
+ if "start" in self and self["start"].task:
2156
2157
  meta = self["start"].task.metadata_dict.get("execution-triggers")
2157
2158
  if meta:
2158
2159
  return Trigger(json.loads(meta))
@@ -10,6 +10,14 @@ class ArgoClientException(MetaflowException):
10
10
  headline = "Argo Client error"
11
11
 
12
12
 
13
+ class ArgoResourceNotFound(MetaflowException):
14
+ headline = "Resource not found"
15
+
16
+
17
+ class ArgoNotPermitted(MetaflowException):
18
+ headline = "Operation not permitted"
19
+
20
+
13
21
  class ArgoClient(object):
14
22
  def __init__(self, namespace=None):
15
23
  self._client = KubernetesClient()
@@ -140,9 +148,7 @@ class ArgoClient(object):
140
148
  if e.status == 404:
141
149
  return None
142
150
  else:
143
- raise ArgoClientException(
144
- json.loads(e.body)["message"] if e.body is not None else e.reason
145
- )
151
+ raise wrap_api_error(e)
146
152
 
147
153
  def delete_workflow_template(self, name):
148
154
  """
@@ -164,9 +170,7 @@ class ArgoClient(object):
164
170
  if e.status == 404:
165
171
  return None
166
172
  else:
167
- raise ArgoClientException(
168
- json.loads(e.body)["message"] if e.body is not None else e.reason
169
- )
173
+ raise wrap_api_error(e)
170
174
 
171
175
  def terminate_workflow(self, name):
172
176
  client = self._client.get()
@@ -428,6 +432,18 @@ class ArgoClient(object):
428
432
  except client.rest.ApiException as e:
429
433
  if e.status == 404:
430
434
  return None
431
- raise ArgoClientException(
432
- json.loads(e.body)["message"] if e.body is not None else e.reason
433
- )
435
+ raise wrap_api_error(e)
436
+
437
+
438
+ def wrap_api_error(error):
439
+ message = (
440
+ json.loads(error.body)["message"] if error.body is not None else error.reason
441
+ )
442
+ # catch all
443
+ ex = ArgoClientException(message)
444
+ if error.status == 404:
445
+ # usually handled outside this function as most cases want to return None instead.
446
+ ex = ArgoResourceNotFound(message)
447
+ if error.status == 403:
448
+ ex = ArgoNotPermitted(message)
449
+ return ex
@@ -2288,7 +2288,9 @@ class ArgoWorkflows(object):
2288
2288
  and k not in set(ARGO_WORKFLOWS_ENV_VARS_TO_SKIP.split(","))
2289
2289
  }
2290
2290
  return [
2291
- Template("error-msg-capture-hook").container(
2291
+ Template("error-msg-capture-hook")
2292
+ .service_account_name(resources["service_account"])
2293
+ .container(
2292
2294
  to_camelcase(
2293
2295
  kubernetes_sdk.V1Container(
2294
2296
  name="main",
@@ -2431,7 +2433,7 @@ class ArgoWorkflows(object):
2431
2433
  Use Slack's Block Kit to add general information about the environment and
2432
2434
  execution metadata, including a link to the UI and an optional message.
2433
2435
  """
2434
- ui_link = "%s%s/argo-{{workflow.name}}" % (UI_URL, self.flow.name)
2436
+ ui_link = "%s/%s/argo-{{workflow.name}}" % (UI_URL.rstrip("/"), self.flow.name)
2435
2437
  # fmt: off
2436
2438
  if getattr(current, "project_name", None):
2437
2439
  # Add @project metadata when available.
@@ -1016,13 +1016,7 @@ def validate_run_id(
1016
1016
 
1017
1017
  if project_name is not None:
1018
1018
  # Verify we are operating on the correct project.
1019
- # Perform match with separators to avoid substrings matching
1020
- # e.g. 'test_proj' and 'test_project' should count as a mismatch.
1021
- project_part = "%s." % sanitize_for_argo(project_name)
1022
- if (
1023
- current.get("project_name") != project_name
1024
- and project_part not in workflow_name
1025
- ):
1019
+ if current.get("project_name") != project_name:
1026
1020
  raise RunIdMismatch(
1027
1021
  "The workflow belongs to the project *%s*. "
1028
1022
  "Please use the project decorator or --name to target the correct project"
@@ -1030,13 +1024,7 @@ def validate_run_id(
1030
1024
  )
1031
1025
 
1032
1026
  # Verify we are operating on the correct branch.
1033
- # Perform match with separators to avoid substrings matching.
1034
- # e.g. 'user.tes' and 'user.test' should count as a mismatch.
1035
- branch_part = ".%s." % sanitize_for_argo(branch_name)
1036
- if (
1037
- current.get("branch_name") != branch_name
1038
- and branch_part not in workflow_name
1039
- ):
1027
+ if current.get("branch_name") != branch_name:
1040
1028
  raise RunIdMismatch(
1041
1029
  "The workflow belongs to the branch *%s*. "
1042
1030
  "Please use --branch, --production or --name to target the correct branch"
@@ -134,6 +134,10 @@ class BatchDecorator(StepDecorator):
134
134
  package_sha = None
135
135
  run_time_limit = None
136
136
 
137
+ # Conda environment support
138
+ supports_conda_environment = True
139
+ target_platform = "linux-64"
140
+
137
141
  def __init__(self, attributes=None, statically_defined=False):
138
142
  super(BatchDecorator, self).__init__(attributes, statically_defined)
139
143
 
@@ -70,7 +70,7 @@ class AzureTail(object):
70
70
  if data is None:
71
71
  return None
72
72
  if data:
73
- buf = BytesIO(data)
73
+ buf = BytesIO(self._tail + data)
74
74
  self._pos += len(data)
75
75
  self._tail = b""
76
76
  return buf
@@ -117,6 +117,7 @@ class CardDecorator(StepDecorator):
117
117
  # `'%s-%s'%(evt_name,step_name)` ensures that we capture this once per @card per @step.
118
118
  # Since there can be many steps checking if event is registered for `evt_name` will only make it check it once for all steps.
119
119
  # Hence, we have `_is_event_registered('%s-%s'%(evt_name,step_name))`
120
+ self._is_runtime_card = False
120
121
  evt = "%s-%s" % (evt_name, step_name)
121
122
  if not self._is_event_registered(evt):
122
123
  # We set the total count of decorators so that we can use it for
@@ -11,15 +11,15 @@ from metaflow.plugins.gcp.gs_utils import parse_gs_full_path
11
11
  class GSTail(object):
12
12
  def __init__(self, blob_full_uri):
13
13
  """Location should be something like gs://<bucket_name>/blob"""
14
- bucket_name, blob_name = parse_gs_full_path(blob_full_uri)
15
- if not blob_name:
14
+ self.bucket_name, self.blob_name = parse_gs_full_path(blob_full_uri)
15
+ if not self.blob_name:
16
16
  raise MetaflowException(
17
17
  msg="Failed to parse blob_full_uri into gs://<bucket_name>/<blob_name> (got %s)"
18
18
  % blob_full_uri
19
19
  )
20
20
  client = get_gs_storage_client()
21
- bucket = client.bucket(bucket_name)
22
- self._blob_client = bucket.blob(blob_name)
21
+ self.bucket = client.bucket(self.bucket_name)
22
+ self._blob_client = self.bucket.blob(self.blob_name)
23
23
  self._pos = 0
24
24
  self._tail = b""
25
25
 
@@ -46,7 +46,11 @@ class GSTail(object):
46
46
  def _make_range_request(self):
47
47
  try:
48
48
  # Yes we read to the end... memory blow up is possible. We can improve by specifying length param
49
- return self._blob_client.download_as_bytes(start=self._pos)
49
+ # NOTE: We must re-instantiate the whole client here due to a behavior with the GS library,
50
+ # otherwise download_as_bytes will simply return the same content for consecutive requests with the same attributes,
51
+ # even if the blob has grown in size.
52
+ blob_client = self.bucket.blob(self.blob_name)
53
+ return blob_client.download_as_bytes(start=self._pos)
50
54
  except NotFound:
51
55
  return None
52
56
  except ClientError as e:
@@ -63,7 +67,7 @@ class GSTail(object):
63
67
  if data is None:
64
68
  return None
65
69
  if data:
66
- buf = BytesIO(data)
70
+ buf = BytesIO(self._tail + data)
67
71
  self._pos += len(data)
68
72
  self._tail = b""
69
73
  return buf
@@ -710,12 +710,23 @@ class Kubernetes(object):
710
710
  wait_for_launch(self._job)
711
711
 
712
712
  # 2) Tail logs until the job has finished
713
+ self._output_final_logs = False
714
+
715
+ def _has_updates():
716
+ if self._job.is_running:
717
+ return True
718
+ # Make sure to output final tail for a job that has finished.
719
+ if not self._output_final_logs:
720
+ self._output_final_logs = True
721
+ return True
722
+ return False
723
+
713
724
  tail_logs(
714
725
  prefix=prefix(),
715
726
  stdout_tail=stdout_tail,
716
727
  stderr_tail=stderr_tail,
717
728
  echo=echo,
718
- has_log_updates=lambda: self._job.is_running,
729
+ has_log_updates=_has_updates,
719
730
  )
720
731
  # 3) Fetch remaining logs
721
732
  #
@@ -2,6 +2,7 @@ import json
2
2
  import os
3
3
  import platform
4
4
  import sys
5
+ import time
5
6
 
6
7
  from metaflow import current
7
8
  from metaflow.decorators import StepDecorator
@@ -104,6 +105,9 @@ class KubernetesDecorator(StepDecorator):
104
105
  compute_pool : str, optional, default None
105
106
  Compute pool to be used for for this step.
106
107
  If not specified, any accessible compute pool within the perimeter is used.
108
+ hostname_resolution_timeout: int, default 10 * 60
109
+ Timeout in seconds for the workers tasks in the gang scheduled cluster to resolve the hostname of control task.
110
+ Only applicable when @parallel is used.
107
111
  """
108
112
 
109
113
  name = "kubernetes"
@@ -130,11 +134,16 @@ class KubernetesDecorator(StepDecorator):
130
134
  "port": None,
131
135
  "compute_pool": None,
132
136
  "executable": None,
137
+ "hostname_resolution_timeout": 10 * 60,
133
138
  }
134
139
  package_url = None
135
140
  package_sha = None
136
141
  run_time_limit = None
137
142
 
143
+ # Conda environment support
144
+ supports_conda_environment = True
145
+ target_platform = "linux-64"
146
+
138
147
  def __init__(self, attributes=None, statically_defined=False):
139
148
  super(KubernetesDecorator, self).__init__(attributes, statically_defined)
140
149
 
@@ -386,7 +395,7 @@ class KubernetesDecorator(StepDecorator):
386
395
  cli_args.command_args.append(self.package_url)
387
396
 
388
397
  # skip certain keys as CLI arguments
389
- _skip_keys = ["compute_pool"]
398
+ _skip_keys = ["compute_pool", "hostname_resolution_timeout"]
390
399
  # --namespace is used to specify Metaflow namespace (a different
391
400
  # concept from k8s namespace).
392
401
  for k, v in self.attributes.items():
@@ -478,7 +487,9 @@ class KubernetesDecorator(StepDecorator):
478
487
  num_parallel = flow._parallel_ubf_iter.num_parallel
479
488
 
480
489
  if num_parallel and num_parallel > 1:
481
- _setup_multinode_environment()
490
+ _setup_multinode_environment(
491
+ ubf_context, self.attributes["hostname_resolution_timeout"]
492
+ )
482
493
  # current.parallel.node_index will be correctly available over here.
483
494
  meta.update({"parallel-node-index": current.parallel.node_index})
484
495
  if ubf_context == UBF_CONTROL:
@@ -542,18 +553,44 @@ class KubernetesDecorator(StepDecorator):
542
553
 
543
554
 
544
555
  # TODO: Unify this method with the multi-node setup in @batch
545
- def _setup_multinode_environment():
546
- # TODO [FIXME SOON]
547
- # Even if Kubernetes may deploy control pods before worker pods, there is always a
548
- # possibility that the worker pods may start before the control. In the case that this happens,
549
- # the worker pods will not be able to resolve the control pod's IP address and this will cause
550
- # the worker pods to fail. This function should account for this in the near future.
556
+ def _setup_multinode_environment(ubf_context, hostname_resolution_timeout):
551
557
  import socket
552
558
 
559
+ def _wait_for_hostname_resolution(max_wait_timeout=10 * 60):
560
+ """
561
+ keep trying to resolve the hostname of the control task until the hostname is resolved
562
+ or the max_wait_timeout is reached. This is a workaround for the issue where the control
563
+ task is not scheduled before the worker task and the worker task fails because it cannot
564
+ resolve the hostname of the control task.
565
+ """
566
+ start_time = time.time()
567
+ while True:
568
+ try:
569
+ return socket.gethostbyname(os.environ["MF_MASTER_ADDR"])
570
+ except socket.gaierror:
571
+ if time.time() - start_time > max_wait_timeout:
572
+ raise MetaflowException(
573
+ "Failed to get host by name for MF_MASTER_ADDR after waiting for {} seconds.".format(
574
+ max_wait_timeout
575
+ )
576
+ )
577
+ time.sleep(1)
578
+
553
579
  try:
554
- os.environ["MF_PARALLEL_MAIN_IP"] = socket.gethostbyname(
555
- os.environ["MF_MASTER_ADDR"]
556
- )
580
+ # Even if Kubernetes may deploy control pods before worker pods, there is always a
581
+ # possibility that the worker pods may start before the control. In the case that this happens,
582
+ # the worker pods will not be able to resolve the control pod's IP address and this will cause
583
+ # the worker pods to fail. So if the worker pods are requesting a hostname resolution, we will
584
+ # make it wait for the name to be resolved within a reasonable timeout period.
585
+ if ubf_context != UBF_CONTROL:
586
+ os.environ["MF_PARALLEL_MAIN_IP"] = _wait_for_hostname_resolution(
587
+ hostname_resolution_timeout
588
+ )
589
+ else:
590
+ os.environ["MF_PARALLEL_MAIN_IP"] = socket.gethostbyname(
591
+ os.environ["MF_MASTER_ADDR"]
592
+ )
593
+
557
594
  os.environ["MF_PARALLEL_NUM_NODES"] = os.environ["MF_WORLD_SIZE"]
558
595
  os.environ["MF_PARALLEL_NODE_INDEX"] = (
559
596
  str(0)
@@ -13,18 +13,19 @@ class ParallelDecorator(StepDecorator):
13
13
  MF Add To Current
14
14
  -----------------
15
15
  parallel -> metaflow.metaflow_current.Parallel
16
+ Returns a namedtuple with relevant information about the parallel task.
16
17
 
17
18
  @@ Returns
18
19
  -------
19
20
  Parallel
20
21
  `namedtuple` with the following fields:
21
- - main_ip : str
22
+ - main_ip (`str`)
22
23
  The IP address of the control task.
23
- - num_nodes : int
24
+ - num_nodes (`int`)
24
25
  The total number of tasks created by @parallel
25
- - node_index : int
26
+ - node_index (`int`)
26
27
  The index of the current task in all the @parallel tasks.
27
- - control_task_id : Optional[str]
28
+ - control_task_id (`Optional[str]`)
28
29
  The task ID of the control task. Available to all tasks.
29
30
 
30
31
  is_parallel -> bool
@@ -269,11 +269,19 @@ class CondaEnvironment(MetaflowEnvironment):
269
269
  # Resolve `linux-64` Conda environments if @batch or @kubernetes are in play
270
270
  target_platform = conda_platform()
271
271
  for decorator in step.decorators:
272
- # TODO: rather than relying on decorator names, rely on attributes
273
- # to make them extensible.
274
- if decorator.name in ["batch", "kubernetes", "nvidia", "snowpark", "slurm"]:
272
+ # NOTE: Keep the list of supported decorator names for backward compatibility purposes.
273
+ # Older versions did not implement the 'support_conda_environment' attribute.
274
+ if getattr(
275
+ decorator, "supports_conda_environment", False
276
+ ) or decorator.name in [
277
+ "batch",
278
+ "kubernetes",
279
+ "nvidia",
280
+ "snowpark",
281
+ "slurm",
282
+ ]:
275
283
  # TODO: Support arm architectures
276
- target_platform = "linux-64"
284
+ target_platform = getattr(decorator, "target_platform", "linux-64")
277
285
  break
278
286
 
279
287
  environment["conda"]["platforms"] = [target_platform]
@@ -270,15 +270,22 @@ class Micromamba(object):
270
270
  except ValueError:
271
271
  vpkg_version = None
272
272
  raise MicromambaException(
273
- "Please set the environment variable CONDA_OVERRIDE_{var} to a specific version{version} of {name}.\n"
274
- "Here is an example of supplying environment variables through the command line -\n\n"
273
+ "{msg}\n\n"
274
+ "*Please set the environment variable CONDA_OVERRIDE_{var} to a specific version{version} of {name}.*\n\n"
275
+ "Here is an example of supplying environment variables through the command line\n"
275
276
  "CONDA_OVERRIDE_{var}=<{name}-version> python flow.py <args>".format(
277
+ msg=msg.format(
278
+ cmd=" ".join(e.cmd),
279
+ code=e.returncode,
280
+ output=e.output.decode(),
281
+ stderr=error,
282
+ ),
276
283
  var=vpkg_name.upper(),
277
284
  version=(
278
- "" if not vpkg_version else (" (%s)" % vpkg_version)
285
+ "" if not vpkg_version else f" ({vpkg_version})"
279
286
  ),
280
287
  name=vpkg_name,
281
- ),
288
+ )
282
289
  )
283
290
  err.append(error)
284
291
  raise MicromambaException(
@@ -140,7 +140,10 @@ class Pip(object):
140
140
  metadata_file = METADATA_FILE.format(prefix=prefix)
141
141
  # download packages only if they haven't ever been downloaded before
142
142
  if os.path.isfile(metadata_file):
143
- return
143
+ with open(metadata_file, "r") as file:
144
+ metadata = json.load(file)
145
+ if all(package["url"] in metadata for package in packages):
146
+ return
144
147
 
145
148
  metadata = {}
146
149
  custom_index_url, extra_index_urls = self.indices(prefix)
@@ -8,7 +8,7 @@ from typing import Dict, Iterator, Optional, Tuple
8
8
 
9
9
  from metaflow import Run, metadata
10
10
 
11
- from .utils import handle_timeout, clear_and_set_os_environ
11
+ from .utils import handle_timeout
12
12
  from .subprocess_manager import CommandManager, SubprocessManager
13
13
 
14
14
 
@@ -249,8 +249,7 @@ class Runner(object):
249
249
  self.flow_file = flow_file
250
250
  self.show_output = show_output
251
251
 
252
- self.old_env = os.environ.copy()
253
- self.env_vars = self.old_env.copy()
252
+ self.env_vars = os.environ.copy()
254
253
  self.env_vars.update(env or {})
255
254
  if profile:
256
255
  self.env_vars["METAFLOW_PROFILE"] = profile
@@ -268,22 +267,12 @@ class Runner(object):
268
267
  return self
269
268
 
270
269
  def __get_executing_run(self, tfp_runner_attribute, command_obj):
271
- # When two 'Runner' executions are done sequentially i.e. one after the other
272
- # the 2nd run kinda uses the 1st run's previously set metadata and
273
- # environment variables.
274
-
275
- # It is thus necessary to set them to correct values before we return
276
- # the Run object.
277
-
278
270
  content = handle_timeout(
279
271
  tfp_runner_attribute, command_obj, self.file_read_timeout
280
272
  )
281
273
  content = json.loads(content)
282
274
  pathspec = "%s/%s" % (content.get("flow_name"), content.get("run_id"))
283
275
 
284
- # Set the environment variables to what they were before the run executed.
285
- clear_and_set_os_environ(self.old_env)
286
-
287
276
  # Set the correct metadata from the runner_attribute file corresponding to this run.
288
277
  metadata_for_flow = content.get("metadata")
289
278
  metadata(metadata_for_flow)
@@ -5,8 +5,6 @@ from typing import Dict, Optional
5
5
  from metaflow import Deployer
6
6
  from metaflow.runner.utils import get_current_cell, format_flowfile
7
7
 
8
- DEFAULT_DIR = tempfile.gettempdir()
9
-
10
8
 
11
9
  class NBDeployerInitializationError(Exception):
12
10
  """Custom exception for errors during NBDeployer initialization."""
@@ -46,8 +44,8 @@ class NBDeployer(object):
46
44
  Additional environment variables to set. This overrides the
47
45
  environment set for this process.
48
46
  base_dir : Optional[str], default None
49
- The directory to run the subprocess in; if not specified, a temporary
50
- directory is used.
47
+ The directory to run the subprocess in; if not specified, the current
48
+ working directory is used.
51
49
  **kwargs : Any
52
50
  Additional arguments that you would pass to `python myflow.py` i.e. options
53
51
  listed in `python myflow.py --help`
@@ -60,7 +58,7 @@ class NBDeployer(object):
60
58
  show_output: bool = True,
61
59
  profile: Optional[str] = None,
62
60
  env: Optional[Dict] = None,
63
- base_dir: str = DEFAULT_DIR,
61
+ base_dir: Optional[str] = None,
64
62
  file_read_timeout: int = 3600,
65
63
  **kwargs,
66
64
  ):
@@ -78,7 +76,7 @@ class NBDeployer(object):
78
76
  self.show_output = show_output
79
77
  self.profile = profile
80
78
  self.env = env
81
- self.cwd = base_dir
79
+ self.cwd = base_dir if base_dir is not None else os.getcwd()
82
80
  self.file_read_timeout = file_read_timeout
83
81
  self.top_level_kwargs = kwargs
84
82
 
metaflow/runner/nbrun.py CHANGED
@@ -5,8 +5,6 @@ from typing import Dict, Optional
5
5
  from metaflow import Runner
6
6
  from metaflow.runner.utils import get_current_cell, format_flowfile
7
7
 
8
- DEFAULT_DIR = tempfile.gettempdir()
9
-
10
8
 
11
9
  class NBRunnerInitializationError(Exception):
12
10
  """Custom exception for errors during NBRunner initialization."""
@@ -43,8 +41,8 @@ class NBRunner(object):
43
41
  Additional environment variables to set for the Run. This overrides the
44
42
  environment set for this process.
45
43
  base_dir : Optional[str], default None
46
- The directory to run the subprocess in; if not specified, a temporary
47
- directory is used.
44
+ The directory to run the subprocess in; if not specified, the current
45
+ working directory is used.
48
46
  file_read_timeout : int, default 3600
49
47
  The timeout until which we try to read the runner attribute file.
50
48
  **kwargs : Any
@@ -59,7 +57,7 @@ class NBRunner(object):
59
57
  show_output: bool = True,
60
58
  profile: Optional[str] = None,
61
59
  env: Optional[Dict] = None,
62
- base_dir: str = DEFAULT_DIR,
60
+ base_dir: Optional[str] = None,
63
61
  file_read_timeout: int = 3600,
64
62
  **kwargs,
65
63
  ):
@@ -84,7 +82,7 @@ class NBRunner(object):
84
82
  if profile:
85
83
  self.env_vars["METAFLOW_PROFILE"] = profile
86
84
 
87
- self.base_dir = base_dir
85
+ self.base_dir = base_dir if base_dir is not None else os.getcwd()
88
86
  self.file_read_timeout = file_read_timeout
89
87
 
90
88
  if not self.cell:
metaflow/runner/utils.py CHANGED
@@ -36,11 +36,6 @@ def format_flowfile(cell):
36
36
  return "\n".join(lines)
37
37
 
38
38
 
39
- def clear_and_set_os_environ(env: Dict):
40
- os.environ.clear()
41
- os.environ.update(env)
42
-
43
-
44
39
  def check_process_status(command_obj: "CommandManager"):
45
40
  if isinstance(command_obj.process, asyncio.subprocess.Process):
46
41
  return command_obj.process.returncode is not None
metaflow/version.py CHANGED
@@ -1 +1 @@
1
- metaflow_version = "2.12.24"
1
+ metaflow_version = "2.12.26"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: metaflow
3
- Version: 2.12.24
3
+ Version: 2.12.26
4
4
  Summary: Metaflow: More Data Science, Less Engineering
5
5
  Author: Metaflow Developers
6
6
  Author-email: help@metaflow.org
@@ -13,7 +13,6 @@ Classifier: License :: OSI Approved :: Apache Software License
13
13
  Classifier: Operating System :: MacOS :: MacOS X
14
14
  Classifier: Operating System :: POSIX :: Linux
15
15
  Classifier: Programming Language :: Python :: 3
16
- Classifier: Programming Language :: Python :: 3.5
17
16
  Classifier: Programming Language :: Python :: 3.6
18
17
  Classifier: Programming Language :: Python :: 3.7
19
18
  Classifier: Programming Language :: Python :: 3.8
@@ -21,12 +20,13 @@ Classifier: Programming Language :: Python :: 3.9
21
20
  Classifier: Programming Language :: Python :: 3.10
22
21
  Classifier: Programming Language :: Python :: 3.11
23
22
  Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
24
  Description-Content-Type: text/markdown
25
25
  License-File: LICENSE
26
26
  Requires-Dist: requests
27
27
  Requires-Dist: boto3
28
28
  Provides-Extra: stubs
29
- Requires-Dist: metaflow-stubs==2.12.24; extra == "stubs"
29
+ Requires-Dist: metaflow-stubs==2.12.26; extra == "stubs"
30
30
 
31
31
  ![Metaflow_Logo_Horizontal_FullColor_Ribbon_Dark_RGB](https://user-images.githubusercontent.com/763451/89453116-96a57e00-d713-11ea-9fa6-82b29d4d6eff.png)
32
32
 
@@ -36,7 +36,7 @@ metaflow/tuple_util.py,sha256=_G5YIEhuugwJ_f6rrZoelMFak3DqAR2tt_5CapS1XTY,830
36
36
  metaflow/unbounded_foreach.py,sha256=p184WMbrMJ3xKYHwewj27ZhRUsSj_kw1jlye5gA9xJk,387
37
37
  metaflow/util.py,sha256=olAvJK3y1it_k99MhLulTaAJo7OFVt5rnrD-ulIFLCU,13616
38
38
  metaflow/vendor.py,sha256=FchtA9tH22JM-eEtJ2c9FpUdMn8sSb1VHuQS56EcdZk,5139
39
- metaflow/version.py,sha256=aKoCkdWC0pmB0cMALP7kB7T-p3E8ndEyTa6kxOX4K5A,29
39
+ metaflow/version.py,sha256=9siD1BzQzD2FQqYjzbXjauLlnjauWCJOE_yl0frkMyY,29
40
40
  metaflow/_vendor/__init__.py,sha256=y_CiwUD3l4eAKvTVDZeqgVujMy31cAM1qjAB-HfI-9s,353
41
41
  metaflow/_vendor/typing_extensions.py,sha256=0nUs5p1A_UrZigrAVBoOEM6TxU37zzPDUtiij1ZwpNc,110417
42
42
  metaflow/_vendor/zipp.py,sha256=ajztOH-9I7KA_4wqDYygtHa6xUBVZgFpmZ8FE74HHHI,8425
@@ -111,7 +111,7 @@ metaflow/_vendor/v3_6/importlib_metadata/_meta.py,sha256=_F48Hu_jFxkfKWz5wcYS8vO
111
111
  metaflow/_vendor/v3_6/importlib_metadata/_text.py,sha256=HCsFksZpJLeTP3NEk_ngrAeXVRRtTrtyh9eOABoRP4A,2166
112
112
  metaflow/_vendor/v3_6/importlib_metadata/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
113
113
  metaflow/client/__init__.py,sha256=1GtQB4Y_CBkzaxg32L1syNQSlfj762wmLrfrDxGi1b8,226
114
- metaflow/client/core.py,sha256=vDRmLhoRXOfFiIplY2Xp3Go5lnn-CKeJdPqtOjWIX4Y,74173
114
+ metaflow/client/core.py,sha256=2MXZu8W1URI3ZbKW1njWCHhOu8qvthg8PcXZI4Pi9oo,74255
115
115
  metaflow/client/filecache.py,sha256=Wy0yhhCqC1JZgebqi7z52GCwXYnkAqMZHTtxThvwBgM,15229
116
116
  metaflow/cmd/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
117
117
  metaflow/cmd/configure_cmd.py,sha256=o-DKnUf2FBo_HiMVyoyzQaGBSMtpbEPEdFTQZ0hkU-k,33396
@@ -151,7 +151,7 @@ metaflow/plugins/environment_decorator.py,sha256=6m9j2B77d-Ja_l_9CTJ__0O6aB2a8Qt
151
151
  metaflow/plugins/events_decorator.py,sha256=c2GcH6Mspbey3wBkjM5lqxaNByFOzYDQdllLpXzRNv8,18283
152
152
  metaflow/plugins/logs_cli.py,sha256=77W5UNagU2mOKSMMvrQxQmBLRzvmjK-c8dWxd-Ygbqs,11410
153
153
  metaflow/plugins/package_cli.py,sha256=-J6D4cupHfWSZ4GEFo2yy9Je9oL3owRWm5pEJwaiqd4,1649
154
- metaflow/plugins/parallel_decorator.py,sha256=hRYTdw3iJjxBEws2rtrZaCHojOaDoO0Pu08ckl-Z5bU,8876
154
+ metaflow/plugins/parallel_decorator.py,sha256=aAM5jHQguADrTdnFu878FxHynOdc3zdye2G98X65HIw,8964
155
155
  metaflow/plugins/project_decorator.py,sha256=eJOe0Ea7CbUCReEhR_XQvRkhV6jyRqDxM72oZI7EMCk,5336
156
156
  metaflow/plugins/resources_decorator.py,sha256=3MMZ7uptDf99795_RcSOq4h0N3OFlKpd3ahIEsozBBs,1333
157
157
  metaflow/plugins/retry_decorator.py,sha256=tz_2Tq6GLg3vjDBZp0KKVTk3ADlCvqaWTSf7blmFdUw,1548
@@ -173,10 +173,10 @@ metaflow/plugins/airflow/sensors/base_sensor.py,sha256=s-OQBfPWZ_T3wn96Ua59CCEj1
173
173
  metaflow/plugins/airflow/sensors/external_task_sensor.py,sha256=zhYlrZnXT20KW8-fVk0fCNtTyNiKJB5PMVASacu30r0,6034
174
174
  metaflow/plugins/airflow/sensors/s3_sensor.py,sha256=iDReG-7FKnumrtQg-HY6cCUAAqNA90nARrjjjEEk_x4,3275
175
175
  metaflow/plugins/argo/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
176
- metaflow/plugins/argo/argo_client.py,sha256=KTUpP0DmnmNsMp4tbdNyKX_zOdTFRVpUkrf7Vv79d-o,16011
176
+ metaflow/plugins/argo/argo_client.py,sha256=Z_A1TO9yw4Y-a8VAlwrFS0BwunWzXpbtik-j_xjcuHE,16303
177
177
  metaflow/plugins/argo/argo_events.py,sha256=_C1KWztVqgi3zuH57pInaE9OzABc2NnncC-zdwOMZ-w,5909
178
- metaflow/plugins/argo/argo_workflows.py,sha256=2todjgM06mkqkXn4_-7ume7AknIqr3ehZNTikJU4bBM,173579
179
- metaflow/plugins/argo/argo_workflows_cli.py,sha256=0qAGo0YlC1Y9-1zqYAzhVCpCcITotfOI421VOIRpseM,37232
178
+ metaflow/plugins/argo/argo_workflows.py,sha256=9ygK_yp5YMCmcNo3TGwi562pRKXJtCw0OyRvKl-B9JQ,173669
179
+ metaflow/plugins/argo/argo_workflows_cli.py,sha256=pyuBtZfqQFs41eTOLq0xUKIJYP0iUKVivLkEhyVARHk,36677
180
180
  metaflow/plugins/argo/argo_workflows_decorator.py,sha256=yprszMdbE3rBTcEA9VR0IEnPjTprUauZBc4SBb-Q7sA,7878
181
181
  metaflow/plugins/argo/argo_workflows_deployer.py,sha256=wSSZtThn_VPvE_Wu6NB1L0Q86LmBJh9g009v_lpvBPM,8125
182
182
  metaflow/plugins/argo/capture_error.py,sha256=Ys9dscGrTpW-ZCirLBU0gD9qBM0BjxyxGlUMKcwewQc,1852
@@ -189,7 +189,7 @@ metaflow/plugins/aws/batch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
189
189
  metaflow/plugins/aws/batch/batch.py,sha256=e9ssahWM18GnipPK2sqYB-ztx9w7Eoo7YtWyEtufYxs,17787
190
190
  metaflow/plugins/aws/batch/batch_cli.py,sha256=6PTbyajRgdy0XmjyJLBTdKdiOB84dcovQQ8sFXlJqko,11749
191
191
  metaflow/plugins/aws/batch/batch_client.py,sha256=ddlGG0Vk1mkO7tcvJjDvNAVsVLOlqddF7MA1kKfHSqM,28830
192
- metaflow/plugins/aws/batch/batch_decorator.py,sha256=kwgxEPCEoI6eZIpU5PuL442Ohg4_BfvwowoYgAnCzKE,17520
192
+ metaflow/plugins/aws/batch/batch_decorator.py,sha256=lxFbXATAjzfCop6ks1U3CiK2kW-mPC8tk6a75MX15p0,17624
193
193
  metaflow/plugins/aws/secrets_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
194
194
  metaflow/plugins/aws/secrets_manager/aws_secrets_manager_secrets_provider.py,sha256=JtFUVu00Cg0FzAizgrPLXmrMqsT7YeQMkQlgeivUxcE,7986
195
195
  metaflow/plugins/aws/step_functions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -207,7 +207,7 @@ metaflow/plugins/azure/__init__.py,sha256=GuuhTVC-zSdyAf79a1wiERMq0Zts7fwVT7t9fA
207
207
  metaflow/plugins/azure/azure_credential.py,sha256=JmdGEbVzgxy8ucqnQDdTTI_atyMX9WSZUw3qYOo7RhE,2174
208
208
  metaflow/plugins/azure/azure_exceptions.py,sha256=NnbwpUC23bc61HZjJmeXztY0tBNn_Y_VpIpDDuYWIZ0,433
209
209
  metaflow/plugins/azure/azure_secret_manager_secrets_provider.py,sha256=GCPhzN9ldv1haD2W5swO2IxCDSqK57mRUNPyo2JwDAs,11107
210
- metaflow/plugins/azure/azure_tail.py,sha256=JAqV4mC42bMpR0O7m6X4cpFuh0peV1ufs_jJXrmicTc,3362
210
+ metaflow/plugins/azure/azure_tail.py,sha256=hlF6bZkqarSIDjNU1mSFOf7tPb2GZrLcJXKgngIzLls,3375
211
211
  metaflow/plugins/azure/azure_utils.py,sha256=j3kAxi2oC-fMpw8YegJvqsAwxi_m7jGPxCaeVwoBZJg,7100
212
212
  metaflow/plugins/azure/blob_service_client_factory.py,sha256=MtyPftBxrXdXMxwhKgLepG6mtlb_2BhJLG_fvbO6D14,6527
213
213
  metaflow/plugins/azure/includefile_support.py,sha256=Wv3g3RlGtLbxAh3Reg0BDLWwqavYibQNCDWddlH7XCE,4706
@@ -216,7 +216,7 @@ metaflow/plugins/cards/card_cli.py,sha256=fZpPdvcybUOXO2DXsTKgZmbzNh6JUw4Yi42oi9
216
216
  metaflow/plugins/cards/card_client.py,sha256=30dFBoC3axc261GeV7QCIs_V1OHhRtS31S0wEWsjw90,9501
217
217
  metaflow/plugins/cards/card_creator.py,sha256=E_NCmWPK6DzkqigtpUpeddCDbjnKF6dJcE6IvWzwiyA,7740
218
218
  metaflow/plugins/cards/card_datastore.py,sha256=3K19wE0CZVvOpuYUytftIYYnHHn3pMZJE87FMD6OYlM,14244
219
- metaflow/plugins/cards/card_decorator.py,sha256=J52zv9qQDEIEC6jIAFGOAj_NoZNav0RsgnsqoI00gMw,9213
219
+ metaflow/plugins/cards/card_decorator.py,sha256=rrPcWzxmN8sKprBsPVr8Ds0X6LEVq90_MimzBws0x0Q,9251
220
220
  metaflow/plugins/cards/card_resolver.py,sha256=bjyujYpGUFbLJNwXNGHlHhL4f-gVdVKebl7XW1vWDtE,717
221
221
  metaflow/plugins/cards/card_server.py,sha256=DvVW0URhmiyfMVDKQoi9UXAz16vd-upmp2l9WWRriWg,10841
222
222
  metaflow/plugins/cards/component_serializer.py,sha256=Row7c_8_euJcF_I1lWHdgMRj7dvAb69O-jZTmxS9h8k,37223
@@ -275,15 +275,15 @@ metaflow/plugins/gcp/__init__.py,sha256=bmoHLvLtawvFT9DSuWziToAttNRfbinOkrZZePWf
275
275
  metaflow/plugins/gcp/gcp_secret_manager_secrets_provider.py,sha256=4551ReVCIWFd1wugU6r4aQ-5ovLHp5kRtfT9YCt67FY,7258
276
276
  metaflow/plugins/gcp/gs_exceptions.py,sha256=NfqKmnPpNJ8nxA0CnPPjcO65SAQ2lhCrhM2dz5x9eCQ,184
277
277
  metaflow/plugins/gcp/gs_storage_client_factory.py,sha256=LIzc5bAzIOmaMhoXUtRkYaEJPEEy2suMv8uuNViC5Ug,2110
278
- metaflow/plugins/gcp/gs_tail.py,sha256=Jl_wvnzU7dub07A-DOAuP5FeccNIrPM-CeL1xKFs1nQ,3034
278
+ metaflow/plugins/gcp/gs_tail.py,sha256=qz0QZKT-5LvL8qgZZK2yyMOwuEnx1YOz-pTSAUmwv4k,3418
279
279
  metaflow/plugins/gcp/gs_utils.py,sha256=ZmIGFse1qYyvAVrwga23PQUzF6dXEDLLsZ2F-YRmvow,2030
280
280
  metaflow/plugins/gcp/includefile_support.py,sha256=vIDeR-MiJuUh-2S2pV7Z7FBkhIWwtHXaRrj76MWGRiY,3869
281
281
  metaflow/plugins/kubernetes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
282
282
  metaflow/plugins/kubernetes/kube_utils.py,sha256=fYDlvqi8jYPsWijDwT6Z2qhQswyFqv7tiwtic_I80Vg,749
283
- metaflow/plugins/kubernetes/kubernetes.py,sha256=bKBqgZXnIDkoa4xKtKoV6InPtYQy4CujfvcbQ3Pvsbc,31305
283
+ metaflow/plugins/kubernetes/kubernetes.py,sha256=_cq_4N8l40cP0kifYMDAL8Te0DnIKUhVCqee3covwdM,31642
284
284
  metaflow/plugins/kubernetes/kubernetes_cli.py,sha256=sFZ9Zrjef85vCO0MGpUF-em8Pw3dePFb3hbX3PtAH4I,13463
285
285
  metaflow/plugins/kubernetes/kubernetes_client.py,sha256=tuvXP-QKpdeSmzVolB2R_TaacOr5DIb0j642eKcjsiM,6491
286
- metaflow/plugins/kubernetes/kubernetes_decorator.py,sha256=xz2tEIapYWMd9rRiOe8qcYvjRIKT5piWnq-twdySpD8,26031
286
+ metaflow/plugins/kubernetes/kubernetes_decorator.py,sha256=p7-6dyzxoMWKkuCqW6JvvCs_dZUgnBTOchZGIRS_yjo,27820
287
287
  metaflow/plugins/kubernetes/kubernetes_job.py,sha256=Cfkee8LbXC17jSXWoeNdomQRvF_8YSeXNg1gvxm6E_M,31806
288
288
  metaflow/plugins/kubernetes/kubernetes_jobsets.py,sha256=wb0sK1OxW7pRbKdj6bWB4JsskXDsoKKqjyUWo4N9Y6E,41196
289
289
  metaflow/plugins/metadata/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
@@ -292,9 +292,9 @@ metaflow/plugins/metadata/service.py,sha256=ihq5F7KQZlxvYwzH_-jyP2aWN_I96i2vp92j
292
292
  metaflow/plugins/pypi/__init__.py,sha256=0YFZpXvX7HCkyBFglatual7XGifdA1RwC3U4kcizyak,1037
293
293
  metaflow/plugins/pypi/bootstrap.py,sha256=FI-itExqIz7DUzLnnkGwoB60rFBviygpIFThUtqk_4E,5227
294
294
  metaflow/plugins/pypi/conda_decorator.py,sha256=fPeXxvmg51oSFTnlguNlcWUIdXHA9OuMnp9ElaxQPFo,15695
295
- metaflow/plugins/pypi/conda_environment.py,sha256=--q-8lypKupCdGsASpqABNpNqRxtQi6UCDgq8iHDFe4,19476
296
- metaflow/plugins/pypi/micromamba.py,sha256=HQIxsixkLjqs0ukWGTlATNu5DrbisReOr39Qd21_GZo,13737
297
- metaflow/plugins/pypi/pip.py,sha256=7B06mPOs5MvY33xbzPVYZlBr1iKMYaN-n8uulL9zSVg,13649
295
+ metaflow/plugins/pypi/conda_environment.py,sha256=IGHIphHm1e8UEJX-PvyTesfKRCpxtJIc1pxJ5Wen-aU,19765
296
+ metaflow/plugins/pypi/micromamba.py,sha256=QaZYMy5w4esW2w_Lb9kZdWU07EtZD_Ky00MVlA4FJw0,14079
297
+ metaflow/plugins/pypi/pip.py,sha256=Uewmt6-meLyPhNLiAOAkDdfd1P4Go07bkQUD0uE5VIs,13827
298
298
  metaflow/plugins/pypi/pypi_decorator.py,sha256=rDMbHl7r81Ye7-TuIlKAVJ_CDnfjl9jV44ZPws-UsTY,7229
299
299
  metaflow/plugins/pypi/pypi_environment.py,sha256=FYMg8kF3lXqcLfRYWD83a9zpVjcoo_TARqMGZ763rRk,230
300
300
  metaflow/plugins/pypi/utils.py,sha256=ds1Mnv_DaxGnLAYp7ozg_K6oyguGyNhvHfE-75Ia1YA,2836
@@ -304,11 +304,11 @@ metaflow/plugins/secrets/secrets_decorator.py,sha256=s-sFzPWOjahhpr5fMj-ZEaHkDYA
304
304
  metaflow/runner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
305
305
  metaflow/runner/click_api.py,sha256=Qfg4BOz5K2LaXTYBsi1y4zTfNIsGGHBVF3UkorX_-o8,13878
306
306
  metaflow/runner/deployer.py,sha256=xNqxFIezWz3AcVqR4jeL-JnInGtefwEYMbXttxgB_I8,12276
307
- metaflow/runner/metaflow_runner.py,sha256=I7Ao0GHHfP55nUCE8g6CpTPGjuWgXedSc9EZX-tIE2c,15001
308
- metaflow/runner/nbdeploy.py,sha256=fP1s_5MeiDyT_igP82pB5EUqX9rOy2s06Hyc-OUbOvQ,4115
309
- metaflow/runner/nbrun.py,sha256=lmvhzMCz7iC9LSPGRijifW1wMXxa4RW_jVmpdjQi22E,7261
307
+ metaflow/runner/metaflow_runner.py,sha256=V7ZZfVIK6KUTdV_k1eYc6F2WST4x3Qj-LbrIwr8tuuw,14501
308
+ metaflow/runner/nbdeploy.py,sha256=lCCeSh_qKVA-LNMwmh3DtEPEfGRYNaLNcwldCF2JzXg,4130
309
+ metaflow/runner/nbrun.py,sha256=yGOSux8rs97Cix9LsjfPrSgV4rDmiyopjquV8GoCT7s,7276
310
310
  metaflow/runner/subprocess_manager.py,sha256=jC_PIYIeAp_G__lf6WHZF3Lxzpp-WAQleMrRZq9j7nc,20467
311
- metaflow/runner/utils.py,sha256=aQ6WiNz9b-pqWWE14PdcAqti7_Zh_MIPlEA8zXJ6tXo,3807
311
+ metaflow/runner/utils.py,sha256=o21_LcILx0F7EbdOK01y5SceJRyxo6qFv57S59Lt-Tc,3714
312
312
  metaflow/sidecar/__init__.py,sha256=1mmNpmQ5puZCpRmmYlCOeieZ4108Su9XQ4_EqF1FGOU,131
313
313
  metaflow/sidecar/sidecar.py,sha256=EspKXvPPNiyRToaUZ51PS5TT_PzrBNAurn_wbFnmGr0,1334
314
314
  metaflow/sidecar/sidecar_messages.py,sha256=zPsCoYgDIcDkkvdC9MEpJTJ3y6TSGm2JWkRc4vxjbFA,1071
@@ -345,9 +345,9 @@ metaflow/tutorials/07-worldview/README.md,sha256=5vQTrFqulJ7rWN6r20dhot9lI2sVj9W
345
345
  metaflow/tutorials/07-worldview/worldview.ipynb,sha256=ztPZPI9BXxvW1QdS2Tfe7LBuVzvFvv0AToDnsDJhLdE,2237
346
346
  metaflow/tutorials/08-autopilot/README.md,sha256=GnePFp_q76jPs991lMUqfIIh5zSorIeWznyiUxzeUVE,1039
347
347
  metaflow/tutorials/08-autopilot/autopilot.ipynb,sha256=DQoJlILV7Mq9vfPBGW-QV_kNhWPjS5n6SJLqePjFYLY,3191
348
- metaflow-2.12.24.dist-info/LICENSE,sha256=nl_Lt5v9VvJ-5lWJDT4ddKAG-VZ-2IaLmbzpgYDz2hU,11343
349
- metaflow-2.12.24.dist-info/METADATA,sha256=mMbMe3DKXaF8_eKtdOrjRSGx_n2Ui1kjXsgnZvcHfTQ,5906
350
- metaflow-2.12.24.dist-info/WHEEL,sha256=AHX6tWk3qWuce7vKLrj7lnulVHEdWoltgauo8bgCXgU,109
351
- metaflow-2.12.24.dist-info/entry_points.txt,sha256=IKwTN1T3I5eJL3uo_vnkyxVffcgnRdFbKwlghZfn27k,57
352
- metaflow-2.12.24.dist-info/top_level.txt,sha256=v1pDHoWaSaKeuc5fKTRSfsXCKSdW1zvNVmvA-i0if3o,9
353
- metaflow-2.12.24.dist-info/RECORD,,
348
+ metaflow-2.12.26.dist-info/LICENSE,sha256=nl_Lt5v9VvJ-5lWJDT4ddKAG-VZ-2IaLmbzpgYDz2hU,11343
349
+ metaflow-2.12.26.dist-info/METADATA,sha256=HE5V3uJroEqQpaYjoVfk1VEnE2vjwq5nasw6OQ44yCA,5907
350
+ metaflow-2.12.26.dist-info/WHEEL,sha256=TJ49d73sNs10F0aze1W_bTW2P_X7-F4YXOlBqoqA-jY,109
351
+ metaflow-2.12.26.dist-info/entry_points.txt,sha256=IKwTN1T3I5eJL3uo_vnkyxVffcgnRdFbKwlghZfn27k,57
352
+ metaflow-2.12.26.dist-info/top_level.txt,sha256=v1pDHoWaSaKeuc5fKTRSfsXCKSdW1zvNVmvA-i0if3o,9
353
+ metaflow-2.12.26.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.1.0)
2
+ Generator: setuptools (75.2.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py2-none-any
5
5
  Tag: py3-none-any