metaflow-netflixext 1.3.4.dev1__tar.gz → 1.3.4.dev2__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.
- {metaflow_netflixext-1.3.4.dev1/metaflow_netflixext.egg-info → metaflow_netflixext-1.3.4.dev2}/PKG-INFO +1 -1
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/plugins/conda/conda.py +1 -1
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/plugins/conda/conda_step_decorator.py +1 -1
- metaflow_netflixext-1.3.4.dev2/metaflow_extensions/netflix_ext/plugins/coverage/__init__.py +0 -0
- metaflow_netflixext-1.3.4.dev2/metaflow_extensions/netflix_ext/plugins/coverage/coveragerc.py +108 -0
- metaflow_netflixext-1.3.4.dev2/metaflow_extensions/netflix_ext/plugins/coverage/generate_coveragerc.py +96 -0
- metaflow_netflixext-1.3.4.dev2/metaflow_extensions/netflix_ext/plugins/coverage/setup_coverage.py +103 -0
- metaflow_netflixext-1.3.4.dev2/metaflow_extensions/netflix_ext/plugins/coverage/upload_coverage_files.py +83 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2/metaflow_netflixext.egg-info}/PKG-INFO +1 -1
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_netflixext.egg-info/SOURCES.txt +5 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/LICENSE +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/README.md +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/__init__.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/cmd/debug/__init__.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/cmd/debug/constants.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/cmd/debug/current_stub_generator.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/cmd/debug/debug_cmd.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/cmd/debug/debug_script_generator.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/cmd/debug/debug_stub_generator.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/cmd/debug/debug_utils.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/cmd/debug/jupyter_instructions_markdown.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/cmd/debug/jupyter_title_markdown.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/cmd/environment/__init__.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/cmd/environment/environment_cmd.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/cmd/environment/utils.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/cmd/mfextinit_netflixext.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/config/mfextinit_netflixext.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/exceptions/__init__.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/exceptions/decorators.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/exceptions/http_helpers.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/generate_vendor.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/http_helpers.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/plugins/conda/__init__.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/plugins/conda/conda_common_decorator.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/plugins/conda/conda_environment.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/plugins/conda/conda_flow_decorator.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/plugins/conda/conda_flow_mutator.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/plugins/conda/conda_lock_micromamba_server.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/plugins/conda/env_descr.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/plugins/conda/envsresolver.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/plugins/conda/parsers.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/plugins/conda/pypi_package_builder.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/plugins/conda/remote_bootstrap.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/plugins/conda/resolvers/__init__.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/plugins/conda/resolvers/builder_envs_resolver.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/plugins/conda/resolvers/conda_lock_resolver.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/plugins/conda/resolvers/conda_resolver.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/plugins/conda/resolvers/micromamba_server_resolver.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/plugins/conda/resolvers/pip_resolver.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/plugins/conda/resolvers/pylock_toml_resolver.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/plugins/conda/resources/logo-32x32.png +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/plugins/conda/resources/logo-64x64.png +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/plugins/conda/resources/logo-svg.svg +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/plugins/conda/terminal_menu.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/plugins/conda/utils.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/plugins/environment_cli.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/plugins/mfextinit_netflixext.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/toplevel/mfextinit_netflixext.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/toplevel/netflixext_toplevel.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_extensions/netflix_ext/toplevel/netflixext_version.py +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_netflixext.egg-info/dependency_links.txt +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_netflixext.egg-info/requires.txt +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/metaflow_netflixext.egg-info/top_level.txt +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/setup.cfg +0 -0
- {metaflow_netflixext-1.3.4.dev1 → metaflow_netflixext-1.3.4.dev2}/setup.py +0 -0
|
@@ -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.
|
|
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.
|
|
664
|
+
from metaflow_extensions.netflix_ext.plugins.coverage.setup_coverage import (
|
|
665
665
|
setup_coverage,
|
|
666
666
|
)
|
|
667
667
|
|
|
File without changes
|
|
@@ -0,0 +1,108 @@
|
|
|
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
|
+
/apps/bdi-venv*/*
|
|
14
|
+
/**/site-packages/*
|
|
15
|
+
/**/vendor/*
|
|
16
|
+
/**/vendored/*
|
|
17
|
+
/**/_vendor/*
|
|
18
|
+
/**/build/*
|
|
19
|
+
/**/pip-install-*/*
|
|
20
|
+
/**/pip-req-build-*/*
|
|
21
|
+
/**/_remote_module_non_scriptable.py
|
|
22
|
+
/**/__autograph_generated_*.py
|
|
23
|
+
/**/metaflow-function-*/*
|
|
24
|
+
metaflow/plugins/airflow/*
|
|
25
|
+
metaflow/plugins/argo/*
|
|
26
|
+
metaflow/plugins/azure/*
|
|
27
|
+
metaflow/plugins/gcp/*
|
|
28
|
+
metaflow/plugins/kubernetes/*
|
|
29
|
+
coveragerc.py
|
|
30
|
+
setup.py
|
|
31
|
+
|
|
32
|
+
include_namespace_packages = True
|
|
33
|
+
show_missing = False
|
|
34
|
+
ignore_errors = False
|
|
35
|
+
|
|
36
|
+
[paths]
|
|
37
|
+
; Specific paths must come before general paths
|
|
38
|
+
; /root/metaflow/.mf_code/metaflow_extensions is more specific than /root/metaflow
|
|
39
|
+
metaflow_extensions =
|
|
40
|
+
.mf_code/metaflow_extensions
|
|
41
|
+
metaflow_extensions
|
|
42
|
+
nflx-metaflow-functions/metaflow_extensions
|
|
43
|
+
nflx-metaflow-serving/metaflow_extensions
|
|
44
|
+
nflx-metaflow/metaflow_extensions
|
|
45
|
+
nflx-fastdata/metaflow_extensions
|
|
46
|
+
mli-metaflow-custom-repo/nflx-metaflow-functions/metaflow_extensions
|
|
47
|
+
mli-metaflow-custom-repo/nflx-metaflow-serving/metaflow_extensions
|
|
48
|
+
mli-metaflow-custom-repo/nflx-metaflow/metaflow_extensions
|
|
49
|
+
mli-metaflow-custom-repo/nflx-fastdata/metaflow_extensions
|
|
50
|
+
metaflow-repo/test/extensions/packages/card_via_extinit/metaflow_extensions
|
|
51
|
+
metaflow-repo/test/extensions/packages/card_via_init/metaflow_extensions
|
|
52
|
+
metaflow-repo/test/extensions/packages/card_via_ns_subpackage/metaflow_extensions
|
|
53
|
+
/*/.mf_code/metaflow_extensions
|
|
54
|
+
/*/*/.mf_code/metaflow_extensions
|
|
55
|
+
/**/.mf_code/metaflow_extensions
|
|
56
|
+
/root/metaflow/.mf_code/metaflow_extensions
|
|
57
|
+
/data/tmp/*/env/install/lib/*/site-packages/metaflow_extensions
|
|
58
|
+
/apps/*/lib/*/site-packages/metaflow_extensions
|
|
59
|
+
|
|
60
|
+
; Test flow files are flattened into test_flows_flat to match remote execution structure
|
|
61
|
+
; First line is canonical location (where files exist), rest are aliases (execution paths)
|
|
62
|
+
test_flows_flat =
|
|
63
|
+
.mf_code/test_flows_flat
|
|
64
|
+
/root/metaflow
|
|
65
|
+
/*/metaflow
|
|
66
|
+
|
|
67
|
+
.mf_code =
|
|
68
|
+
.mf_code
|
|
69
|
+
/*/.mf_code
|
|
70
|
+
/*/*/.mf_code
|
|
71
|
+
/**/.mf_code
|
|
72
|
+
|
|
73
|
+
metaflow =
|
|
74
|
+
.mf_code/metaflow
|
|
75
|
+
metaflow
|
|
76
|
+
metaflow-repo/metaflow
|
|
77
|
+
/*/.mf_code/metaflow
|
|
78
|
+
/*/*/.mf_code/metaflow
|
|
79
|
+
/**/.mf_code/metaflow
|
|
80
|
+
/*/metaflow
|
|
81
|
+
/*/*/metaflow
|
|
82
|
+
/**/metaflow
|
|
83
|
+
/data/tmp/*/env/install/lib/*/site-packages/metaflow
|
|
84
|
+
/apps/*/lib/*/site-packages/metaflow
|
|
85
|
+
|
|
86
|
+
test =
|
|
87
|
+
.mf_code/test
|
|
88
|
+
test
|
|
89
|
+
mli-metaflow-custom-repo/test
|
|
90
|
+
metaflow-repo/test
|
|
91
|
+
/*/metaflow/test
|
|
92
|
+
/*/*/metaflow/test
|
|
93
|
+
/**/metaflow/test
|
|
94
|
+
|
|
95
|
+
; Spin tests are at test/unit/spin in OSS metaflow but executed at test/plugins/spin/spin
|
|
96
|
+
test_spin =
|
|
97
|
+
.mf_code/test/unit/spin
|
|
98
|
+
/root/metaflow/test/plugins/spin/spin
|
|
99
|
+
/*/metaflow/test/plugins/spin/spin
|
|
100
|
+
|
|
101
|
+
cwd =
|
|
102
|
+
.
|
|
103
|
+
mli-metaflow-custom-repo
|
|
104
|
+
/data/tmp/*/user/corp/mli-metaflow-custom
|
|
105
|
+
/*/metaflow
|
|
106
|
+
/*/*/metaflow
|
|
107
|
+
/**/.mf_code
|
|
108
|
+
/**/metaflow
|
|
@@ -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")
|
metaflow_netflixext-1.3.4.dev2/metaflow_extensions/netflix_ext/plugins/coverage/setup_coverage.py
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
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
|
+
COVERAGE_RCFILE = os.path.join(MODULE_DIR, "coveragerc.py")
|
|
13
|
+
SITECUSTOMIZE_FILE = os.path.join(MODULE_DIR, "sitecustomize.py")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_local_coverage_dir():
|
|
17
|
+
METAFLOW_COVERAGE_S3_PATH = os.environ.get("METAFLOW_COVERAGE_S3_PATH")
|
|
18
|
+
assert METAFLOW_COVERAGE_S3_PATH, "METAFLOW_COVERAGE_S3_PATH is not set"
|
|
19
|
+
group_tag = os.path.basename(METAFLOW_COVERAGE_S3_PATH)
|
|
20
|
+
coverage_dir = os.path.join("/tmp", group_tag)
|
|
21
|
+
os.makedirs(coverage_dir, exist_ok=True)
|
|
22
|
+
return coverage_dir
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def get_sitepackages_dir(python_binary: str):
|
|
26
|
+
return (
|
|
27
|
+
subprocess.run(
|
|
28
|
+
[python_binary, "-c", "import site; print(site.getsitepackages()[0])"],
|
|
29
|
+
check=True,
|
|
30
|
+
stdout=subprocess.PIPE,
|
|
31
|
+
)
|
|
32
|
+
.stdout.decode()
|
|
33
|
+
.strip()
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def setup_sitecustomize(python_binary: str):
|
|
38
|
+
site_packages_dir = get_sitepackages_dir(python_binary)
|
|
39
|
+
sitecustomize_file = os.path.join(site_packages_dir, "sitecustomize.py")
|
|
40
|
+
local_coverage_dir = get_local_coverage_dir()
|
|
41
|
+
|
|
42
|
+
# We also copy the .coveragerc file to the site-packages directory.
|
|
43
|
+
# This keeps the .coveragerc file in the same directory as the sitecustomize.py file
|
|
44
|
+
# so that when the env is saved and restored we will have both.
|
|
45
|
+
coverage_rcfile = os.path.join(site_packages_dir, ".coveragerc")
|
|
46
|
+
if not os.path.exists(coverage_rcfile):
|
|
47
|
+
shutil.copy(COVERAGE_RCFILE, coverage_rcfile)
|
|
48
|
+
|
|
49
|
+
# We harden out the environment variables as the fallback so that it will work with
|
|
50
|
+
# subprocesses that are launched without inheriting the environment.
|
|
51
|
+
lines = [
|
|
52
|
+
"import os",
|
|
53
|
+
"import site",
|
|
54
|
+
"site_packages_dir = site.getsitepackages()[0]",
|
|
55
|
+
f"os.environ['COVERAGE_FILE'] = os.environ.get('COVERAGE_FILE') or os.path.join('{local_coverage_dir}', f'.coverage')",
|
|
56
|
+
f"os.environ['COVERAGE_PROCESS_START'] = os.environ.get('COVERAGE_PROCESS_START') or os.path.join(site_packages_dir, '.coveragerc')",
|
|
57
|
+
f"os.environ['COVERAGE_RCFILE'] = os.environ.get('COVERAGE_RCFILE') or os.path.join(site_packages_dir, '.coveragerc')",
|
|
58
|
+
f"os.environ['METAFLOW_COVERAGE_S3_PATH'] = os.environ.get('METAFLOW_COVERAGE_S3_PATH') or '{os.environ['METAFLOW_COVERAGE_S3_PATH']}'",
|
|
59
|
+
"import coverage",
|
|
60
|
+
"coverage.process_startup()",
|
|
61
|
+
"coverage_context = os.environ.get('METAFLOW_COVERAGE_CONTEXT')",
|
|
62
|
+
"if coverage_context and coverage.Coverage.current():",
|
|
63
|
+
" coverage.Coverage.current().switch_context(coverage_context)",
|
|
64
|
+
]
|
|
65
|
+
if not os.path.exists(sitecustomize_file):
|
|
66
|
+
with open(sitecustomize_file, "w") as target_file:
|
|
67
|
+
target_file.write("\n".join(lines) + "\n")
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def maybe_install_coverage(python_binary: str):
|
|
71
|
+
try:
|
|
72
|
+
subprocess.run(
|
|
73
|
+
[python_binary, "-c", "import coverage"],
|
|
74
|
+
check=True,
|
|
75
|
+
stdout=subprocess.PIPE,
|
|
76
|
+
stderr=subprocess.PIPE,
|
|
77
|
+
)
|
|
78
|
+
except subprocess.CalledProcessError:
|
|
79
|
+
subprocess.run([python_binary, "-m", "pip", "install", "coverage"], check=True)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def setup_coverage(sh_python: Union[str, "sh.Command"]):
|
|
83
|
+
sh_python = str(sh_python)
|
|
84
|
+
assert os.environ.get(
|
|
85
|
+
"METAFLOW_COVERAGE_S3_PATH"
|
|
86
|
+
), "METAFLOW_COVERAGE_S3_PATH is not set"
|
|
87
|
+
|
|
88
|
+
python_binaries = set(
|
|
89
|
+
["python"]
|
|
90
|
+
+ os.environ.get("MF_BDI_PYTHONPATH", "").split()
|
|
91
|
+
+ os.environ.get("MFPY", "").split()
|
|
92
|
+
+ [sh_python]
|
|
93
|
+
)
|
|
94
|
+
for python_binary in python_binaries:
|
|
95
|
+
if not python_binary or not which(python_binary):
|
|
96
|
+
continue
|
|
97
|
+
maybe_install_coverage(python_binary)
|
|
98
|
+
setup_sitecustomize(python_binary)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
if __name__ == "__main__":
|
|
102
|
+
sh_python = sys.argv[1] if len(sys.argv) > 1 else ""
|
|
103
|
+
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])
|
|
@@ -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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|