metaflow 2.16.6__py2.py3-none-any.whl → 2.16.8__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 (48) hide show
  1. metaflow/_vendor/click/core.py +3 -4
  2. metaflow/_vendor/imghdr/__init__.py +11 -0
  3. metaflow/_vendor/yaml/__init__.py +427 -0
  4. metaflow/_vendor/yaml/composer.py +139 -0
  5. metaflow/_vendor/yaml/constructor.py +748 -0
  6. metaflow/_vendor/yaml/cyaml.py +101 -0
  7. metaflow/_vendor/yaml/dumper.py +62 -0
  8. metaflow/_vendor/yaml/emitter.py +1137 -0
  9. metaflow/_vendor/yaml/error.py +75 -0
  10. metaflow/_vendor/yaml/events.py +86 -0
  11. metaflow/_vendor/yaml/loader.py +63 -0
  12. metaflow/_vendor/yaml/nodes.py +49 -0
  13. metaflow/_vendor/yaml/parser.py +589 -0
  14. metaflow/_vendor/yaml/reader.py +185 -0
  15. metaflow/_vendor/yaml/representer.py +389 -0
  16. metaflow/_vendor/yaml/resolver.py +227 -0
  17. metaflow/_vendor/yaml/scanner.py +1435 -0
  18. metaflow/_vendor/yaml/serializer.py +111 -0
  19. metaflow/_vendor/yaml/tokens.py +104 -0
  20. metaflow/cli.py +11 -2
  21. metaflow/client/core.py +6 -1
  22. metaflow/extension_support/__init__.py +4 -3
  23. metaflow/metaflow_environment.py +14 -6
  24. metaflow/package/__init__.py +18 -9
  25. metaflow/packaging_sys/__init__.py +53 -43
  26. metaflow/packaging_sys/backend.py +21 -6
  27. metaflow/packaging_sys/tar_backend.py +16 -3
  28. metaflow/packaging_sys/v1.py +21 -21
  29. metaflow/plugins/argo/argo_workflows.py +60 -2
  30. metaflow/plugins/argo/argo_workflows_deployer_objects.py +37 -0
  31. metaflow/plugins/aws/step_functions/step_functions_deployer_objects.py +16 -0
  32. metaflow/plugins/cards/card_modules/convert_to_native_type.py +7 -1
  33. metaflow/plugins/kubernetes/kubernetes_jobsets.py +26 -28
  34. metaflow/plugins/pypi/conda_decorator.py +4 -2
  35. metaflow/runner/click_api.py +14 -7
  36. metaflow/runner/deployer.py +77 -0
  37. metaflow/runner/subprocess_manager.py +20 -12
  38. metaflow/vendor.py +23 -6
  39. metaflow/version.py +1 -1
  40. {metaflow-2.16.6.dist-info → metaflow-2.16.8.dist-info}/METADATA +2 -2
  41. {metaflow-2.16.6.dist-info → metaflow-2.16.8.dist-info}/RECORD +48 -31
  42. {metaflow-2.16.6.data → metaflow-2.16.8.data}/data/share/metaflow/devtools/Makefile +0 -0
  43. {metaflow-2.16.6.data → metaflow-2.16.8.data}/data/share/metaflow/devtools/Tiltfile +0 -0
  44. {metaflow-2.16.6.data → metaflow-2.16.8.data}/data/share/metaflow/devtools/pick_services.sh +0 -0
  45. {metaflow-2.16.6.dist-info → metaflow-2.16.8.dist-info}/WHEEL +0 -0
  46. {metaflow-2.16.6.dist-info → metaflow-2.16.8.dist-info}/entry_points.txt +0 -0
  47. {metaflow-2.16.6.dist-info → metaflow-2.16.8.dist-info}/licenses/LICENSE +0 -0
  48. {metaflow-2.16.6.dist-info → metaflow-2.16.8.dist-info}/top_level.txt +0 -0
@@ -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)
@@ -3226,8 +3226,8 @@ class ArgoWorkflows(object):
3226
3226
  Trigger().template(
3227
3227
  TriggerTemplate(self.name)
3228
3228
  # Trigger a deployed workflow template
3229
- .argo_workflow_trigger(
3230
- ArgoWorkflowTrigger()
3229
+ .k8s_trigger(
3230
+ StandardK8STrigger()
3231
3231
  .source(
3232
3232
  {
3233
3233
  "resource": {
@@ -4205,6 +4205,10 @@ class TriggerTemplate(object):
4205
4205
  self.payload = tree()
4206
4206
  self.payload["name"] = name
4207
4207
 
4208
+ def k8s_trigger(self, k8s_trigger):
4209
+ self.payload["k8s"] = k8s_trigger.to_json()
4210
+ return self
4211
+
4208
4212
  def argo_workflow_trigger(self, argo_workflow_trigger):
4209
4213
  self.payload["argoWorkflow"] = argo_workflow_trigger.to_json()
4210
4214
  return self
@@ -4279,3 +4283,57 @@ class TriggerParameter(object):
4279
4283
 
4280
4284
  def __str__(self):
4281
4285
  return json.dumps(self.payload, indent=4)
4286
+
4287
+
4288
+ class StandardK8STrigger(object):
4289
+ # https://pkg.go.dev/github.com/argoproj/argo-events/pkg/apis/sensor/v1alpha1#StandardK8STrigger
4290
+
4291
+ def __init__(self):
4292
+ tree = lambda: defaultdict(tree)
4293
+ self.payload = tree()
4294
+ self.payload["operation"] = "create"
4295
+
4296
+ def operation(self, operation):
4297
+ self.payload["operation"] = operation
4298
+ return self
4299
+
4300
+ def group(self, group):
4301
+ self.payload["group"] = group
4302
+ return self
4303
+
4304
+ def version(self, version):
4305
+ self.payload["version"] = version
4306
+ return self
4307
+
4308
+ def resource(self, resource):
4309
+ self.payload["resource"] = resource
4310
+ return self
4311
+
4312
+ def namespace(self, namespace):
4313
+ self.payload["namespace"] = namespace
4314
+ return self
4315
+
4316
+ def source(self, source):
4317
+ self.payload["source"] = source
4318
+ return self
4319
+
4320
+ def parameters(self, trigger_parameters):
4321
+ if "parameters" not in self.payload:
4322
+ self.payload["parameters"] = []
4323
+ for trigger_parameter in trigger_parameters:
4324
+ self.payload["parameters"].append(trigger_parameter.to_json())
4325
+ return self
4326
+
4327
+ def live_object(self, live_object=True):
4328
+ self.payload["liveObject"] = live_object
4329
+ return self
4330
+
4331
+ def patch_strategy(self, patch_strategy):
4332
+ self.payload["patchStrategy"] = patch_strategy
4333
+ return self
4334
+
4335
+ def to_json(self):
4336
+ return self.payload
4337
+
4338
+ def __str__(self):
4339
+ return json.dumps(self.payload, indent=4)
@@ -310,6 +310,43 @@ class ArgoWorkflowsDeployedFlow(DeployedFlow):
310
310
 
311
311
  return cls(deployer=d)
312
312
 
313
+ @classmethod
314
+ def get_triggered_run(
315
+ cls, identifier: str, run_id: str, metadata: Optional[str] = None
316
+ ):
317
+ """
318
+ Retrieves a `ArgoWorkflowsTriggeredRun` object from an identifier, a run id and
319
+ optional metadata.
320
+
321
+ Parameters
322
+ ----------
323
+ identifier : str
324
+ Deployer specific identifier for the workflow to retrieve
325
+ run_id : str
326
+ Run ID for the which to fetch the triggered run object
327
+ metadata : str, optional, default None
328
+ Optional deployer specific metadata.
329
+
330
+ Returns
331
+ -------
332
+ ArgoWorkflowsTriggeredRun
333
+ A `ArgoWorkflowsTriggeredRun` object representing the
334
+ triggered run on argo workflows.
335
+ """
336
+ deployed_flow_obj = cls.from_deployment(identifier, metadata)
337
+ return ArgoWorkflowsTriggeredRun(
338
+ deployer=deployed_flow_obj.deployer,
339
+ content=json.dumps(
340
+ {
341
+ "metadata": deployed_flow_obj.deployer.metadata,
342
+ "pathspec": "/".join(
343
+ (deployed_flow_obj.deployer.flow_name, run_id)
344
+ ),
345
+ "name": run_id,
346
+ }
347
+ ),
348
+ )
349
+
313
350
  @property
314
351
  def production_token(self) -> Optional[str]:
315
352
  """
@@ -84,6 +84,22 @@ class StepFunctionsDeployedFlow(DeployedFlow):
84
84
  "from_deployment is not implemented for StepFunctions"
85
85
  )
86
86
 
87
+ @classmethod
88
+ def get_triggered_run(
89
+ cls, identifier: str, run_id: str, metadata: Optional[str] = None
90
+ ):
91
+ """
92
+ This method is not currently implemented for Step Functions.
93
+
94
+ Raises
95
+ ------
96
+ NotImplementedError
97
+ This method is not implemented for Step Functions.
98
+ """
99
+ raise NotImplementedError(
100
+ "get_triggered_run is not implemented for StepFunctions"
101
+ )
102
+
87
103
  @property
88
104
  def production_token(self: DeployedFlow) -> Optional[str]:
89
105
  """
@@ -146,7 +146,13 @@ class TaskToDict:
146
146
  # Python 3.13 removes the standard ``imghdr`` module. Metaflow
147
147
  # vendors a copy so we can keep using ``what`` to detect image
148
148
  # formats irrespective of the Python version.
149
- from metaflow._vendor import imghdr
149
+ import warnings
150
+
151
+ with warnings.catch_warnings():
152
+ warnings.filterwarnings(
153
+ "ignore", category=DeprecationWarning, module="imghdr"
154
+ )
155
+ from metaflow._vendor import imghdr
150
156
 
151
157
  resp = imghdr.what(None, h=data_object)
152
158
  # Only accept types supported on the web
@@ -6,6 +6,7 @@ from collections import namedtuple
6
6
  from metaflow.exception import MetaflowException
7
7
  from metaflow.metaflow_config import KUBERNETES_JOBSET_GROUP, KUBERNETES_JOBSET_VERSION
8
8
  from metaflow.tracing import inject_tracing_vars
9
+ from metaflow._vendor import yaml
9
10
 
10
11
  from .kube_utils import qos_requests_and_limits
11
12
 
@@ -1025,34 +1026,32 @@ class KubernetesArgoJobSet(object):
1025
1026
 
1026
1027
  def dump(self):
1027
1028
  client = self._kubernetes_sdk
1028
-
1029
- data = json.dumps(
1030
- client.ApiClient().sanitize_for_serialization(
1031
- dict(
1032
- apiVersion=self._group + "/" + self._version,
1033
- kind="JobSet",
1034
- metadata=client.api_client.ApiClient().sanitize_for_serialization(
1035
- client.V1ObjectMeta(
1036
- name=self.name,
1037
- labels=self._labels,
1038
- annotations=self._annotations,
1039
- )
1040
- ),
1041
- spec=dict(
1042
- replicatedJobs=[self.control.dump(), self.worker.dump()],
1043
- suspend=False,
1044
- startupPolicy=None,
1045
- successPolicy=None,
1046
- # The Failure Policy helps setting the number of retries for the jobset.
1047
- # but we don't rely on it and instead rely on either the local scheduler
1048
- # or the Argo Workflows to handle retries.
1049
- failurePolicy=None,
1050
- network=None,
1051
- ),
1052
- status=None,
1053
- )
1029
+ js_dict = client.ApiClient().sanitize_for_serialization(
1030
+ dict(
1031
+ apiVersion=self._group + "/" + self._version,
1032
+ kind="JobSet",
1033
+ metadata=client.api_client.ApiClient().sanitize_for_serialization(
1034
+ client.V1ObjectMeta(
1035
+ name=self.name,
1036
+ labels=self._labels,
1037
+ annotations=self._annotations,
1038
+ )
1039
+ ),
1040
+ spec=dict(
1041
+ replicatedJobs=[self.control.dump(), self.worker.dump()],
1042
+ suspend=False,
1043
+ startupPolicy=None,
1044
+ successPolicy=None,
1045
+ # The Failure Policy helps setting the number of retries for the jobset.
1046
+ # but we don't rely on it and instead rely on either the local scheduler
1047
+ # or the Argo Workflows to handle retries.
1048
+ failurePolicy=None,
1049
+ network=None,
1050
+ ),
1051
+ status=None,
1054
1052
  )
1055
1053
  )
1054
+ data = yaml.dump(js_dict, default_flow_style=False, indent=2)
1056
1055
  # The values we populate in the Jobset manifest (for Argo Workflows) piggybacks on the Argo Workflow's templating engine.
1057
1056
  # Even though Argo Workflows's templating helps us constructing all the necessary IDs and populating the fields
1058
1057
  # required by Metaflow, we run into one glitch. When we construct JSON/YAML serializable objects,
@@ -1067,7 +1066,6 @@ class KubernetesArgoJobSet(object):
1067
1066
  # Since the value of `num_parallel` can be dynamic and can change from run to run, we need to ensure that the
1068
1067
  # value can be passed-down dynamically and is **explicitly set as a integer** in the Jobset Manifest submitted as a
1069
1068
  # part of the Argo Workflow
1070
-
1071
- quoted_substring = '"{{=asInt(inputs.parameters.workerCount)}}"'
1069
+ quoted_substring = "'{{=asInt(inputs.parameters.workerCount)}}'"
1072
1070
  unquoted_substring = "{{=asInt(inputs.parameters.workerCount)}}"
1073
1071
  return data.replace(quoted_substring, unquoted_substring)
@@ -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
@@ -231,6 +231,67 @@ class DeployedFlowMeta(type):
231
231
  }
232
232
  )
233
233
 
234
+ def _get_triggered_run_injected_method():
235
+ def f(
236
+ cls,
237
+ identifier: str,
238
+ run_id: str,
239
+ metadata: Optional[str] = None,
240
+ impl: str = DEFAULT_FROM_DEPLOYMENT_IMPL.replace("-", "_"),
241
+ ) -> "TriggeredRun":
242
+ """
243
+ Retrieves a `TriggeredRun` object from an identifier, a run id and optional
244
+ metadata. The `impl` parameter specifies the deployer implementation
245
+ to use (like `argo-workflows`).
246
+
247
+ Parameters
248
+ ----------
249
+ identifier : str
250
+ Deployer specific identifier for the workflow to retrieve
251
+ run_id : str
252
+ Run ID for the which to fetch the triggered run object
253
+ metadata : str, optional, default None
254
+ Optional deployer specific metadata.
255
+ impl : str, optional, default given by METAFLOW_DEFAULT_FROM_DEPLOYMENT_IMPL
256
+ The default implementation to use if not specified
257
+
258
+ Returns
259
+ -------
260
+ TriggeredRun
261
+ A `TriggeredRun` object representing the triggered run corresponding
262
+ to the identifier and the run id.
263
+ """
264
+ if impl in allowed_providers:
265
+ return (
266
+ allowed_providers[impl]
267
+ .deployed_flow_type()
268
+ .get_triggered_run(identifier, run_id, metadata)
269
+ )
270
+ else:
271
+ raise ValueError(
272
+ f"No deployer '{impl}' exists; valid deployers are: "
273
+ f"{list(allowed_providers.keys())}"
274
+ )
275
+
276
+ f.__name__ = "get_triggered_run"
277
+ return f
278
+
279
+ def _per_type_get_triggered_run_injected_method(method_name, impl):
280
+ def f(
281
+ cls,
282
+ identifier: str,
283
+ run_id: str,
284
+ metadata: Optional[str] = None,
285
+ ):
286
+ return (
287
+ allowed_providers[impl]
288
+ .deployed_flow_type()
289
+ .get_triggered_run(identifier, run_id, metadata)
290
+ )
291
+
292
+ f.__name__ = method_name
293
+ return f
294
+
234
295
  def _from_deployment_injected_method():
235
296
  def f(
236
297
  cls,
@@ -347,10 +408,16 @@ class DeployedFlowMeta(type):
347
408
  "list_deployed_flows",
348
409
  classmethod(_list_deployed_flows_injected_method()),
349
410
  )
411
+ setattr(
412
+ cls,
413
+ "get_triggered_run",
414
+ classmethod(_get_triggered_run_injected_method()),
415
+ )
350
416
 
351
417
  for impl in allowed_providers:
352
418
  from_deployment_method_name = f"from_{impl}"
353
419
  list_deployed_flows_method_name = f"list_{impl}"
420
+ get_triggered_run_method_name = f"get_triggered_{impl}_run"
354
421
 
355
422
  setattr(
356
423
  cls,
@@ -372,6 +439,16 @@ class DeployedFlowMeta(type):
372
439
  ),
373
440
  )
374
441
 
442
+ setattr(
443
+ cls,
444
+ get_triggered_run_method_name,
445
+ classmethod(
446
+ _per_type_get_triggered_run_injected_method(
447
+ get_triggered_run_method_name, impl
448
+ )
449
+ ),
450
+ )
451
+
375
452
  return cls
376
453
 
377
454
 
@@ -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/vendor.py CHANGED
@@ -63,14 +63,29 @@ def find_vendored_libs(vendor_dir, whitelist, whitelist_dirs):
63
63
  return vendored_libs, paths
64
64
 
65
65
 
66
- def fetch_licenses(*info_dir, vendor_dir):
67
- for file in chain.from_iterable(map(iter_subtree, info_dir)):
68
- if "LICENSE" in file.name:
69
- library = file.parent.name.split("-")[0]
70
- shutil.copy(file, vendor_dir / ("%s.LICENSE" % library))
71
- else:
66
+ def fetch_licenses(*info_dirs, vendor_dir):
67
+ for dist_info in info_dirs:
68
+ metadata_file = dist_info / "METADATA"
69
+ if not metadata_file.exists():
70
+ continue
71
+
72
+ project_name = None
73
+ for line in metadata_file.read_text("utf-8").splitlines():
74
+ if line.startswith("Name: "):
75
+ project_name = line.split("Name: ", 1)[1].strip()
76
+ break
77
+ if not project_name:
72
78
  continue
73
79
 
80
+ for item in dist_info.iterdir():
81
+ if item.is_file() and re.search(r"(LICENSE|COPYING)", item.name, re.I):
82
+ shutil.copy(item, vendor_dir / f"{project_name}.LICENSE")
83
+ elif item.is_dir() and item.name.lower() == "licenses":
84
+ for license_file in item.iterdir():
85
+ if license_file.is_file():
86
+ dest_name = f"{project_name}.{license_file.name}"
87
+ shutil.copy(license_file, vendor_dir / dest_name)
88
+
74
89
 
75
90
  def vendor(vendor_dir):
76
91
  # remove everything
@@ -108,6 +123,8 @@ def vendor(vendor_dir):
108
123
  "-r",
109
124
  "_vendor/vendor_%s.txt" % subdir,
110
125
  "--no-compile",
126
+ "--no-binary",
127
+ ":all:",
111
128
  ]
112
129
  )
113
130
 
metaflow/version.py CHANGED
@@ -1 +1 @@
1
- metaflow_version = "2.16.6"
1
+ metaflow_version = "2.16.8"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: metaflow
3
- Version: 2.16.6
3
+ Version: 2.16.8
4
4
  Summary: Metaflow: More AI and ML, Less Engineering
5
5
  Author: Metaflow Developers
6
6
  Author-email: help@metaflow.org
@@ -26,7 +26,7 @@ License-File: LICENSE
26
26
  Requires-Dist: requests
27
27
  Requires-Dist: boto3
28
28
  Provides-Extra: stubs
29
- Requires-Dist: metaflow-stubs==2.16.6; extra == "stubs"
29
+ Requires-Dist: metaflow-stubs==2.16.8; extra == "stubs"
30
30
  Dynamic: author
31
31
  Dynamic: author-email
32
32
  Dynamic: classifier