ob-metaflow 2.16.4.5__py2.py3-none-any.whl → 2.16.4.6__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.

Potentially problematic release.


This version of ob-metaflow might be problematic. Click here for more details.

metaflow/client/core.py CHANGED
@@ -831,10 +831,12 @@ class MetaflowCode(object):
831
831
  )
832
832
  self._code_obj = BytesIO(blobdata)
833
833
  self._info = MetaflowPackage.cls_get_info(self._code_metadata, self._code_obj)
834
+ self._code_obj.seek(0)
834
835
  if self._info:
835
836
  self._flowspec = MetaflowPackage.cls_get_content(
836
837
  self._code_metadata, self._code_obj, self._info["script"]
837
838
  )
839
+ self._code_obj.seek(0)
838
840
  else:
839
841
  raise MetaflowInternalError("Code package metadata is invalid.")
840
842
 
@@ -885,7 +887,9 @@ class MetaflowCode(object):
885
887
  TarFile for everything in this code package
886
888
  """
887
889
  if self._backend.type == "tgz":
888
- return self._backend.cls_open(self._code_obj)
890
+ to_return = self._backend.cls_open(self._code_obj)
891
+ self._code_obj.seek(0)
892
+ return to_return
889
893
  raise RuntimeError("Archive is not a tarball")
890
894
 
891
895
  def extract(self) -> TemporaryDirectory:
@@ -921,6 +925,7 @@ class MetaflowCode(object):
921
925
  MetaflowPackage.cls_extract_into(
922
926
  self._code_metadata, self._code_obj, tmp.name, ContentType.USER_CONTENT
923
927
  )
928
+ self._code_obj.seek(0)
924
929
  return tmp
925
930
 
926
931
  @property
@@ -205,9 +205,10 @@ def package_mfext_all():
205
205
  # the packaged metaflow_extensions directory "self-contained" so that
206
206
  # python doesn't go and search other parts of the system for more
207
207
  # metaflow_extensions.
208
- yield os.path.join(
209
- os.path.dirname(os.path.abspath(__file__)), "_empty_file.py"
210
- ), os.path.join(EXT_PKG, "__init__.py")
208
+ if _all_packages:
209
+ yield os.path.join(
210
+ os.path.dirname(os.path.abspath(__file__)), "_empty_file.py"
211
+ ), os.path.join(EXT_PKG, "__init__.py")
211
212
 
212
213
  for p in _all_packages:
213
214
  for path_tuple in package_mfext_package(p):
@@ -203,6 +203,19 @@ class MetaflowEnvironment(object):
203
203
  "mfcontent_version": 1,
204
204
  }
205
205
  )
206
+
207
+ extra_exports = []
208
+ for k, v in MetaflowPackage.get_post_extract_env_vars(
209
+ code_package_metadata, dest_dir="$(pwd)"
210
+ ).items():
211
+ if k.endswith(":"):
212
+ # If the value ends with a colon, we override the existing value
213
+ extra_exports.append("export %s=%s" % (k[:-1], v))
214
+ else:
215
+ extra_exports.append(
216
+ "export %s=%s:$(printenv %s)" % (k, v.replace('"', '\\"'), k)
217
+ )
218
+
206
219
  cmds = (
207
220
  [
208
221
  BASH_MFLOG,
@@ -226,12 +239,7 @@ class MetaflowEnvironment(object):
226
239
  + MetaflowPackage.get_extract_commands(
227
240
  code_package_metadata, "job.tar", dest_dir="."
228
241
  )
229
- + [
230
- "export %s=%s:$(printenv %s)" % (k, v.replace('"', '\\"'), k)
231
- for k, v in MetaflowPackage.get_post_extract_env_vars(
232
- code_package_metadata, dest_dir="."
233
- ).items()
234
- ]
242
+ + extra_exports
235
243
  + [
236
244
  "mflog 'Task is starting.'",
237
245
  "flush_mflogs",
@@ -17,7 +17,6 @@ from ..packaging_sys.utils import suffix_filter, walk
17
17
  from ..metaflow_config import DEFAULT_PACKAGE_SUFFIXES
18
18
  from ..exception import MetaflowException
19
19
  from ..user_configs.config_parameters import dump_config_values
20
- from ..util import get_metaflow_root
21
20
  from .. import R
22
21
 
23
22
  DEFAULT_SUFFIXES_LIST = DEFAULT_PACKAGE_SUFFIXES.split(",")
@@ -76,12 +75,22 @@ class MetaflowPackage(object):
76
75
  from ..user_decorators.user_flow_decorator import FlowMutatorMeta
77
76
  from ..user_decorators.user_step_decorator import UserStepDecoratorMeta
78
77
 
79
- if (
80
- m.__name__ in FlowMutatorMeta._import_modules
81
- or m.__name__ in UserStepDecoratorMeta._import_modules
82
- or hasattr(m, "METAFLOW_PACKAGE")
83
- ):
84
- return True
78
+ # Be very defensive here to filter modules in case there are
79
+ # some badly behaved modules that have weird values for
80
+ # METAFLOW_PACKAGE_POLICY for example.
81
+ try:
82
+ if (
83
+ m.__name__ in FlowMutatorMeta._import_modules
84
+ or m.__name__ in UserStepDecoratorMeta._import_modules
85
+ or (
86
+ hasattr(m, "METAFLOW_PACKAGE_POLICY")
87
+ and m.METAFLOW_PACKAGE_POLICY == "include"
88
+ )
89
+ ):
90
+ return True
91
+ return False
92
+ except:
93
+ return False
85
94
 
86
95
  if mfcontent is None:
87
96
  self._mfcontent = MetaflowCodeContentV1(criteria=_module_selector)
@@ -350,10 +359,10 @@ class MetaflowPackage(object):
350
359
  """
351
360
  backend = cls.get_backend(pkg_metadata)
352
361
  with backend.cls_open(archive) as opened_archive:
353
- include_names = MetaflowCodeContent.get_archive_content_names(
362
+ include_members = MetaflowCodeContent.get_archive_content_members(
354
363
  opened_archive, content_types, backend
355
364
  )
356
- backend.extract_members(include_names, dest_dir)
365
+ backend.cls_extract_members(opened_archive, include_members, dest_dir)
357
366
 
358
367
  def user_tuples(self, timeout: Optional[float] = None):
359
368
  # Wait for at least the blob to be formed
@@ -118,9 +118,7 @@ class MetaflowCodeContent:
118
118
  return handling_cls.get_filename_impl(mfcontent_info, filename, content_type)
119
119
 
120
120
  @classmethod
121
- def get_env_vars_for_packaged_metaflow(
122
- cls, dest_dir: str
123
- ) -> Optional[Dict[str, str]]:
121
+ def get_env_vars_for_packaged_metaflow(cls, dest_dir: str) -> Dict[str, str]:
124
122
  """
125
123
  Get the environment variables that are needed to run Metaflow when it is
126
124
  packaged. This is typically used to set the PYTHONPATH to include the
@@ -128,17 +126,19 @@ class MetaflowCodeContent:
128
126
 
129
127
  Returns
130
128
  -------
131
- Optional[Dict[str, str]]
129
+ Dict[str, str]
132
130
  The environment variables that are needed to run Metaflow when it is
133
- packaged -- None if there are no such variables (not packaged for example)
131
+ packaged it present.
134
132
  """
135
- mfcontent_info = cls._extract_mfcontent_info()
133
+ mfcontent_info = cls._extract_mfcontent_info(dest_dir)
136
134
  if mfcontent_info is None:
137
135
  # No MFCONTENT_MARKER file found -- this is not a packaged Metaflow code
138
136
  # package so no environment variables to set.
139
- return None
137
+ return {}
140
138
  handling_cls = cls._get_mfcontent_class(mfcontent_info)
141
- return handling_cls.get_post_extract_env_vars_impl(dest_dir)
139
+ v = handling_cls.get_post_extract_env_vars_impl(dest_dir)
140
+ v["METAFLOW_EXTRACTED_ROOT:"] = dest_dir
141
+ return v
142
142
 
143
143
  @classmethod
144
144
  def get_archive_info(
@@ -216,15 +216,15 @@ class MetaflowCodeContent:
216
216
  )
217
217
 
218
218
  @classmethod
219
- def get_archive_content_names(
219
+ def get_archive_content_members(
220
220
  cls,
221
221
  archive: Any,
222
222
  content_types: Optional[int] = None,
223
223
  packaging_backend: Type[PackagingBackend] = TarPackagingBackend,
224
- ) -> List[str]:
224
+ ) -> List[Any]:
225
225
  mfcontent_info = cls._extract_archive_mfcontent_info(archive, packaging_backend)
226
226
  handling_cls = cls._get_mfcontent_class(mfcontent_info)
227
- return handling_cls.get_archive_content_names_impl(
227
+ return handling_cls.get_archive_content_members_impl(
228
228
  mfcontent_info, archive, content_types, packaging_backend
229
229
  )
230
230
 
@@ -276,7 +276,9 @@ class MetaflowCodeContent:
276
276
  "Invalid package -- unknown version %s in info: %s"
277
277
  % (version_id, cls._mappings)
278
278
  )
279
- return cls._mappings[version_id].get_post_extract_env_vars_impl(dest_dir)
279
+ v = cls._mappings[version_id].get_post_extract_env_vars_impl(dest_dir)
280
+ v["METAFLOW_EXTRACTED_ROOT:"] = dest_dir
281
+ return v
280
282
 
281
283
  # Implement the _impl methods in the base subclass (in this file). These need to
282
284
  # happen with as few imports as possible to prevent circular dependencies.
@@ -337,14 +339,14 @@ class MetaflowCodeContent:
337
339
  raise NotImplementedError("get_archive_filename_impl not implemented")
338
340
 
339
341
  @classmethod
340
- def get_archive_content_names_impl(
342
+ def get_archive_content_members_impl(
341
343
  cls,
342
344
  mfcontent_info: Optional[Dict[str, Any]],
343
345
  archive: Any,
344
346
  content_types: Optional[int] = None,
345
347
  packaging_backend: Type[PackagingBackend] = TarPackagingBackend,
346
- ) -> List[str]:
347
- raise NotImplementedError("get_archive_content_names_impl not implemented")
348
+ ) -> List[Any]:
349
+ raise NotImplementedError("get_archive_content_members_impl not implemented")
348
350
 
349
351
  @classmethod
350
352
  def get_post_extract_env_vars_impl(cls, dest_dir: str) -> Dict[str, str]:
@@ -523,19 +525,22 @@ class MetaflowCodeContent:
523
525
  return mfcontent_info
524
526
 
525
527
  @classmethod
526
- def _extract_mfcontent_info(cls) -> Optional[Dict[str, Any]]:
527
- if "_local" in cls._cached_mfcontent_info:
528
- return cls._cached_mfcontent_info["_local"]
528
+ def _extract_mfcontent_info(
529
+ cls, target_dir: Optional[str] = None
530
+ ) -> Optional[Dict[str, Any]]:
531
+ target_dir = target_dir or "_local"
532
+ if target_dir in cls._cached_mfcontent_info:
533
+ return cls._cached_mfcontent_info[target_dir]
529
534
 
530
535
  mfcontent_info = None # type: Optional[Dict[str, Any]]
531
- if os.path.exists(os.path.join(get_metaflow_root(), MFCONTENT_MARKER)):
532
- with open(
533
- os.path.join(get_metaflow_root(), MFCONTENT_MARKER),
534
- "r",
535
- encoding="utf-8",
536
- ) as f:
536
+ if target_dir == "_local":
537
+ root = os.environ.get("METAFLOW_EXTRACTED_ROOT", get_metaflow_root())
538
+ else:
539
+ root = target_dir
540
+ if os.path.exists(os.path.join(root, MFCONTENT_MARKER)):
541
+ with open(os.path.join(root, MFCONTENT_MARKER), "r", encoding="utf-8") as f:
537
542
  mfcontent_info = json.load(f)
538
- cls._cached_mfcontent_info["_local"] = mfcontent_info
543
+ cls._cached_mfcontent_info[target_dir] = mfcontent_info
539
544
  return mfcontent_info
540
545
 
541
546
  def get_package_version(self) -> int:
@@ -627,13 +632,13 @@ class MetaflowCodeContentV0(MetaflowCodeContent, version_id=0):
627
632
  return None
628
633
 
629
634
  @classmethod
630
- def get_archive_content_names_impl(
635
+ def get_archive_content_members_impl(
631
636
  cls,
632
637
  mfcontent_info: Optional[Dict[str, Any]],
633
638
  archive: Any,
634
639
  content_types: Optional[int] = None,
635
640
  packaging_backend: Type[PackagingBackend] = TarPackagingBackend,
636
- ) -> List[str]:
641
+ ) -> List[Any]:
637
642
  """
638
643
  For V0, we use a static list of known files to classify the content
639
644
  """
@@ -649,16 +654,20 @@ class MetaflowCodeContentV0(MetaflowCodeContent, version_id=0):
649
654
  "condav2-1.cnd": ContentType.OTHER_CONTENT.value,
650
655
  }
651
656
  to_return = []
652
- for filename in packaging_backend.cls_list_members(archive):
657
+ for member in packaging_backend.cls_list_members(archive):
658
+ filename = packaging_backend.cls_member_name(member)
659
+ added = False
653
660
  for prefix, classification in known_prefixes.items():
654
661
  if (
655
662
  prefix[-1] == "/" and filename.startswith(prefix)
656
663
  ) or prefix == filename:
657
664
  if content_types & classification:
658
- to_return.append(filename)
659
- elif content_types & ContentType.USER_CONTENT.value:
660
- # Everything else is user content
661
- to_return.append(filename)
665
+ to_return.append(member)
666
+ added = True
667
+ break
668
+ if not added and content_types & ContentType.USER_CONTENT.value:
669
+ # Everything else is user content
670
+ to_return.append(member)
662
671
  return to_return
663
672
 
664
673
  @classmethod
@@ -705,7 +714,7 @@ class MetaflowCodeContentV1Base(MetaflowCodeContent, version_id=1):
705
714
  cls, mfcontent_info: Optional[Dict[str, Any]], filename: str, in_archive: bool
706
715
  ) -> str:
707
716
  if in_archive:
708
- return filename
717
+ return os.path.join(cls._other_dir, filename)
709
718
  return os.path.join(get_metaflow_root(), "..", cls._other_dir, filename)
710
719
 
711
720
  @classmethod
@@ -713,7 +722,7 @@ class MetaflowCodeContentV1Base(MetaflowCodeContent, version_id=1):
713
722
  cls, mfcontent_info: Optional[Dict[str, Any]], filename: str, in_archive: bool
714
723
  ) -> str:
715
724
  if in_archive:
716
- return filename
725
+ return os.path.join(cls._code_dir, filename)
717
726
  return os.path.join(get_metaflow_root(), filename)
718
727
 
719
728
  @classmethod
@@ -832,37 +841,38 @@ class MetaflowCodeContentV1Base(MetaflowCodeContent, version_id=1):
832
841
  return None
833
842
 
834
843
  @classmethod
835
- def get_archive_content_names_impl(
844
+ def get_archive_content_members_impl(
836
845
  cls,
837
846
  mfcontent_info: Optional[Dict[str, Any]],
838
847
  archive: Any,
839
848
  content_types: Optional[int] = None,
840
849
  packaging_backend: Type[PackagingBackend] = TarPackagingBackend,
841
- ) -> List[str]:
850
+ ) -> List[Any]:
842
851
  to_return = []
843
852
  module_content = set(mfcontent_info.get("module_files", []))
844
- for filename in packaging_backend.cls_list_members(archive):
853
+ for member in packaging_backend.cls_list_members(archive):
854
+ filename = packaging_backend.cls_member_name(member)
845
855
  if filename.startswith(cls._other_dir) and (
846
856
  content_types & ContentType.OTHER_CONTENT.value
847
857
  ):
848
- to_return.append(filename)
858
+ to_return.append(member)
849
859
  elif filename.startswith(cls._code_dir):
850
860
  # Special case for marker which is a other content even if in code.
851
- if filename == f"{cls._code_dir}/{MFCONTENT_MARKER}":
861
+ if filename == MFCONTENT_MARKER:
852
862
  if content_types & ContentType.OTHER_CONTENT.value:
853
- to_return.append(filename)
863
+ to_return.append(member)
854
864
  else:
855
865
  continue
856
866
  # Here it is either module or code
857
867
  if os.path.join(cls._code_dir, filename) in module_content:
858
868
  if content_types & ContentType.MODULE_CONTENT.value:
859
- to_return.append(filename)
869
+ to_return.append(member)
860
870
  elif content_types & ContentType.CODE_CONTENT.value:
861
- to_return.append(filename)
871
+ to_return.append(member)
862
872
  else:
863
873
  if content_types & ContentType.USER_CONTENT.value:
864
874
  # Everything else is user content
865
- to_return.append(filename)
875
+ to_return.append(member)
866
876
  return to_return
867
877
 
868
878
  @classmethod
@@ -57,6 +57,15 @@ class PackagingBackend(ABC):
57
57
  """Open the archive from the given content."""
58
58
  pass
59
59
 
60
+ @classmethod
61
+ @abstractmethod
62
+ def cls_member_name(cls, member: Union[Any, str]) -> str:
63
+ """
64
+ Returns the name of the member as a string.
65
+ This is used to ensure consistent naming across different archive formats.
66
+ """
67
+ pass
68
+
60
69
  @classmethod
61
70
  @abstractmethod
62
71
  def cls_has_member(cls, archive: Any, name: str) -> bool:
@@ -72,14 +81,20 @@ class PackagingBackend(ABC):
72
81
  def cls_extract_members(
73
82
  cls,
74
83
  archive: Any,
75
- members: Optional[List[str]] = None,
84
+ members: Optional[List[Any]] = None,
76
85
  dest_dir: str = ".",
77
86
  ) -> None:
78
87
  pass
79
88
 
80
89
  @classmethod
81
90
  @abstractmethod
82
- def cls_list_members(cls, archive: Any) -> Optional[List[str]]:
91
+ def cls_list_names(cls, archive: Any) -> Optional[List[str]]:
92
+ pass
93
+
94
+ @classmethod
95
+ @abstractmethod
96
+ def cls_list_members(cls, archive: Any) -> Optional[List[Any]]:
97
+ """List all members in the archive."""
83
98
  pass
84
99
 
85
100
  def has_member(self, name: str) -> bool:
@@ -93,17 +108,17 @@ class PackagingBackend(ABC):
93
108
  raise ValueError("Cannot get member from an uncreated archive")
94
109
 
95
110
  def extract_members(
96
- self, members: Optional[List[str]] = None, dest_dir: str = "."
111
+ self, members: Optional[List[Any]] = None, dest_dir: str = "."
97
112
  ) -> None:
98
113
  if self._archive:
99
114
  self.cls_extract_members(self._archive, members, dest_dir)
100
115
  else:
101
116
  raise ValueError("Cannot extract from an uncreated archive")
102
117
 
103
- def list_members(self) -> Optional[List[str]]:
118
+ def list_names(self) -> Optional[List[str]]:
104
119
  if self._archive:
105
- return self.cls_list_members(self._archive)
106
- raise ValueError("Cannot list members from an uncreated archive")
120
+ return self.cls_list_names(self._archive)
121
+ raise ValueError("Cannot list names from an uncreated archive")
107
122
 
108
123
  def __enter__(self):
109
124
  self.create()
@@ -1,7 +1,7 @@
1
1
  import tarfile
2
2
 
3
3
  from io import BytesIO
4
- from typing import IO, List, Optional, Union
4
+ from typing import Any, IO, List, Optional, Union
5
5
 
6
6
  from .backend import PackagingBackend
7
7
 
@@ -56,6 +56,13 @@ class TarPackagingBackend(PackagingBackend):
56
56
  def cls_open(cls, content: IO[bytes]) -> tarfile.TarFile:
57
57
  return tarfile.open(fileobj=content, mode="r:gz")
58
58
 
59
+ @classmethod
60
+ def cls_member_name(cls, member: Union[tarfile.TarInfo, str]) -> str:
61
+ """
62
+ Returns the name of the member as a string.
63
+ """
64
+ return member.name if isinstance(member, tarfile.TarInfo) else member
65
+
59
66
  @classmethod
60
67
  def cls_has_member(cls, archive: tarfile.TarFile, name: str) -> bool:
61
68
  try:
@@ -76,11 +83,17 @@ class TarPackagingBackend(PackagingBackend):
76
83
  def cls_extract_members(
77
84
  cls,
78
85
  archive: tarfile.TarFile,
79
- members: Optional[List[str]] = None,
86
+ members: Optional[List[Any]] = None,
80
87
  dest_dir: str = ".",
81
88
  ) -> None:
82
89
  archive.extractall(path=dest_dir, members=members)
83
90
 
84
91
  @classmethod
85
- def cls_list_members(cls, archive: tarfile.TarFile) -> Optional[List[str]]:
92
+ def cls_list_members(
93
+ cls, archive: tarfile.TarFile
94
+ ) -> Optional[List[tarfile.TarInfo]]:
95
+ return archive.getmembers() or None
96
+
97
+ @classmethod
98
+ def cls_list_names(cls, archive: tarfile.TarFile) -> Optional[List[str]]:
86
99
  return archive.getnames() or None
@@ -61,23 +61,25 @@ class MetaflowCodeContentV1(MetaflowCodeContentV1Base):
61
61
  else:
62
62
  new_modules = []
63
63
 
64
- self._modules = {
65
- name: _ModuleInfo(
66
- name,
67
- set(
68
- Path(p).resolve().as_posix()
69
- for p in getattr(mod, "__path__", [mod.__file__])
70
- ),
71
- mod,
72
- True, # This is a Metaflow module (see filter below)
73
- )
74
- for (name, mod) in new_modules
75
- }
76
-
77
- # Filter the modules
78
- self._modules = {
79
- name: info for name, info in self._modules.items() if criteria(info.module)
80
- }
64
+ self._modules = {} # type: Dict[str, _ModuleInfo]
65
+ # We do this explicitly module by module to harden it against misbehaving
66
+ # modules like the one in:
67
+ # https://github.com/Netflix/metaflow/issues/2512
68
+ # We will silently ignore modules that are not well built.
69
+ for name, mod in new_modules:
70
+ try:
71
+ minfo = _ModuleInfo(
72
+ name,
73
+ set(
74
+ Path(p).resolve().as_posix()
75
+ for p in getattr(mod, "__path__", [mod.__file__])
76
+ ),
77
+ mod,
78
+ True, # This is a Metaflow module (see filter below)
79
+ )
80
+ except:
81
+ continue
82
+ self._modules[name] = minfo
81
83
 
82
84
  # Contain metadata information regarding the distributions packaged.
83
85
  # This allows Metaflow to "fake" distribution information when packaged
@@ -355,16 +357,14 @@ class MetaflowCodeContentV1(MetaflowCodeContentV1Base):
355
357
  )
356
358
  yield json.dumps(self.create_mfcontent_info()).encode(
357
359
  "utf-8"
358
- ), os.path.join(self._code_dir, MFCONTENT_MARKER)
360
+ ), MFCONTENT_MARKER
359
361
  else:
360
362
  for k in self._other_content.keys():
361
363
  yield "<generated %s content>" % (os.path.basename(k)), k
362
364
  yield "<generated %s content>" % (
363
365
  os.path.basename(self._dist_info_file)
364
366
  ), os.path.join(self._other_dir, self._dist_info_file)
365
- yield "<generated %s content>" % MFCONTENT_MARKER, os.path.join(
366
- self._code_dir, MFCONTENT_MARKER
367
- )
367
+ yield "<generated %s content>" % MFCONTENT_MARKER, MFCONTENT_MARKER
368
368
 
369
369
  def _metaflow_distribution_files(self) -> Generator[Tuple[str, str], None, None]:
370
370
  debug.package_exec("Including Metaflow from '%s'" % self._metaflow_root)
@@ -243,9 +243,11 @@ class CondaStepDecorator(StepDecorator):
243
243
  # Ensure local installation of Metaflow is visible to user code
244
244
  python_path = self.__class__._metaflow_home.name
245
245
  addl_env_vars = {}
246
- if self.__class__._addl_env_vars is not None:
246
+ if self.__class__._addl_env_vars:
247
247
  for key, value in self.__class__._addl_env_vars.items():
248
- if key == "PYTHONPATH":
248
+ if key.endswith(":"):
249
+ addl_env_vars[key[:-1]] = value
250
+ elif key == "PYTHONPATH":
249
251
  addl_env_vars[key] = os.pathsep.join([value, python_path])
250
252
  else:
251
253
  addl_env_vars[key] = value
@@ -43,6 +43,7 @@ from metaflow._vendor.click.types import (
43
43
  )
44
44
  from metaflow.decorators import add_decorator_options
45
45
  from metaflow.exception import MetaflowException
46
+ from metaflow.flowspec import _FlowState
46
47
  from metaflow.includefile import FilePathClass
47
48
  from metaflow.metaflow_config import CLICK_API_PROCESS_CONFIG
48
49
  from metaflow.parameters import JSONTypeClass, flow_context
@@ -171,7 +172,6 @@ def _lazy_load_command(
171
172
  _self,
172
173
  name: str,
173
174
  ):
174
-
175
175
  # Context is not used in get_command so we can pass None. Since we pin click,
176
176
  # this won't change from under us.
177
177
 
@@ -516,6 +516,11 @@ class MetaflowAPI(object):
516
516
  # Note that if CLICK_API_PROCESS_CONFIG is False, we still do this because
517
517
  # it will init all parameters (config_options will be None)
518
518
  # We ignore any errors if we don't check the configs in the click API.
519
+
520
+ # Init all values in the flow mutators and then process them
521
+ for decorator in self._flow_cls._flow_state.get(_FlowState.FLOW_MUTATORS, []):
522
+ decorator.external_init()
523
+
519
524
  new_cls = self._flow_cls._process_config_decorators(
520
525
  config_options, process_configs=CLICK_API_PROCESS_CONFIG
521
526
  )
@@ -541,14 +546,16 @@ def extract_all_params(cmd_obj: Union[click.Command, click.Group]):
541
546
 
542
547
  for each_param in cmd_obj.params:
543
548
  if isinstance(each_param, click.Argument):
544
- arg_params_sigs[each_param.name], annotations[each_param.name] = (
545
- get_inspect_param_obj(each_param, inspect.Parameter.POSITIONAL_ONLY)
546
- )
549
+ (
550
+ arg_params_sigs[each_param.name],
551
+ annotations[each_param.name],
552
+ ) = get_inspect_param_obj(each_param, inspect.Parameter.POSITIONAL_ONLY)
547
553
  arg_parameters[each_param.name] = each_param
548
554
  elif isinstance(each_param, click.Option):
549
- opt_params_sigs[each_param.name], annotations[each_param.name] = (
550
- get_inspect_param_obj(each_param, inspect.Parameter.KEYWORD_ONLY)
551
- )
555
+ (
556
+ opt_params_sigs[each_param.name],
557
+ annotations[each_param.name],
558
+ ) = get_inspect_param_obj(each_param, inspect.Parameter.KEYWORD_ONLY)
552
559
  opt_parameters[each_param.name] = each_param
553
560
 
554
561
  defaults[each_param.name] = each_param.default
@@ -152,12 +152,20 @@ class SubprocessManager(object):
152
152
  int
153
153
  The process ID of the subprocess.
154
154
  """
155
- updated_env = MetaflowCodeContent.get_env_vars_for_packaged_metaflow(
156
- get_metaflow_root()
157
- )
158
- if updated_env:
159
- env = env or {}
160
- env.update(updated_env)
155
+ env = env or {}
156
+ installed_root = os.environ.get("METAFLOW_EXTRACTED_ROOT", get_metaflow_root())
157
+
158
+ for k, v in MetaflowCodeContent.get_env_vars_for_packaged_metaflow(
159
+ installed_root
160
+ ).items():
161
+ if k.endswith(":"):
162
+ # Override
163
+ env[k[:-1]] = v
164
+ elif k in env:
165
+ env[k] = "%s:%s" % (v, env[k])
166
+ else:
167
+ env[k] = v
168
+
161
169
  command_obj = CommandManager(command, env, cwd)
162
170
  pid = command_obj.run(show_output=show_output)
163
171
  self.commands[pid] = command_obj
@@ -188,12 +196,12 @@ class SubprocessManager(object):
188
196
  int
189
197
  The process ID of the subprocess.
190
198
  """
191
- updated_env = MetaflowCodeContent.get_env_vars_for_packaged_metaflow(
192
- get_metaflow_root()
193
- )
194
- if updated_env:
195
- env = env or {}
196
- env.update(updated_env)
199
+ env = env or {}
200
+ if "PYTHONPATH" in env:
201
+ env["PYTHONPATH"] = "%s:%s" % (get_metaflow_root(), env["PYTHONPATH"])
202
+ else:
203
+ env["PYTHONPATH"] = get_metaflow_root()
204
+
197
205
  command_obj = CommandManager(command, env, cwd)
198
206
  pid = await command_obj.async_run()
199
207
  self.commands[pid] = command_obj
metaflow/version.py CHANGED
@@ -1 +1 @@
1
- metaflow_version = "2.16.4.5"
1
+ metaflow_version = "2.16.4.6"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ob-metaflow
3
- Version: 2.16.4.5
3
+ Version: 2.16.4.6
4
4
  Summary: Metaflow: More AI and ML, Less Engineering
5
5
  Author: Netflix, Outerbounds & the Metaflow Community
6
6
  Author-email: help@outerbounds.co
@@ -12,7 +12,7 @@ Requires-Dist: boto3
12
12
  Requires-Dist: pylint
13
13
  Requires-Dist: kubernetes
14
14
  Provides-Extra: stubs
15
- Requires-Dist: metaflow-stubs==2.16.4.5; extra == "stubs"
15
+ Requires-Dist: metaflow-stubs==2.16.4.6; extra == "stubs"
16
16
  Dynamic: author
17
17
  Dynamic: author-email
18
18
  Dynamic: description
@@ -19,7 +19,7 @@ metaflow/meta_files.py,sha256=vlgJHI8GJUKzXoxdrVoH8yyCF5bhFgwYemUgnyd1wgM,342
19
19
  metaflow/metaflow_config.py,sha256=xEAsJ-Fa-fkDq1hzI72Hx9BC00-S1yQoqgSYG9K46AY,24267
20
20
  metaflow/metaflow_config_funcs.py,sha256=5GlvoafV6SxykwfL8D12WXSfwjBN_NsyuKE_Q3gjGVE,6738
21
21
  metaflow/metaflow_current.py,sha256=pfkXmkyHeMJhxIs6HBJNBEaBDpcl5kz9Wx5mW6F_3qo,7164
22
- metaflow/metaflow_environment.py,sha256=YVq2j8cK6JjbmBkPXoTBNiAw29H1TVpDsnpVN-ZTBR4,11210
22
+ metaflow/metaflow_environment.py,sha256=20PIhA5R_rJneNj8f8UaWRmznGRPcEd6hP7goj_rc1s,11477
23
23
  metaflow/metaflow_git.py,sha256=Pb_VtvQzcjpuuM7UfC2u5kz85EbPMUfspl2UrPWBQMM,3647
24
24
  metaflow/metaflow_profile.py,sha256=jKPEW-hmAQO-htSxb9hXaeloLacAh41A35rMZH6G8pA,418
25
25
  metaflow/metaflow_version.py,sha256=KJJAxhOMY28DaavMpvJUzvw-G6MI-29Fi2A6AEcQpok,7495
@@ -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=g2SOU_CRzJLgDM_UGF9QDMANMAIHAsDRXE6S76_YzsY,14594
38
38
  metaflow/vendor.py,sha256=EDZokNMrx1PU07jNMiWFMFtC7TL03pMXZ1kKn13k-2g,5139
39
- metaflow/version.py,sha256=YbR0nHkTh55Ua282ig3LmM-SdTg7NeBQeE4tKU8Qo44,30
39
+ metaflow/version.py,sha256=0bLVnyslb9FTRAj4LmHwCMFnk1pe0Cy54ArUn2Uo5Tk,30
40
40
  metaflow/_vendor/__init__.py,sha256=y_CiwUD3l4eAKvTVDZeqgVujMy31cAM1qjAB-HfI-9s,353
41
41
  metaflow/_vendor/typing_extensions.py,sha256=q9zxWa6p6CzF1zZvSkygSlklduHf_b3K7MCxGz7MJRc,134519
42
42
  metaflow/_vendor/zipp.py,sha256=ajztOH-9I7KA_4wqDYygtHa6xUBVZgFpmZ8FE74HHHI,8425
@@ -140,7 +140,7 @@ metaflow/cli_components/run_cmds.py,sha256=xULZQ2UrxLNsWjQIZd38EbOGNBw8UJT7w_T19
140
140
  metaflow/cli_components/step_cmd.py,sha256=zGJgTv7wxrv34nWDi__CHaC2eS6kItR95EdVGJX803w,4766
141
141
  metaflow/cli_components/utils.py,sha256=gpoDociadjnJD7MuiJup_MDR02ZJjjleejr0jPBu29c,6057
142
142
  metaflow/client/__init__.py,sha256=1GtQB4Y_CBkzaxg32L1syNQSlfj762wmLrfrDxGi1b8,226
143
- metaflow/client/core.py,sha256=h_sPTG36U2UPiZRRwL-oQ5EAeJ6ijfXaibhinb22Hj0,83548
143
+ metaflow/client/core.py,sha256=tj2PuqQt1RXg8GuyLQ_WRuxYEvTaDyi_lW18YGwWvAQ,83714
144
144
  metaflow/client/filecache.py,sha256=Wy0yhhCqC1JZgebqi7z52GCwXYnkAqMZHTtxThvwBgM,15229
145
145
  metaflow/cmd/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
146
146
  metaflow/cmd/configure_cmd.py,sha256=o-DKnUf2FBo_HiMVyoyzQaGBSMtpbEPEdFTQZ0hkU-k,33396
@@ -160,7 +160,7 @@ metaflow/datastore/exceptions.py,sha256=r7Ab5FvHIzyFh6kwiptA1lO5nLqWg0xRBoeYGefv
160
160
  metaflow/datastore/flow_datastore.py,sha256=rDMEHdYwub1PwLp2uaK-8CHdd8hiwxqeELXzsUfuqZs,10250
161
161
  metaflow/datastore/inputs.py,sha256=i43dXr2xvgtsgKMO9allgCR18bk80GeayeQFyUTH36w,449
162
162
  metaflow/datastore/task_datastore.py,sha256=bEti1X5rvKBQykfvsoAnmHXel_itZbI5MeLrEpWPHPQ,35059
163
- metaflow/extension_support/__init__.py,sha256=Y0DoVZ1Eh4W7aO9DMIXrI8xUoJFP40Oux2gVWv-xoYY,52488
163
+ metaflow/extension_support/__init__.py,sha256=xLkhh0IzQ70IfF9j6MopMY02SMpEVI_eguksIOEXbDs,52522
164
164
  metaflow/extension_support/_empty_file.py,sha256=vz61sSExf5DZH3JCqdfwkp7l_NrJR8HV175kG82yUas,133
165
165
  metaflow/extension_support/cmd.py,sha256=hk8iBUUINqvKCDxInKgWpum8ThiRZtHSJP7qBASHzl8,5711
166
166
  metaflow/extension_support/integrations.py,sha256=AWAh-AZ-vo9IxuAVEjGw3s8p_NMm2DKHYx10oC51gPU,5506
@@ -174,13 +174,13 @@ metaflow/mflog/mflog.py,sha256=VebXxqitOtNAs7VJixnNfziO_i_urG7bsJ5JiB5IXgY,4370
174
174
  metaflow/mflog/save_logs.py,sha256=4p1OwozsHJBslOzAf0wUq2XPMNpEOZWM68MgWzh_jJY,2330
175
175
  metaflow/mflog/save_logs_periodically.py,sha256=2Uvk9hi-zlCqXxOQoXmmjH1SCugfw6eG6w70WgfI-ho,1256
176
176
  metaflow/mflog/tee.py,sha256=wTER15qeHuiRpCkOqo-bd-r3Gj-EVlf3IvWRCA4beW4,887
177
- metaflow/package/__init__.py,sha256=O2fCTo7Ktm4TnjA6blkepUWs8sf3fTY1q5y8I5nk8Ak,25962
178
- metaflow/packaging_sys/__init__.py,sha256=VIDa-J_UNyD-Axf5Iuom6SUssQfqAcTq4ezHU6_uqIM,31529
179
- metaflow/packaging_sys/backend.py,sha256=b3OX42Io8xP5l56zhXZxi-VLqXBr-B2q8AM7cWZvmMw,3104
177
+ metaflow/package/__init__.py,sha256=1ckiqYx4mJ0Rtp5vr5ta3fIx1LuCSkTdoHy0Gzx_Lwo,26384
178
+ metaflow/packaging_sys/__init__.py,sha256=HzpznaW1T9xppCIsxI7MzcTIu6QasyDZ3pJ5fRjzuao,31994
179
+ metaflow/packaging_sys/backend.py,sha256=uiJNMTTh3RV9_Zqkd5MBd2TnukfxpY2OEGBOmDroeTM,3534
180
180
  metaflow/packaging_sys/distribution_support.py,sha256=VvikZBCH8N1TBZZ2Twk8jH1brmiinKWCD3y_aFqBsIw,4937
181
- metaflow/packaging_sys/tar_backend.py,sha256=EYZD5iGEzPoO4L6IQhmrZC1QlaOPV471SBKyOYBS2XU,2593
181
+ metaflow/packaging_sys/tar_backend.py,sha256=nFWuXiwYjWQkFdV2KaZ6gazNVvtY84Eqsh9txhU3pNY,3010
182
182
  metaflow/packaging_sys/utils.py,sha256=x8SVglJvY5mIAilS7MqZi2PpMr6IEyi6RCg3l8hN3G0,2972
183
- metaflow/packaging_sys/v1.py,sha256=_YlVPR7oSYt7B8dHOS0Fb6ZPrxaSZMSQyRhdN8CL8ZY,20483
183
+ metaflow/packaging_sys/v1.py,sha256=kbNK0-pDAv3QJPZ789TE0UirGXcHbXkVQiyNT815H7A,20631
184
184
  metaflow/plugins/__init__.py,sha256=yFxjJOlnfap7tQMNgSgaso2tl_zr1BcWL7KoUKk4c9Y,8617
185
185
  metaflow/plugins/catch_decorator.py,sha256=UOM2taN_OL2RPpuJhwEOA9ZALm0-hHD0XS2Hn2GUev0,4061
186
186
  metaflow/plugins/debug_logger.py,sha256=mcF5HYzJ0NQmqCMjyVUk3iAP-heroHRIiVWQC6Ha2-I,879
@@ -338,7 +338,7 @@ metaflow/plugins/metadata_providers/local.py,sha256=Z0CXaGZJbAkj4II3WspJi-uCCtSh
338
338
  metaflow/plugins/metadata_providers/service.py,sha256=WL3GkEQlQk0syjSZ6iOnBSb3nRGfeUye95ySvLnMwhg,22953
339
339
  metaflow/plugins/pypi/__init__.py,sha256=0YFZpXvX7HCkyBFglatual7XGifdA1RwC3U4kcizyak,1037
340
340
  metaflow/plugins/pypi/bootstrap.py,sha256=8EWBdwOp5moXkTfLadn3ZOtPXoGftjOFD-c2W_rn77c,14998
341
- metaflow/plugins/pypi/conda_decorator.py,sha256=ie0ftOcozloj_qdASOxYvycb-Zr1GJHedzEDnp6cl2w,13984
341
+ metaflow/plugins/pypi/conda_decorator.py,sha256=fXG9EvImP4Eqle_Trhb3tQhs40xTc4cxL_r7r0BlJzo,14064
342
342
  metaflow/plugins/pypi/conda_environment.py,sha256=MFWHUykFXpBhEuvxRku2FV5dtiWH_ECZgnoq1PoF9Ik,25134
343
343
  metaflow/plugins/pypi/micromamba.py,sha256=UltfY8NmLphfZ-AbpaMFIdxIeOXLdTYDrMrabvPrYVU,17352
344
344
  metaflow/plugins/pypi/parsers.py,sha256=gpOOG2Ph95wI73MWCAi7XjpK0gYhv5k5YIGBs73QPuE,8556
@@ -356,13 +356,13 @@ metaflow/plugins/uv/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
356
356
  metaflow/plugins/uv/bootstrap.py,sha256=1UmNnnR7I1YcOtjdAmhuiU23-vj7NimUk3C9QillBaE,4380
357
357
  metaflow/plugins/uv/uv_environment.py,sha256=AYZICrBEq3Bv-taXktJwu9DhKFxNooPFwlcH379EYMs,2719
358
358
  metaflow/runner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
359
- metaflow/runner/click_api.py,sha256=DSxa5A0C_IHNug7fZlLpD_N99F_skDcAjTRx5YRMylY,23756
359
+ metaflow/runner/click_api.py,sha256=w0-47ntQD4Dd-LiSJ9vPSjlvW5YBniNSmlf95lcXs5E,24026
360
360
  metaflow/runner/deployer.py,sha256=U-hwf4gVzwUlXgnkfTW3y1daGXvo5eP4HTQZwb-vS0g,11058
361
361
  metaflow/runner/deployer_impl.py,sha256=zTING0_fwP44JcGo69DuNrVut5KqdBVzYOM7MYTZgIY,7049
362
362
  metaflow/runner/metaflow_runner.py,sha256=uo3BzcAfZ67VT_f-TPe5ZHiWHn6uuojWusOMGksvX14,18178
363
363
  metaflow/runner/nbdeploy.py,sha256=Sp5w-6nCZwjHaRBHWxi8udya-RYnJOB76KNLjB4L7Gs,4166
364
364
  metaflow/runner/nbrun.py,sha256=LhJu-Teoi7wTkNxg0kpNPVXFxH_9P4lvtp0ysMEIFJ8,7299
365
- metaflow/runner/subprocess_manager.py,sha256=0nAXG1PM7KXSIpu6ECeNMv5EfrqCymlIS6k25crnIBA,22792
365
+ metaflow/runner/subprocess_manager.py,sha256=x-MtPpGGMQUkIbQ_oNOuR-45b91DFvsCJ0SPoFc0dF4,23028
366
366
  metaflow/runner/utils.py,sha256=fU4vPazBdi6ATAUW_DaBAQeVslRwrLT8Pn9s5wav3gg,10350
367
367
  metaflow/sidecar/__init__.py,sha256=1mmNpmQ5puZCpRmmYlCOeieZ4108Su9XQ4_EqF1FGOU,131
368
368
  metaflow/sidecar/sidecar.py,sha256=EspKXvPPNiyRToaUZ51PS5TT_PzrBNAurn_wbFnmGr0,1334
@@ -409,12 +409,12 @@ metaflow/user_decorators/mutable_flow.py,sha256=icF7XFCS5FdlW3OEL68ZbQOtTPhLsUyc
409
409
  metaflow/user_decorators/mutable_step.py,sha256=-BY0UDXf_RCAEnC5JlLzEXGdiw1KD9oSrSxS_SWaB9Y,16791
410
410
  metaflow/user_decorators/user_flow_decorator.py,sha256=2yDwZq9QGv9W-7kEuKwa8o4ZkTvuHJ5ESz7VVrGViAI,9890
411
411
  metaflow/user_decorators/user_step_decorator.py,sha256=JYNGXONWCpzwn-_bF5WiAkof4Ii9tRS4xdK8ojSxG6M,26007
412
- ob_metaflow-2.16.4.5.data/data/share/metaflow/devtools/Makefile,sha256=5n89OGIC_kE4wxtEI66VCucN-b-1w5bqvGeZYmeRGz8,13737
413
- ob_metaflow-2.16.4.5.data/data/share/metaflow/devtools/Tiltfile,sha256=I55XTG4RBnrMfDcYRtREXqqS8T9bF8agkZq0DlvdFLk,21404
414
- ob_metaflow-2.16.4.5.data/data/share/metaflow/devtools/pick_services.sh,sha256=DCnrMXwtApfx3B4S-YiZESMyAFHbXa3VuNL0MxPLyiE,2196
415
- ob_metaflow-2.16.4.5.dist-info/licenses/LICENSE,sha256=nl_Lt5v9VvJ-5lWJDT4ddKAG-VZ-2IaLmbzpgYDz2hU,11343
416
- ob_metaflow-2.16.4.5.dist-info/METADATA,sha256=SBvy_6vSYHgn4P_6LPV-mw-OcTyTrF0nR7rw0HzgvIk,5935
417
- ob_metaflow-2.16.4.5.dist-info/WHEEL,sha256=JNWh1Fm1UdwIQV075glCn4MVuCRs0sotJIq-J6rbxCU,109
418
- ob_metaflow-2.16.4.5.dist-info/entry_points.txt,sha256=RvEq8VFlgGe_FfqGOZi0D7ze1hLD0pAtXeNyGfzc_Yc,103
419
- ob_metaflow-2.16.4.5.dist-info/top_level.txt,sha256=v1pDHoWaSaKeuc5fKTRSfsXCKSdW1zvNVmvA-i0if3o,9
420
- ob_metaflow-2.16.4.5.dist-info/RECORD,,
412
+ ob_metaflow-2.16.4.6.data/data/share/metaflow/devtools/Makefile,sha256=5n89OGIC_kE4wxtEI66VCucN-b-1w5bqvGeZYmeRGz8,13737
413
+ ob_metaflow-2.16.4.6.data/data/share/metaflow/devtools/Tiltfile,sha256=I55XTG4RBnrMfDcYRtREXqqS8T9bF8agkZq0DlvdFLk,21404
414
+ ob_metaflow-2.16.4.6.data/data/share/metaflow/devtools/pick_services.sh,sha256=DCnrMXwtApfx3B4S-YiZESMyAFHbXa3VuNL0MxPLyiE,2196
415
+ ob_metaflow-2.16.4.6.dist-info/licenses/LICENSE,sha256=nl_Lt5v9VvJ-5lWJDT4ddKAG-VZ-2IaLmbzpgYDz2hU,11343
416
+ ob_metaflow-2.16.4.6.dist-info/METADATA,sha256=Vgm6oN_6xrgMpIxnfVcNFzObs8Mdwjm1TGHY6EsIEEs,5935
417
+ ob_metaflow-2.16.4.6.dist-info/WHEEL,sha256=JNWh1Fm1UdwIQV075glCn4MVuCRs0sotJIq-J6rbxCU,109
418
+ ob_metaflow-2.16.4.6.dist-info/entry_points.txt,sha256=RvEq8VFlgGe_FfqGOZi0D7ze1hLD0pAtXeNyGfzc_Yc,103
419
+ ob_metaflow-2.16.4.6.dist-info/top_level.txt,sha256=v1pDHoWaSaKeuc5fKTRSfsXCKSdW1zvNVmvA-i0if3o,9
420
+ ob_metaflow-2.16.4.6.dist-info/RECORD,,