metaflow-netflixext 1.3.4.dev1__tar.gz → 1.3.4.dev3__tar.gz

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 (65) hide show
  1. {metaflow_netflixext-1.3.4.dev1/metaflow_netflixext.egg-info → metaflow_netflixext-1.3.4.dev3}/PKG-INFO +1 -1
  2. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/plugins/conda/conda.py +1 -1
  3. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/plugins/conda/conda_step_decorator.py +1 -1
  4. metaflow_netflixext-1.3.4.dev3/metaflow_extensions/netflix_ext/plugins/coverage/__init__.py +0 -0
  5. metaflow_netflixext-1.3.4.dev3/metaflow_extensions/netflix_ext/plugins/coverage/coveragerc.py +21 -0
  6. metaflow_netflixext-1.3.4.dev3/metaflow_extensions/netflix_ext/plugins/coverage/generate_coveragerc.py +96 -0
  7. metaflow_netflixext-1.3.4.dev3/metaflow_extensions/netflix_ext/plugins/coverage/setup_coverage.py +111 -0
  8. metaflow_netflixext-1.3.4.dev3/metaflow_extensions/netflix_ext/plugins/coverage/upload_coverage_files.py +83 -0
  9. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3/metaflow_netflixext.egg-info}/PKG-INFO +1 -1
  10. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_netflixext.egg-info/SOURCES.txt +5 -0
  11. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/LICENSE +0 -0
  12. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/README.md +0 -0
  13. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/__init__.py +0 -0
  14. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/cmd/debug/__init__.py +0 -0
  15. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/cmd/debug/constants.py +0 -0
  16. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/cmd/debug/current_stub_generator.py +0 -0
  17. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/cmd/debug/debug_cmd.py +0 -0
  18. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/cmd/debug/debug_script_generator.py +0 -0
  19. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/cmd/debug/debug_stub_generator.py +0 -0
  20. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/cmd/debug/debug_utils.py +0 -0
  21. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/cmd/debug/jupyter_instructions_markdown.py +0 -0
  22. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/cmd/debug/jupyter_title_markdown.py +0 -0
  23. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/cmd/environment/__init__.py +0 -0
  24. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/cmd/environment/environment_cmd.py +0 -0
  25. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/cmd/environment/utils.py +0 -0
  26. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/cmd/mfextinit_netflixext.py +0 -0
  27. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/config/mfextinit_netflixext.py +0 -0
  28. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/exceptions/__init__.py +0 -0
  29. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/exceptions/decorators.py +0 -0
  30. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/exceptions/http_helpers.py +0 -0
  31. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/generate_vendor.py +0 -0
  32. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/http_helpers.py +0 -0
  33. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/plugins/conda/__init__.py +0 -0
  34. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/plugins/conda/conda_common_decorator.py +0 -0
  35. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/plugins/conda/conda_environment.py +0 -0
  36. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/plugins/conda/conda_flow_decorator.py +0 -0
  37. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/plugins/conda/conda_flow_mutator.py +0 -0
  38. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/plugins/conda/conda_lock_micromamba_server.py +0 -0
  39. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/plugins/conda/env_descr.py +0 -0
  40. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/plugins/conda/envsresolver.py +0 -0
  41. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/plugins/conda/parsers.py +0 -0
  42. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/plugins/conda/pypi_package_builder.py +0 -0
  43. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/plugins/conda/remote_bootstrap.py +0 -0
  44. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/plugins/conda/resolvers/__init__.py +0 -0
  45. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/plugins/conda/resolvers/builder_envs_resolver.py +0 -0
  46. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/plugins/conda/resolvers/conda_lock_resolver.py +0 -0
  47. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/plugins/conda/resolvers/conda_resolver.py +0 -0
  48. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/plugins/conda/resolvers/micromamba_server_resolver.py +0 -0
  49. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/plugins/conda/resolvers/pip_resolver.py +0 -0
  50. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/plugins/conda/resolvers/pylock_toml_resolver.py +0 -0
  51. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/plugins/conda/resources/logo-32x32.png +0 -0
  52. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/plugins/conda/resources/logo-64x64.png +0 -0
  53. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/plugins/conda/resources/logo-svg.svg +0 -0
  54. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/plugins/conda/terminal_menu.py +0 -0
  55. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/plugins/conda/utils.py +0 -0
  56. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/plugins/environment_cli.py +0 -0
  57. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/plugins/mfextinit_netflixext.py +0 -0
  58. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/toplevel/mfextinit_netflixext.py +0 -0
  59. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/toplevel/netflixext_toplevel.py +0 -0
  60. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_extensions/netflix_ext/toplevel/netflixext_version.py +0 -0
  61. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_netflixext.egg-info/dependency_links.txt +0 -0
  62. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_netflixext.egg-info/requires.txt +0 -0
  63. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/metaflow_netflixext.egg-info/top_level.txt +0 -0
  64. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/setup.cfg +0 -0
  65. {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev3}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: metaflow-netflixext
3
- Version: 1.3.4.dev1
3
+ Version: 1.3.4.dev3
4
4
  Summary: Metaflow extensions from Netflix
5
5
  Author: Netflix Metaflow Developers
6
6
  Author-email: metaflow-dev@netflix.com
@@ -2743,7 +2743,7 @@ class Conda(object):
2743
2743
 
2744
2744
  entrypoint = os.path.join(env_dir, "bin", "python")
2745
2745
  if os.environ.get("METAFLOW_COVERAGE_S3_PATH"):
2746
- from metaflow_extensions.nflx.plugins.coverage.setup_coverage import (
2746
+ from metaflow_extensions.netflix_ext.plugins.coverage.setup_coverage import (
2747
2747
  setup_coverage,
2748
2748
  )
2749
2749
 
@@ -661,7 +661,7 @@ class CondaEnvInternalDecorator(StepDecorator):
661
661
  cli_args.entrypoint[0] = entrypoint
662
662
 
663
663
  if os.environ.get("METAFLOW_COVERAGE_S3_PATH"):
664
- from metaflow_extensions.nflx.plugins.coverage.setup_coverage import (
664
+ from metaflow_extensions.netflix_ext.plugins.coverage.setup_coverage import (
665
665
  setup_coverage,
666
666
  )
667
667
 
@@ -0,0 +1,21 @@
1
+ # this file is named coveragerc.py since metaflow does not have a way to package hidden files
2
+
3
+ [run]
4
+ branch = True
5
+ parallel = True
6
+ concurrency = multiprocessing,thread
7
+
8
+ [html]
9
+ show_contexts = True
10
+
11
+ [report]
12
+ omit =
13
+ /**/site-packages/*
14
+ /**/vendor/*
15
+ /**/vendored/*
16
+ /**/_vendor/*
17
+ /**/build/*
18
+
19
+ include_namespace_packages = True
20
+ show_missing = False
21
+ ignore_errors = False
@@ -0,0 +1,96 @@
1
+ import configparser
2
+ import coverage
3
+ import re
4
+ import os
5
+ from metaflow_extensions.netflix_ext.plugins.coverage.setup_coverage import COVERAGE_RCFILE
6
+
7
+
8
+ def get_tmp_dirs(pattern, coverage_rcfile, data_file=".coverage"):
9
+ cov = coverage.Coverage(data_file=data_file, config_file=coverage_rcfile)
10
+ cov.load()
11
+ cov_data = cov.get_data()
12
+ files_covered = cov_data.measured_files()
13
+ tmp_dirs = set()
14
+ pattern = re.compile(pattern)
15
+ for file_path in files_covered:
16
+ match = pattern.match(file_path)
17
+ if match:
18
+ tmp_dirs.add(match.group(1))
19
+ return list(tmp_dirs)
20
+
21
+
22
+ def remap_tmp_dirs(cov, coverage_rcfile, data_file):
23
+ """
24
+ We need to update the [paths] config to remap all the temporary directories.
25
+ This function iterates over all the files and uses a regex match to
26
+ find the relevent paths that need remapping.
27
+ """
28
+ config = cov.config
29
+ trampoline_dirs = get_tmp_dirs(
30
+ r"^(/data/tmp/[^/]+/)([^/]+\.py)$", coverage_rcfile, data_file
31
+ )
32
+ config.paths["_escape_trampolines"] = ["_escape_trampolines"] + trampoline_dirs
33
+ ttk_dirs = get_tmp_dirs(
34
+ r"(?P<dir>.*?/_ttk_[^/]+)/ray_train_func\.py$", coverage_rcfile, data_file
35
+ )
36
+ print(f"DEBUG: Found {len(ttk_dirs)} TTK directories: {ttk_dirs}")
37
+ config.paths["trainingplatform"] = [
38
+ ".mf_code/metaflow_extensions/nflx/plugins/trainingplatform"
39
+ ] + ttk_dirs
40
+ ray_dirs = get_tmp_dirs(
41
+ r"(?P<dir>.*?/_ray_pkg_[^/]+)/.+", coverage_rcfile, data_file
42
+ )
43
+ config.paths["ray"] = ["."] + ray_dirs
44
+ # Initialize training_platform if it doesn't exist, then add ray_dirs
45
+ if "training_platform" not in config.paths:
46
+ config.paths["training_platform"] = []
47
+ config.paths["training_platform"] = config.paths["training_platform"] + ray_dirs
48
+ formatted_dirs = get_tmp_dirs(
49
+ r"(?P<dir>.*?)/[^/]+_(check|test)_flow\.py$", coverage_rcfile, data_file
50
+ )
51
+ config.paths["_formatted_flows"] = ["_formatted_flows"] + formatted_dirs
52
+
53
+
54
+ def save_updated_coveragerc(config, coverage_rcfile, output_file):
55
+ # Read template as text to preserve all sections and comments
56
+ with open(coverage_rcfile, "r") as f:
57
+ template_content = f.read()
58
+
59
+ # Parse template to get existing paths
60
+ config_parser = configparser.ConfigParser()
61
+ config_parser.read_string(template_content)
62
+
63
+ # Update [paths] section: preserve template paths, add/update dynamic ones
64
+ if not config_parser.has_section("paths"):
65
+ config_parser.add_section("paths")
66
+
67
+ # Merge: Start with all template paths, then add/update dynamic ones
68
+ merged_paths = {}
69
+
70
+ # First, preserve all template paths
71
+ if config_parser.has_section("paths"):
72
+ for key in config_parser.options("paths"):
73
+ value = config_parser.get("paths", key)
74
+ # ConfigParser returns multi-line values with newlines already
75
+ merged_paths[key] = value
76
+
77
+ # Then add or override with dynamic paths from config.paths
78
+ for key, path_list in config.paths.items():
79
+ # Skip empty path lists to avoid IndexError in coverage.py
80
+ if path_list:
81
+ merged_paths[key] = "\n".join(path_list)
82
+
83
+ # Write merged paths back (skip empty values)
84
+ for key, value in merged_paths.items():
85
+ if value.strip(): # Only write non-empty paths
86
+ config_parser.set("paths", key, value)
87
+
88
+ with open(output_file, "w") as configfile:
89
+ config_parser.write(configfile)
90
+ print(f"Updated .coveragerc file written to {output_file}")
91
+
92
+
93
+ if __name__ == "__main__":
94
+ cov = coverage.Coverage(data_file=".coverage", config_file=COVERAGE_RCFILE)
95
+ remap_tmp_dirs(cov, COVERAGE_RCFILE, ".coverage")
96
+ save_updated_coveragerc(cov.config, COVERAGE_RCFILE, ".coveragerc")
@@ -0,0 +1,111 @@
1
+ import os
2
+ import shutil
3
+ import subprocess
4
+ import sys
5
+ from typing import Union, TYPE_CHECKING
6
+ from metaflow.util import which
7
+
8
+ if TYPE_CHECKING:
9
+ import sh
10
+
11
+ MODULE_DIR = os.path.dirname(os.path.abspath(__file__))
12
+ SITECUSTOMIZE_FILE = os.path.join(MODULE_DIR, "sitecustomize.py")
13
+
14
+ # Allow internal extensions to provide their own coveragerc override
15
+ try:
16
+ import metaflow_extensions.nflx.plugins.coverage as _nflx_cov
17
+
18
+ _nflx_coveragerc = os.path.join(os.path.dirname(_nflx_cov.__file__), "coveragerc.py")
19
+ COVERAGE_RCFILE = _nflx_coveragerc if os.path.exists(_nflx_coveragerc) else os.path.join(MODULE_DIR, "coveragerc.py")
20
+ except ImportError:
21
+ COVERAGE_RCFILE = os.path.join(MODULE_DIR, "coveragerc.py")
22
+
23
+
24
+ def get_local_coverage_dir():
25
+ METAFLOW_COVERAGE_S3_PATH = os.environ.get("METAFLOW_COVERAGE_S3_PATH")
26
+ assert METAFLOW_COVERAGE_S3_PATH, "METAFLOW_COVERAGE_S3_PATH is not set"
27
+ group_tag = os.path.basename(METAFLOW_COVERAGE_S3_PATH)
28
+ coverage_dir = os.path.join("/tmp", group_tag)
29
+ os.makedirs(coverage_dir, exist_ok=True)
30
+ return coverage_dir
31
+
32
+
33
+ def get_sitepackages_dir(python_binary: str):
34
+ return (
35
+ subprocess.run(
36
+ [python_binary, "-c", "import site; print(site.getsitepackages()[0])"],
37
+ check=True,
38
+ stdout=subprocess.PIPE,
39
+ )
40
+ .stdout.decode()
41
+ .strip()
42
+ )
43
+
44
+
45
+ def setup_sitecustomize(python_binary: str):
46
+ site_packages_dir = get_sitepackages_dir(python_binary)
47
+ sitecustomize_file = os.path.join(site_packages_dir, "sitecustomize.py")
48
+ local_coverage_dir = get_local_coverage_dir()
49
+
50
+ # We also copy the .coveragerc file to the site-packages directory.
51
+ # This keeps the .coveragerc file in the same directory as the sitecustomize.py file
52
+ # so that when the env is saved and restored we will have both.
53
+ coverage_rcfile = os.path.join(site_packages_dir, ".coveragerc")
54
+ if not os.path.exists(coverage_rcfile):
55
+ shutil.copy(COVERAGE_RCFILE, coverage_rcfile)
56
+
57
+ # We harden out the environment variables as the fallback so that it will work with
58
+ # subprocesses that are launched without inheriting the environment.
59
+ lines = [
60
+ "import os",
61
+ "import site",
62
+ "site_packages_dir = site.getsitepackages()[0]",
63
+ f"os.environ['COVERAGE_FILE'] = os.environ.get('COVERAGE_FILE') or os.path.join('{local_coverage_dir}', f'.coverage')",
64
+ f"os.environ['COVERAGE_PROCESS_START'] = os.environ.get('COVERAGE_PROCESS_START') or os.path.join(site_packages_dir, '.coveragerc')",
65
+ f"os.environ['COVERAGE_RCFILE'] = os.environ.get('COVERAGE_RCFILE') or os.path.join(site_packages_dir, '.coveragerc')",
66
+ f"os.environ['METAFLOW_COVERAGE_S3_PATH'] = os.environ.get('METAFLOW_COVERAGE_S3_PATH') or '{os.environ['METAFLOW_COVERAGE_S3_PATH']}'",
67
+ "import coverage",
68
+ "coverage.process_startup()",
69
+ "coverage_context = os.environ.get('METAFLOW_COVERAGE_CONTEXT')",
70
+ "if coverage_context and coverage.Coverage.current():",
71
+ " coverage.Coverage.current().switch_context(coverage_context)",
72
+ ]
73
+ if not os.path.exists(sitecustomize_file):
74
+ with open(sitecustomize_file, "w") as target_file:
75
+ target_file.write("\n".join(lines) + "\n")
76
+
77
+
78
+ def maybe_install_coverage(python_binary: str):
79
+ try:
80
+ subprocess.run(
81
+ [python_binary, "-c", "import coverage"],
82
+ check=True,
83
+ stdout=subprocess.PIPE,
84
+ stderr=subprocess.PIPE,
85
+ )
86
+ except subprocess.CalledProcessError:
87
+ subprocess.run([python_binary, "-m", "pip", "install", "coverage"], check=True)
88
+
89
+
90
+ def setup_coverage(sh_python: Union[str, "sh.Command"]):
91
+ sh_python = str(sh_python)
92
+ assert os.environ.get(
93
+ "METAFLOW_COVERAGE_S3_PATH"
94
+ ), "METAFLOW_COVERAGE_S3_PATH is not set"
95
+
96
+ python_binaries = set(
97
+ ["python"]
98
+ + os.environ.get("MF_BDI_PYTHONPATH", "").split()
99
+ + os.environ.get("MFPY", "").split()
100
+ + [sh_python]
101
+ )
102
+ for python_binary in python_binaries:
103
+ if not python_binary or not which(python_binary):
104
+ continue
105
+ maybe_install_coverage(python_binary)
106
+ setup_sitecustomize(python_binary)
107
+
108
+
109
+ if __name__ == "__main__":
110
+ sh_python = sys.argv[1] if len(sys.argv) > 1 else ""
111
+ setup_coverage(sh_python)
@@ -0,0 +1,83 @@
1
+ import coverage
2
+ import glob
3
+ import gzip
4
+ import hashlib
5
+ import os
6
+ import shutil
7
+ import subprocess
8
+ import uuid
9
+
10
+ from .setup_coverage import get_local_coverage_dir, COVERAGE_RCFILE
11
+
12
+
13
+ def hash_file(file_path: str) -> str:
14
+ hasher = hashlib.sha256()
15
+ with open(file_path, "rb") as f:
16
+ for chunk in iter(lambda: f.read(4096), b""):
17
+ hasher.update(chunk)
18
+ return hasher.hexdigest()
19
+
20
+
21
+ def gzip_file(source_path: str, destination_path: str) -> None:
22
+ with open(source_path, "rb") as f_in:
23
+ with gzip.open(destination_path, "wb") as f_out:
24
+ shutil.copyfileobj(f_in, f_out)
25
+
26
+
27
+ def has_arcs(file_path: str) -> bool:
28
+ try:
29
+ cov = coverage.Coverage(data_file=file_path)
30
+ cov.load()
31
+ return cov.get_data().has_arcs
32
+ except:
33
+ # sometimes the coverage file is corrupted and cannot be loaded, eg:
34
+ # - doesn't seem to be a coverage data file
35
+ # - database disk image is malformed
36
+ # so we just ignore it
37
+ return False
38
+
39
+
40
+ def combine_coverage_files(local_dir):
41
+ combined_coverage_file = f"{local_dir}/.coverage"
42
+ if os.path.exists(combined_coverage_file):
43
+ shutil.move(combined_coverage_file, f"{combined_coverage_file}.{uuid.uuid4()}")
44
+ files = [f for f in glob.glob(f"{local_dir}/.coverage.*") if has_arcs(f)]
45
+ if not files:
46
+ return
47
+ cov = coverage.Coverage(
48
+ data_file=combined_coverage_file, config_file=COVERAGE_RCFILE
49
+ )
50
+ try:
51
+ cov.combine(data_paths=files, keep=True)
52
+ except coverage.exceptions.NoDataError:
53
+ return
54
+ cov.save()
55
+ assert os.path.exists(combined_coverage_file)
56
+
57
+
58
+ def upload_coverage_files(coverage_s3_path: str) -> None:
59
+ coverage_s3_path = coverage_s3_path.rstrip("/")
60
+ local_coverage_dir = get_local_coverage_dir()
61
+ combine_coverage_files(local_coverage_dir)
62
+
63
+ combined_coverage_file = f"{local_coverage_dir}/.coverage"
64
+ file_hash = hash_file(combined_coverage_file)
65
+
66
+ gzipped_file = f"{local_coverage_dir}/.coverage.{file_hash}.gz"
67
+ gzip_file(combined_coverage_file, gzipped_file)
68
+
69
+ subprocess.run(
70
+ f"aws s3 cp {gzipped_file} {coverage_s3_path}/",
71
+ shell=True,
72
+ check=True,
73
+ cwd=local_coverage_dir,
74
+ )
75
+
76
+
77
+ if __name__ == "__main__":
78
+ import sys
79
+
80
+ if len(sys.argv) != 2:
81
+ print("Usage: python script.py <COVERAGE_S3_PATH>")
82
+ else:
83
+ upload_coverage_files(sys.argv[1])
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: metaflow-netflixext
3
- Version: 1.3.4.dev1
3
+ Version: 1.3.4.dev3
4
4
  Summary: Metaflow extensions from Netflix
5
5
  Author: Netflix Metaflow Developers
6
6
  Author-email: metaflow-dev@netflix.com
@@ -48,6 +48,11 @@ metaflow_extensions/netflix_ext/plugins/conda/resolvers/pylock_toml_resolver.py
48
48
  metaflow_extensions/netflix_ext/plugins/conda/resources/logo-32x32.png
49
49
  metaflow_extensions/netflix_ext/plugins/conda/resources/logo-64x64.png
50
50
  metaflow_extensions/netflix_ext/plugins/conda/resources/logo-svg.svg
51
+ metaflow_extensions/netflix_ext/plugins/coverage/__init__.py
52
+ metaflow_extensions/netflix_ext/plugins/coverage/coveragerc.py
53
+ metaflow_extensions/netflix_ext/plugins/coverage/generate_coveragerc.py
54
+ metaflow_extensions/netflix_ext/plugins/coverage/setup_coverage.py
55
+ metaflow_extensions/netflix_ext/plugins/coverage/upload_coverage_files.py
51
56
  metaflow_extensions/netflix_ext/toplevel/mfextinit_netflixext.py
52
57
  metaflow_extensions/netflix_ext/toplevel/netflixext_toplevel.py
53
58
  metaflow_extensions/netflix_ext/toplevel/netflixext_version.py